| 1 |
<?php |
| 2 |
/* |
| 3 |
Plugin Name: Upload Larger Plugins |
| 4 |
Version: 1.7 |
| 5 |
Plugin URI: https://wordpress.org/plugins/upload-larger-plugins |
| 6 |
Description: Allow plugins larger than the PHP-defined limit to be uploaded. |
| 7 |
Author: David Anderson |
| 8 |
Donate: https://david.dw-perspective.org.uk/donate |
| 9 |
Author URI: https://david.dw-perspective.org.uk |
| 10 |
Text Domain: upload-larger-plugins |
| 11 |
License: MIT |
| 12 |
*/ |
| 13 |
|
| 14 |
if (!defined('ABSPATH')) die('No direct access'); |
| 15 |
|
| 16 |
// Globals |
| 17 |
define('UPLOADLARGERPLUGINS_VERSION', '1.7'); |
| 18 |
define('UPLOADLARGERPLUGINS_SLUG', "upload-larger-plugins"); |
| 19 |
define('UPLOADLARGERPLUGINS_DIR', dirname(realpath(__FILE__))); |
| 20 |
define('UPLOADLARGERPLUGINS_URL', plugins_url('', __FILE__)); |
| 21 |
|
| 22 |
$simba_upload_larger_plugins = new Simba_Upload_Larger_Plugins(); |
| 23 |
|
| 24 |
class Simba_Upload_Larger_Plugins { |
| 25 |
|
| 26 |
private $upload_dir; |
| 27 |
private $upload_basedir; |
| 28 |
|
| 29 |
/** |
| 30 |
* Plugin constructor |
| 31 |
*/ |
| 32 |
public function __construct() { |
| 33 |
//add_filter('plugin_action_links', array($this, 'action_links'), 10, 2 ); |
| 34 |
add_action('install_plugins_upload', array($this, 'install_plugins_upload'), 9, 1); |
| 35 |
add_action('install_plugins_pre_upload', array($this, 'install_plugins_pre_upload')); |
| 36 |
add_action('admin_enqueue_scripts', array($this, 'admin_enqueue_scripts')); |
| 37 |
add_action('plugins_loaded', array($this, 'load_translations')); |
| 38 |
add_action('admin_head', array($this, 'admin_head')); |
| 39 |
add_action('wp_ajax_ulp_plupload_action', array($this, 'ulp_plupload_action')); |
| 40 |
add_action('admin_init', array($this, 'admin_init')); |
| 41 |
// This filter only exists on WP 3.7+. We used to use it then... but then WP 4.6.1 broke our method, so we've reverted to the pre-WP-3.7 method |
| 42 |
// add_filter('upgrader_pre_download', array($this, 'upgrader_pre_download'), 10, 3); |
| 43 |
// This action allows us to tweak the link URL on WP 5.5+ |
| 44 |
add_filter('install_plugin_overwrite_actions', array($this, 'install_plugin_overwrite_actions')); |
| 45 |
} |
| 46 |
|
| 47 |
/** |
| 48 |
* Called by the WP filter install_plugin_overwrite_actions (WP 5.5+) |
| 49 |
* |
| 50 |
* @param Array $install_actions Array of plugin action links. |
| 51 |
* |
| 52 |
* @return Array - modified array |
| 53 |
*/ |
| 54 |
public function install_plugin_overwrite_actions($install_actions) { |
| 55 |
|
| 56 |
if (!empty($install_actions['overwrite_plugin']) && false !== strpos($install_actions['overwrite_plugin'], 'action=upload-plugin&')) { |
| 57 |
|
| 58 |
if (!empty($_GET['plugincksha1']) && !empty($_GET['overridebd']) && !empty($_GET['package']) && current_user_can('install_plugins')) { |
| 59 |
|
| 60 |
// WP 5.5 already uses "package=0"; so we have to replace that with the proper name that will work with our upload directory |
| 61 |
$install_actions['overwrite_plugin'] = str_replace('action=upload-plugin&', 'action=upload-plugin&plugincksha1='.urlencode($_GET['plugincksha1']).'&overridebd='.urlencode($_GET['overridebd']).'&package='.urlencode($_GET['package']).'&', $install_actions['overwrite_plugin']); |
| 62 |
|
| 63 |
$install_actions['overwrite_plugin'] = str_replace('package=0&', '', $install_actions['overwrite_plugin']); |
| 64 |
} |
| 65 |
|
| 66 |
} |
| 67 |
|
| 68 |
// error_log(print_r($install_actions, true)); |
| 69 |
|
| 70 |
return $install_actions; |
| 71 |
} |
| 72 |
|
| 73 |
/** |
| 74 |
* Called by the WP action admin_init. Used to continue when a completed upload from our widget has occurred. |
| 75 |
*/ |
| 76 |
public function admin_init() { |
| 77 |
|
| 78 |
// Check if parameters present indicate our action |
| 79 |
if (empty($_GET['plugincksha1']) || empty($_GET['overridebd']) || !isset($_GET['package']) || !current_user_can('install_plugins')) return; |
| 80 |
|
| 81 |
/* |
| 82 |
Old note: |
| 83 |
|
| 84 |
The rest of the code's purpose is to work-around the lack of the upgrader_pre_download filter before WP 3.7 |
| 85 |
The below would work on >= 3.7 too; but there, we use a more elegant/direct method. |
| 86 |
|
| 87 |
New situation: |
| 88 |
WP 4.6.1 - https://build.trac.wordpress.org/changeset/38466 - introduced a change which prevents upgrader_pre_download from working. So, this way is back. |
| 89 |
*/ |
| 90 |
|
| 91 |
// require(ABSPATH.WPINC.'/version.php'); |
| 92 |
// if (version_compare($wp_version, '3.7', '>=')) return; |
| 93 |
|
| 94 |
$package = (isset($_GET['fpackage']) && is_numeric($_GET['package'])) ? stripslashes($_GET['fpackage']) : stripslashes($_GET['package']); |
| 95 |
|
| 96 |
$upgrader = new stdClass; |
| 97 |
$upgrader->strings = array('download_failed' => __('Error when trying to find uploaded file', 'upload-larger-plugins')); |
| 98 |
$try_file = $this->upgrader_pre_download(false, $package, $upgrader); |
| 99 |
|
| 100 |
// The File_Upload_Upgrader object eventually gets constructed with this (where $urlholder = 'package', and $uploads = wp_upload_dir()) |
| 101 |
//File_Upload_Upgrader::filename = $_GET[$urlholder]; |
| 102 |
//File_Upload_Upgrader::package = $uploads['basedir'] . '/' . $this->filename; |
| 103 |
|
| 104 |
if (!(($uploads = wp_upload_dir()) && false === $uploads['error'])) return; |
| 105 |
|
| 106 |
if (is_string($try_file) && file_exists($try_file)) { |
| 107 |
$upload_dir = untrailingslashit(get_temp_dir()); |
| 108 |
// if (!is_writable($upload_dir)) return; |
| 109 |
$this->upload_basedir = $upload_dir; |
| 110 |
add_filter('upload_dir', array($this, 'upload_dir')); |
| 111 |
add_action('upgrader_process_complete', array($this, 'upgrader_process_complete')); |
| 112 |
} |
| 113 |
} |
| 114 |
|
| 115 |
// Only hooked on WP < 3.7 |
| 116 |
public function upgrader_process_complete() { |
| 117 |
remove_filter('upload_dir', array($this, 'upload_dir')); |
| 118 |
} |
| 119 |
|
| 120 |
public function upgrader_pre_download($result, $package, $upgrader) { |
| 121 |
|
| 122 |
if (empty($_GET['plugincksha1']) || empty($_GET['overridebd'])) return $result; |
| 123 |
$upload_dir = untrailingslashit(get_temp_dir()); |
| 124 |
|
| 125 |
// Sanity checks |
| 126 |
if ($upload_dir != $_GET['overridebd']) return new WP_Error('download_failed', $upgrader->strings['download_failed']); |
| 127 |
$try_file = $upload_dir.'/'.basename($package); |
| 128 |
|
| 129 |
if (!file_exists($try_file) || sha1_file($try_file) != $_GET['plugincksha1']) return new WP_Error('download_failed', $upgrader->strings['download_failed']); |
| 130 |
|
| 131 |
return $try_file; |
| 132 |
} |
| 133 |
|
| 134 |
/** |
| 135 |
* @return Boolean |
| 136 |
*/ |
| 137 |
private function is_our_page_and_authorised() { |
| 138 |
if (!current_user_can('install_plugins')) return false; |
| 139 |
|
| 140 |
require(ABSPATH.WPINC.'/version.php'); |
| 141 |
|
| 142 |
global $pagenow; |
| 143 |
// On WP 4.6, there is no longer an upload 'tab' - it's a slide-down instead |
| 144 |
|
| 145 |
return ($pagenow != 'plugin-install.php' || (version_compare($wp_version, '4.5.9999', '<') && (!isset($_REQUEST['tab']) || 'upload' != $_REQUEST['tab']))) ? false : true; |
| 146 |
|
| 147 |
} |
| 148 |
|
| 149 |
/** |
| 150 |
* Runs upon the WP action admin_enqueue_scripts |
| 151 |
*/ |
| 152 |
public function admin_enqueue_scripts() { |
| 153 |
|
| 154 |
if (!$this->is_our_page_and_authorised()) return; |
| 155 |
|
| 156 |
wp_enqueue_script('ulp-admin-ui', UPLOADLARGERPLUGINS_URL.'/admin.js', array('jquery', 'plupload-all'), '1'); |
| 157 |
|
| 158 |
wp_localize_script('ulp-admin-ui', 'ulplion', array( |
| 159 |
'notarchive' => __('This file does not appear to be a zip file.', 'upload-larger-plugins'), |
| 160 |
'notarchive2' => '<p>'.__('This file does not appear to be a zip file.', 'upload-larger-plugins').'</p>', |
| 161 |
'uploaderror' => __('Upload error:', 'upload-larger-plugins'), |
| 162 |
'makesure' => __('(make sure that you were trying to upload a zip file', 'upload-larger-plugins'), |
| 163 |
'uploaderr' => __('Upload error', 'upload-larger-plugins'), |
| 164 |
'jsonnotunderstood' => __('Error: the server sent us a response (JSON) which we did not understand.', 'upload-larger-plugins'), |
| 165 |
'error' => __('Error:', 'upload-larger-plugins') |
| 166 |
)); |
| 167 |
|
| 168 |
} |
| 169 |
|
| 170 |
/** |
| 171 |
* Runs upon the WP action plugins_loaded |
| 172 |
*/ |
| 173 |
public function load_translations() { |
| 174 |
// Tell WordPress where to find the translations |
| 175 |
load_plugin_textdomain('upload-larger-plugins', false, basename(dirname(__FILE__)).'/languages/'); |
| 176 |
} |
| 177 |
|
| 178 |
/** |
| 179 |
* Used by the WP filter upload_dir |
| 180 |
* |
| 181 |
* @param Array $uploads |
| 182 |
* |
| 183 |
* @return Array |
| 184 |
*/ |
| 185 |
public function upload_dir($uploads) { |
| 186 |
if (!empty($this->upload_dir)) $uploads['path'] = $this->upload_dir; |
| 187 |
if (!empty($this->upload_basedir)) $uploads['basedir'] = $this->upload_basedir; |
| 188 |
return $uploads; |
| 189 |
} |
| 190 |
|
| 191 |
/** |
| 192 |
* Runs upon the AJAX event ulp_plupload_action |
| 193 |
*/ |
| 194 |
public function ulp_plupload_action() { |
| 195 |
|
| 196 |
@set_time_limit(900); |
| 197 |
|
| 198 |
if (!current_user_can('install_plugins')) return; |
| 199 |
check_ajax_referer('uploadlargerplugins-uploader'); |
| 200 |
|
| 201 |
$upload_dir = untrailingslashit(get_temp_dir()); |
| 202 |
if (!is_writable($upload_dir)) exit; |
| 203 |
$this->upload_dir = $upload_dir; |
| 204 |
|
| 205 |
add_filter('upload_dir', array($this, 'upload_dir')); |
| 206 |
// handle file upload |
| 207 |
|
| 208 |
$farray = array('test_form' => true, 'action' => 'ulp_plupload_action'); |
| 209 |
|
| 210 |
$farray['test_type'] = false; |
| 211 |
$farray['ext'] = 'zip'; |
| 212 |
$farray['type'] = 'application/zip'; |
| 213 |
|
| 214 |
// if (isset($_POST['chunks'])) { |
| 215 |
// |
| 216 |
// } else { |
| 217 |
// # Over-write - that's OK. |
| 218 |
// $farray['unique_filename_callback'] = array($this, 'unique_filename_callback'); |
| 219 |
// } |
| 220 |
|
| 221 |
$status = wp_handle_upload( |
| 222 |
$_FILES['async-upload'], |
| 223 |
$farray |
| 224 |
); |
| 225 |
remove_filter('upload_dir', array($this, 'upload_dir')); |
| 226 |
|
| 227 |
if (isset($status['error'])) { |
| 228 |
echo json_encode(array('e' => $status['error'])); |
| 229 |
exit; |
| 230 |
} |
| 231 |
|
| 232 |
// Should be a no-op |
| 233 |
$name = basename($_POST['name']); |
| 234 |
|
| 235 |
// If this was the chunk, then we should instead be concatenating onto the final file |
| 236 |
if (isset($_POST['chunks']) && isset($_POST['chunk']) && preg_match('/^[0-9]+$/',$_POST['chunk'])) { |
| 237 |
# A random element is added, because otherwise it is theoretically possible for another user to upload into a shared temporary directory in between the upload and install, and over-write |
| 238 |
$final_file = $name; |
| 239 |
rename($status['file'], $upload_dir.'/'.$final_file.'.'.$_POST['chunk'].'.zip.tmp'); |
| 240 |
$status['file'] = $upload_dir.'/'.$final_file.'.'.$_POST['chunk'].'.zip.tmp'; |
| 241 |
|
| 242 |
// Final chunk? If so, then stich it all back together |
| 243 |
if ($_POST['chunk'] == $_POST['chunks']-1) { |
| 244 |
if ($wh = fopen($upload_dir.'/'.$final_file, 'wb')) { |
| 245 |
for ($i=0 ; $i<$_POST['chunks']; $i++) { |
| 246 |
$rf = $upload_dir.'/'.$final_file.'.'.$i.'.zip.tmp'; |
| 247 |
if ($rh = fopen($rf, 'rb')) { |
| 248 |
while ($line = fread($rh, 32768)) fwrite($wh, $line); |
| 249 |
fclose($rh); |
| 250 |
@unlink($rf); |
| 251 |
} |
| 252 |
} |
| 253 |
fclose($wh); |
| 254 |
$status['file'] = $upload_dir.'/'.$final_file; |
| 255 |
} |
| 256 |
} |
| 257 |
|
| 258 |
} |
| 259 |
|
| 260 |
$response = array(); |
| 261 |
if (!isset($_POST['chunks']) || (isset($_POST['chunk']) && $_POST['chunk'] == $_POST['chunks']-1)) { |
| 262 |
$file = basename($status['file']); |
| 263 |
if (!preg_match('/\.zip$/i', $file, $matches)) { |
| 264 |
@unlink($status['file']); |
| 265 |
echo json_encode(array('e' => sprintf(__('Error: %s', 'upload-larger-plugins'), __('This file does not appear to be a zip file.', 'upload-larger-plugins')))); |
| 266 |
exit; |
| 267 |
} |
| 268 |
} |
| 269 |
|
| 270 |
// send the redirect URL |
| 271 |
$response['m'] = admin_url('update.php?action=upload-plugin&overridebd='.urlencode(dirname($status['file'])).'&plugincksha1='.sha1_file($status['file']).'&_wpnonce='.wp_create_nonce( 'plugin-upload' ).'&package='.urlencode(basename($status['file']))); |
| 272 |
echo json_encode($response); |
| 273 |
exit; |
| 274 |
} |
| 275 |
|
| 276 |
/** |
| 277 |
* Runs upon the WP action admin_head |
| 278 |
*/ |
| 279 |
public function admin_head() { |
| 280 |
|
| 281 |
if (!$this->is_our_page_and_authorised()) return; |
| 282 |
|
| 283 |
$chunk_size = min(wp_max_upload_size()-1024, 1024*1024*2-1024); |
| 284 |
|
| 285 |
# The multiple_queues argument is ignored in plupload 2.x (WP3.9+) - https://make.wordpress.org/core/2014/04/11/plupload-2-x-in-wordpress-3-9/ |
| 286 |
# max_file_size is also in filters as of plupload 2.x, but in its default position is still supported for backwards-compatibility. Likewise, our use of filters.extensions below is supported by a backwards-compatibility option (the current way is filters.mime-types.extensions |
| 287 |
|
| 288 |
$plupload_init = array( |
| 289 |
'runtimes' => 'html5,flash,silverlight,html4', |
| 290 |
'browse_button' => 'plupload-browse-button', |
| 291 |
'container' => 'plupload-upload-ui', |
| 292 |
'drop_element' => 'drag-drop-area', |
| 293 |
'file_data_name' => 'async-upload', |
| 294 |
'multiple_queues' => false, |
| 295 |
'max_file_count' => 1, |
| 296 |
'max_file_size' => '100Gb', |
| 297 |
'chunk_size' => $chunk_size.'b', |
| 298 |
'url' => admin_url('admin-ajax.php'), |
| 299 |
'filters' => array(array('title' => __('Allowed Files'), 'extensions' => 'zip')), |
| 300 |
'multipart' => true, |
| 301 |
'multi_selection' => false, |
| 302 |
'urlstream_upload' => true, |
| 303 |
// additional post data to send to our ajax hook |
| 304 |
'multipart_params' => array( |
| 305 |
'_ajax_nonce' => wp_create_nonce('uploadlargerplugins-uploader'), |
| 306 |
'action' => 'ulp_plupload_action' |
| 307 |
) |
| 308 |
); |
| 309 |
// 'flash_swf_url' => includes_url('js/plupload/plupload.flash.swf'), |
| 310 |
// 'silverlight_xap_url' => includes_url('js/plupload/plupload.silverlight.xap'), |
| 311 |
|
| 312 |
# WP 3.9 updated to plupload 2.0 - https://core.trac.wordpress.org/ticket/25663 |
| 313 |
if (is_file(ABSPATH.'wp-includes/js/plupload/Moxie.swf')) { |
| 314 |
$plupload_init['flash_swf_url'] = includes_url('js/plupload/Moxie.swf'); |
| 315 |
} else { |
| 316 |
$plupload_init['flash_swf_url'] = includes_url('js/plupload/plupload.flash.swf'); |
| 317 |
} |
| 318 |
|
| 319 |
if (is_file(ABSPATH.'wp-includes/js/plupload/Moxie.xap')) { |
| 320 |
$plupload_init['silverlight_xap_url'] = includes_url('js/plupload/Moxie.xap'); |
| 321 |
} else { |
| 322 |
$plupload_init['silverlight_xap_url'] = includes_url('js/plupload/plupload.silverlight.swf'); |
| 323 |
} |
| 324 |
|
| 325 |
?><script type="text/javascript"> |
| 326 |
var ulp_plupload_config=<?php echo json_encode($plupload_init); ?>; |
| 327 |
</script> |
| 328 |
<style type="text/css"> |
| 329 |
.drag-drop #drag-drop-area { |
| 330 |
border: 4px dashed #ddd; |
| 331 |
height: 200px; |
| 332 |
} |
| 333 |
#filelist { |
| 334 |
width: 100%; |
| 335 |
} |
| 336 |
#filelist .file { |
| 337 |
padding: 5px; |
| 338 |
background: #ececec; |
| 339 |
border: solid 1px #ccc; |
| 340 |
margin: 4px 0; |
| 341 |
} |
| 342 |
#filelist .fileprogress { |
| 343 |
width: 0%; |
| 344 |
background: #f6a828; |
| 345 |
height: 5px; |
| 346 |
} |
| 347 |
</style> |
| 348 |
<?php |
| 349 |
|
| 350 |
} |
| 351 |
|
| 352 |
public function install_plugins_pre_upload() { |
| 353 |
// Unhook the default uploader (works on WP < 4.6 only) |
| 354 |
remove_action('install_plugins_upload', 'install_plugins_upload'); |
| 355 |
} |
| 356 |
|
| 357 |
/** |
| 358 |
* If hooked, runs upon the WP action install_plugins_upload |
| 359 |
* |
| 360 |
*/ |
| 361 |
public function install_plugins_upload() { |
| 362 |
|
| 363 |
echo '<div class="upload-plugin">'; |
| 364 |
|
| 365 |
require(ABSPATH.WPINC.'/version.php'); |
| 366 |
|
| 367 |
if (version_compare($wp_version, '4.5.9999', '<')) { ?> |
| 368 |
|
| 369 |
<!-- Upload form from Upload Larger Plugins --> |
| 370 |
<h4><?php _e('Install a plugin in .zip format'); ?></h4> |
| 371 |
|
| 372 |
<?php } ?> |
| 373 |
|
| 374 |
<p class="install-help" style="text-align:left; margin-bottom: 6px;"> |
| 375 |
|
| 376 |
<?php |
| 377 |
|
| 378 |
$upload_dir = untrailingslashit(get_temp_dir()); |
| 379 |
if (!$this->really_is_writable($upload_dir)) { |
| 380 |
echo '<strong>'.sprintf(__("Your hosting's temporary directory (%s) is not writable (as verified by attempting to write to it). You need to fix this (asking your hosting company for help if necessary) to be able to upload any plugins.", 'upload-larger-plugins'), $upload_dir).'</strong>'; |
| 381 |
} else { |
| 382 |
_e('If you have a plugin in a .zip format, you may install it by uploading it here.').'<br>'; |
| 383 |
} |
| 384 |
|
| 385 |
echo '</p>'; |
| 386 |
|
| 387 |
if (version_compare($wp_version, '3.3', '<')) { |
| 388 |
echo '<em>'.sprintf(__('This feature requires %s version %s or later', 'upload-larger-plugins'), 'WordPress', '3.3').'</em>'; |
| 389 |
} else { |
| 390 |
?> |
| 391 |
<div id="plupload-upload-ui" class="drag-drop" style="width: 70%;"> |
| 392 |
<div id="drag-drop-area"> |
| 393 |
<div class="drag-drop-inside"> |
| 394 |
<p class="drag-drop-info"><?php _e('Drop plugin zip here', 'upload-larger-plugins'); ?></p> |
| 395 |
<p><?php _ex('or', 'Uploader: Drop plugin zip here - or - Select File'); ?></p> |
| 396 |
<p class="drag-drop-buttons"><input id="plupload-browse-button" type="button" value="<?php echo esc_attr(__('Select File', 'upload-larger-plugins')); ?>" class="button" /></p> |
| 397 |
</div> |
| 398 |
</div> |
| 399 |
<div id="filelist"> |
| 400 |
</div> |
| 401 |
</div> |
| 402 |
<?php |
| 403 |
} |
| 404 |
?> |
| 405 |
|
| 406 |
</div> |
| 407 |
|
| 408 |
<?php |
| 409 |
/* |
| 410 |
<div style="display:none;"> |
| 411 |
<form method="post" enctype="multipart/form-data" class="wp-upload-form" action="<?php echo self_admin_url('update.php?action=upload-plugin'); ?>"> |
| 412 |
<?php wp_nonce_field( 'plugin-upload'); ?> |
| 413 |
<input type="file" id="pluginzip" name="pluginzip" /> |
| 414 |
<?php submit_button( __( 'Install Now' ), 'button', 'install-plugin-submit', false ); ?> |
| 415 |
</form> |
| 416 |
</div> |
| 417 |
*/ |
| 418 |
} |
| 419 |
|
| 420 |
/** |
| 421 |
* Find out whether we really can write to a particular folder |
| 422 |
* |
| 423 |
* @param String $dir - the folder path |
| 424 |
* |
| 425 |
* @return Boolean - the result |
| 426 |
*/ |
| 427 |
private function really_is_writable($dir) { |
| 428 |
// Suppress warnings, since if the user is dumping warnings to screen, then invalid JavaScript results and the screen breaks. |
| 429 |
if (!@is_writable($dir)) return false;// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged |
| 430 |
// Found a case - GoDaddy server, Windows, PHP 5.2.17 - where is_writable returned true, but writing failed |
| 431 |
$rand_file = "$dir/test-".md5(rand().time()).".txt"; |
| 432 |
while (file_exists($rand_file)) { |
| 433 |
$rand_file = "$dir/test-".md5(rand().time()).".txt"; |
| 434 |
} |
| 435 |
$ret = @file_put_contents($rand_file, 'testing...');// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged |
| 436 |
@unlink($rand_file);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged |
| 437 |
return ($ret > 0); |
| 438 |
} |
| 439 |
|
| 440 |
public function action_links($links, $file) { |
| 441 |
if ($file == UPLOADLARGERPLUGINS_SLUG."/".basename(__FILE__)) { |
| 442 |
array_unshift( $links, |
| 443 |
'<a href="options-general.php?page=upload_larger_plugins">'.__('Settings', 'upload-larger-plugins').'</a>' |
| 444 |
); |
| 445 |
} |
| 446 |
return $links; |
| 447 |
} |
| 448 |
|
| 449 |
} |
| 450 |
|