Ordermain/user_restrictions.php000064400000046312147600405370012003 0ustar00 fileorganizer_optpost($restr_key), 'restrict_operations' => fileorganizer_optpost('restrict_operations'), 'private_dir' => fileorganizer_cleanpath($path), 'restrict_dirs' => fileorganizer_cleanpath(fileorganizer_optpost('restrict_dirs')), 'restrict_files' => fileorganizer_cleanpath(fileorganizer_optpost('restrict_files')), 'disable_toolbar' => fileorganizer_optpost('disable_toolbar'), 'disable_context_menu' => fileorganizer_optpost('disable_context_menu'), ); $roles = !empty($options[$restr_option]) && is_array($options[$restr_option]) ? $options[$restr_option] : ''; // Array already exists? if(!empty($roles) ){ $options[$restr_option] = $roles; } // Update or add user role. if(!empty($_POST['rule_id']) && isset($options[$restr_option][ (int)$_POST['rule_id'] - 1])){ $options[$restr_option][(int)fileorganizer_optpost('rule_id') - 1] = $roles_; }else{ $options[$restr_option][] = $roles_; } if(update_option( 'fileorganizer_options', $options )){ fileorganizer_notify(__('Settings saved successfully.')); } } } function fileorganizer_user_restriction_render(){ // Load header fileorganizer_pro_user_restriction_header(); $settings = get_option('fileorganizer_options', array()); if(empty($settings) || !is_array($settings)){ $settings = array(); } $file_operations = ['mkdir', 'mkfile', 'rename', 'duplicate', 'paste', 'archive', 'extract', 'copy', 'cut', 'edit', 'rm', 'download', 'upload', 'search', 'empty']; ?>

$restriction){ $operations_ = !empty($restriction['restrict_operations']) ? $restriction['restrict_operations'] : array(); $rm_pos = array_search('rm', $operations_); if(!empty($rm_pos)){ $operations_[$rm_pos] = 'Remove'; } $operations = !empty($operations_) && is_array($operations_) ? implode(', ',$operations_) : __('NA'); $user_restrict = !empty($restriction['user']) ? $restriction ['user'] : __('NA.'); $private_path = !empty($restriction['private_dir']) ? $restriction['private_dir'] : ''; $hasInvalidPath = false; if(!empty($private_path) && empty($settings['disable_path_restriction']) && !fileorganizer_validate_path($private_path)){ $hasInvalidPath = true; } echo ''; } }else{ echo ''; } ?>
'. $user_restrict.'
'.ucwords($operations).' '.( empty($restriction['private_dir']) ? __('NA') : $restriction['private_dir']).( $hasInvalidPath ? '  ' : '').' '.(empty($restriction['restrict_dirs']) ? __('NA') : $restriction['restrict_dirs']).' '.(empty($restriction['restrict_files']) ? __('NA') : $restriction['restrict_files']).' '.(empty($restriction['disable_toolbar']) ? __('No') : __('Yes') ).' '.(empty($restriction['disable_context_menu']) ? __('No') : __('Yes')).'
User Restrictions not found!

'.__( ucwords(($operation == 'rm' ? 'remove' : $operation))).'
'; } ?>


Note: It will override "File Manager Path" settings. '. __("File manager path restriction is enabled, access outside of your WordPress installation path is not permitted.").''; } ?>

Note: The extensions are case-sensitive and must be separated by vertical bar (|) without any spaces. Example: .jpg, JPG' ); ?>

   
options = empty($options) ? array() : $options; } fileorganizer_pro_update_checker(); // Load license fileorganizer_pro_load_license(); // Check for updates include_once(FILEORGANIZER_PRO_DIR.'/main/plugin-update-checker.php'); $fileorganizer_updater = Fileorganizer_PucFactory::buildUpdateChecker(fileorganizer_pro_api_url().'/updates.php?version='.FILEORGANIZER_PRO_VERSION, FILEORGANIZER_PRO_FILE); // Add the license key to query arguments $fileorganizer_updater->addQueryArgFilter('fileorganizer_pro_updater_filter_args'); // Show the text to install the license key add_filter('puc_manual_final_check_link-fileorganizer-pro', 'fileorganizer_pro_updater_check_link', 10, 1); // Nag informing the user to install the free version. if(current_user_can('activate_plugins')){ add_action('admin_notices', 'fileorganizer_pro_free_version_nag', 9); add_action('admin_menu', 'fileorganizer_pro_add_menu', 9); } $is_network_wide = fileorganizer_pro_is_network_active('fileorganizer-pro'); $_fo_version = get_option('fileorganizer_version'); $req_free_update = !empty($_fo_version) && version_compare($_fo_version, '1.0.9', '<'); if($is_network_wide){ $free_installed = get_site_option('fileorganizer_free_installed'); }else{ $free_installed = get_option('fileorganizer_free_installed'); } if(!empty($free_installed)){ return; } // Include the necessary stuff include_once(ABSPATH . 'wp-admin/includes/plugin-install.php'); include_once(ABSPATH . 'wp-admin/includes/plugin.php'); include_once(ABSPATH . 'wp-admin/includes/file.php'); if(file_exists(WP_PLUGIN_DIR . '/fileorganizer/fileorganizer.php') && is_plugin_inactive('/fileorganizer/fileorganizer.php') && empty($req_free_update)) { if($is_network_wide){ update_site_option('fileorganizer_free_installed', time()); }else{ update_option('fileorganizer_free_installed', time()); } activate_plugin('/fileorganizer/fileorganizer.php', '', $is_network_wide); remove_action('admin_notices', 'fileorganizer_pro_free_version_nag', 9); remove_action('admin_menu', 'fileorganizer_pro_add_menu', 9); return; } // Includes necessary for Plugin_Upgrader and Plugin_Installer_Skin include_once(ABSPATH . 'wp-admin/includes/misc.php'); include_once(ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'); // Filter to prevent the activate text add_filter('install_plugin_complete_actions', 'fileorganizer_pro_prevent_activation_text', 10, 3); $upgrader = new Plugin_Upgrader(new WP_Ajax_Upgrader_Skin()); // Upgrade the plugin to the latest version of free already installed. if(!empty($req_free_update)){ $installed = $upgrader->upgrade('fileorganizer/fileorganizer.php'); }else{ $installed = $upgrader->install('https://downloads.wordpress.org/plugin/fileorganizer.zip'); } if(!is_wp_error($installed) && $installed){ if($is_network_wide){ update_site_option('fileorganizer_free_installed', time()); }else{ update_option('fileorganizer_free_installed', time()); } activate_plugin('fileorganizer/fileorganizer.php', '', $is_network_wide); remove_action('admin_notices', 'fileorganizer_pro_free_version_nag', 9); remove_action('admin_menu', 'fileorganizer_pro_add_menu', 9); } } // Do not shows the activation text if function fileorganizer_pro_prevent_activation_text($install_actions, $api, $plugin_file){ if($plugin_file == 'fileorganizer/fileorganizer.php'){ return array(); } return $install_actions; } function fileorganizer_pro_free_version_nag(){ $fo_version = get_option('fileorganizer_version'); $lower_version = __('You have not installed the free version of FileOrganizer. FileOrganizer Pro depends on the free version, so you must install it first in order to use FileOrganizer Pro.'); if(!empty($fo_version) && version_compare($fo_version, '1.0.9', '<')){ $lower_version = __('You are using an older version of the free version of FileOrganizer, please update FileOrganizer to work without any issues'); } echo '

'.esc_html($lower_version).' Install/Update Now

'; } function fileorganizer_pro_add_menu(){ add_menu_page('FileOrganizer Settings', 'FileOrganizer', 'activate_plugins', 'fileorganizer', 'fileorganizer_pro_menu_page'); } function fileorganizer_pro_menu_page(){ echo '

FileOrganizer Free version is not installed / outdated!

FileOrganizer Pro depends on the free version of FileOrganizer, so you need to install / update the free version first.

Install/Update Now

'; }main/functions.php000064400000021565147600405370010230 0ustar00 5){ $lic['license'] = $parent; // Load license of Soft Pro }elseif(!empty($parent)){ $license_field = 'softaculous_pro_license'; $lic = get_option('softaculous_pro_license', []); // My license }else{ $lic = get_option($license_field, []); } // Loaded license is a Soft Pro if(!empty($lic['license']) && preg_match('/^softwp/is', $lic['license'])){ $license_field = 'softaculous_pro_license'; $license_api_url = 'https://a.softaculous.com/softwp/'; $prods = apply_filters('softaculous_pro_products', []); }else{ $prods = []; } if(empty($lic['last_update'])){ $lic['last_update'] = time() - 86600; } // Update license details as well if(!empty($lic) && !empty($lic['license']) && (time() - @$lic['last_update']) >= 86400){ $url = $license_api_url.'/license.php?license='.$lic['license'].'&prods='.implode(',', $prods).'&url='.rawurlencode(site_url()); $resp = wp_remote_get($url); $lic_resp = $resp; //Did we get a response ? if(is_array($resp)){ $tosave = json_decode($resp['body'], true); //Is it the license ? if(!empty($tosave['license'])){ $tosave['last_update'] = time(); update_option($license_field, $tosave); $lic = $tosave; } } } // If the license is Free or Expired check for Softaculous Pro license if(empty($lic) || empty($lic['active'])){ if(function_exists('softaculous_pro_load_license')){ $softaculous_license = softaculous_pro_load_license(); if(!empty($softaculous_license['license']) && (!empty($softaculous_license['active']) || empty($lic['license'])) ){ $lic = $softaculous_license; } }elseif(empty($parent)){ $lic = get_option('softaculous_pro_license', []); if(!empty($lic)){ fileorganizer_pro_load_license(1); } } } $fileorganizer->license = $lic; } add_filter('softaculous_pro_products', 'fileorganizer_softaculous_pro_products', 10, 1); function fileorganizer_softaculous_pro_products($r = []){ $r['fileorganizer'] = 'fileorganizer'; return $r; } // Add our license key if ANY function fileorganizer_pro_updater_filter_args($queryArgs){ global $fileorganizer; if (!empty($fileorganizer->license['license'])){ $queryArgs['license'] = $fileorganizer->license['license']; } $queryArgs['url'] = rawurlencode(site_url()); return $queryArgs; } // Handle the Check for update link and ask to install license key function fileorganizer_pro_updater_check_link($final_link){ global $fileorganizer; if(empty($fileorganizer->license['license'])){ return 'Install FileOrganizer Pro License Key'; } return $final_link; } // Prevent update of fileorganizer free function fileorganizer_pro_get_free_version_num(){ if(defined('FILEORGANIZER_VERSION')){ return FILEORGANIZER_VERSION; } // In case of fileorganizer deactive return fileorganizer_pro_file_get_version_num('fileorganizer/fileorganizer.php'); } // Prevent update of fileorganizer free function fileorganizer_pro_file_get_version_num($plugin){ // In case of fileorganizer deactive include_once(ABSPATH . 'wp-admin/includes/plugin.php'); $plugin_data = get_plugin_data(WP_PLUGIN_DIR . '/'.$plugin); if(empty($plugin_data)){ return false; } return $plugin_data['Version']; } // Prevent update of fileorganizer free function fileorganizer_pro_disable_manual_update_for_plugin($transient){ $plugin = 'fileorganizer/fileorganizer.php'; // Is update available? if(!isset($transient->response) || !isset($transient->response[$plugin])){ return $transient; } $free_version = fileorganizer_pro_get_free_version_num(); $pro_version = FILEORGANIZER_PRO_VERSION; if(!empty($GLOBALS['fileorganizer_pro_is_upgraded'])){ $pro_version = fileorganizer_pro_file_get_version_num('fileorganizer-pro/fileorganizer-pro.php'); } // Update the fileorganizer version to the equivalent of Pro version if(!empty($pro_version) && version_compare($free_version, $pro_version, '<')){ $transient->response[$plugin]->new_version = $pro_version; $transient->response[$plugin]->package = 'https://downloads.wordpress.org/plugin/fileorganizer.'.$pro_version.'.zip'; }else{ unset($transient->response[$plugin]); } return $transient; } // Auto update free version after update pro version function fileorganizer_pro_update_free_after_pro($upgrader_object, $options){ // Check if the action is an update for the plugins if($options['action'] != 'update' || $options['type'] != 'plugin'){ return; } // Define the slugs for the free and pro plugins $free_slug = 'fileorganizer/fileorganizer.php'; $pro_slug = 'fileorganizer-pro/fileorganizer-pro.php'; // Check if the pro plugin is in the list of updated plugins if( (isset($options['plugins']) && in_array($pro_slug, $options['plugins']) && !in_array($free_slug, $options['plugins'])) || (isset($options['plugin']) && $pro_slug == $options['plugin']) ){ // Trigger the update for the free plugin $current_version = fileorganizer_pro_get_free_version_num(); if(empty($current_version)){ return; } $GLOBALS['fileorganizer_pro_is_upgraded'] = true; // This will set the 'update_plugins' transient again wp_update_plugins(); // Check for updates for the free plugin $update_plugins = get_site_transient('update_plugins'); if(empty($update_plugins) || !isset($update_plugins->response[$free_slug]) || version_compare($update_plugins->response[$free_slug]->new_version, $current_version, '<=')){ return; } require_once(ABSPATH . 'wp-admin/includes/plugin.php'); require_once(ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'); $skin = wp_doing_ajax()? new WP_Ajax_Upgrader_Skin() : null; $upgrader = new Plugin_Upgrader($skin); $upgraded = $upgrader->upgrade($free_slug); if(!is_wp_error($upgraded) && $upgraded){ // Re-active free plugins if( file_exists( WP_PLUGIN_DIR . '/'. $free_slug ) && is_plugin_inactive($free_slug) ){ activate_plugin($free_slug); // TODO for network } // Re-active pro plugins if( file_exists( WP_PLUGIN_DIR . '/'. $pro_slug ) && is_plugin_inactive($pro_slug) ){ activate_plugin($pro_slug); // TODO for network } } } } function fileorganizer_pro_api_url($main_server = 0, $suffix = 'fileorganizer'){ global $fileorganizer; $r = array( 'https://s0.softaculous.com/a/softwp/', 'https://s1.softaculous.com/a/softwp/', 'https://s2.softaculous.com/a/softwp/', 'https://s3.softaculous.com/a/softwp/', 'https://s4.softaculous.com/a/softwp/', 'https://s5.softaculous.com/a/softwp/', 'https://s7.softaculous.com/a/softwp/', 'https://s8.softaculous.com/a/softwp/' ); $mirror = $r[array_rand($r)]; // If the license is newly issued, we need to fetch from API only if(!empty($main_server) || empty($fileorganizer->license['last_edit']) || (!empty($fileorganizer->license['last_edit']) && (time() - 3600) < $fileorganizer->license['last_edit']) ){ $mirror = FILEORGANIZER_API; } if(!empty($suffix)){ $mirror = str_replace('/softwp', '/'.$suffix, $mirror); } return $mirror; }main/license.php000064400000010011147600405370007622 0ustar00'.var_export($lic_resp, true)), 'error'); return; } $json = json_decode($lic_resp['body'], true); if(empty($json['license'])){ fileorganizer_notify(__('The license key is invalid'), 'error'); return; } fileorganizer_notify(__('Successfully updated the license key')); } if(isset($_REQUEST['save_fileorganizer_license'])){ fileorganizer_pro_license(); } ?>

license['license']) ? 'Unlicensed    ' : '')?>  
license['license'])){ $expires = $fileorganizer->license['expires']; $expires = substr($expires, 0, 4).'/'.substr($expires, 4, 2).'/'.substr($expires, 6); echo '
License Status : '.(empty($fileorganizer->license['status_txt']) ? 'N.A.' : wp_kses_post($fileorganizer->license['status_txt'])).'       License Expires : '.($fileorganizer->license['expires'] <= date('Ymd') ? ''.esc_attr($expires).'' : esc_attr($expires)).'
'; }?>
URL
Path
.htaccess Yes' : 'No');?>
main/plugin-update-checker.php000064400000151545147600405370012402 0ustar00metadataUrl = $metadataUrl; $this->pluginAbsolutePath = $pluginFile; $this->pluginFile = plugin_basename($this->pluginAbsolutePath); $this->muPluginFile = $muPluginFile; $this->slug = $slug; $this->optionName = $optionName; $this->debugMode = (bool)(constant('WP_DEBUG')); //If no slug is specified, use the name of the main plugin file as the slug. //For example, 'my-cool-plugin/cool-plugin.php' becomes 'cool-plugin'. if ( empty($this->slug) ){ $this->slug = basename($this->pluginFile, '.php'); } //Plugin slugs must be unique. $slugCheckFilter = 'puc_is_slug_in_use-' . $this->slug; $slugUsedBy = apply_filters($slugCheckFilter, false); if ( $slugUsedBy ) { $this->triggerError(sprintf( 'Plugin slug "%s" is already in use by %s. Slugs must be unique.', htmlentities($this->slug), htmlentities($slugUsedBy) ), E_USER_ERROR); } add_filter($slugCheckFilter, array($this, 'getAbsolutePath')); if ( empty($this->optionName) ){ $this->optionName = 'external_updates-' . $this->slug; } //Backwards compatibility: If the plugin is a mu-plugin but no $muPluginFile is specified, assume //it's the same as $pluginFile given that it's not in a subdirectory (WP only looks in the base dir). if ( (strpbrk($this->pluginFile, '/\\') === false) && $this->isUnknownMuPlugin() ) { $this->muPluginFile = $this->pluginFile; } $this->scheduler = $this->createScheduler($checkPeriod); $this->upgraderStatus = new Fileorganizer_PucUpgraderStatus_3_2(); $this->installHooks(); } /** * Create an instance of the scheduler. * * This is implemented as a method to make it possible for plugins to subclass the update checker * and substitute their own scheduler. * * @param int $checkPeriod * @return Fileorganizer_PucScheduler_3_2 */ protected function createScheduler($checkPeriod) { return new Fileorganizer_PucScheduler_3_2($this, $checkPeriod); } /** * Install the hooks required to run periodic update checks and inject update info * into WP data structures. * * @return void */ protected function installHooks(){ //Override requests for plugin information add_filter('plugins_api', array($this, 'injectInfo'), 20, 3); //Insert our update info into the update array maintained by WP. add_filter('site_transient_update_plugins', array($this,'injectUpdate')); //WP 3.0+ add_filter('transient_update_plugins', array($this,'injectUpdate')); //WP 2.8+ add_filter('site_transient_update_plugins', array($this, 'injectTranslationUpdates')); add_filter('plugin_row_meta', array($this, 'addCheckForUpdatesLink'), 10, 2); add_action('admin_init', array($this, 'handleManualCheck')); add_action('all_admin_notices', array($this, 'displayManualCheckResult')); //Clear the version number cache when something - anything - is upgraded or WP clears the update cache. add_filter('upgrader_post_install', array($this, 'clearCachedVersion')); add_action('delete_site_transient_update_plugins', array($this, 'clearCachedVersion')); //Clear translation updates when WP clears the update cache. //This needs to be done directly because the library doesn't actually remove obsolete plugin updates, //it just hides them (see getUpdate()). We can't do that with translations - too much disk I/O. add_action('delete_site_transient_update_plugins', array($this, 'clearCachedTranslationUpdates')); if ( did_action('plugins_loaded') ) { $this->initDebugBarPanel(); } else { add_action('plugins_loaded', array($this, 'initDebugBarPanel')); } //Rename the update directory to be the same as the existing directory. add_filter('upgrader_source_selection', array($this, 'fixDirectoryName'), 10, 3); //Enable language support (i18n). load_plugin_textdomain('plugin-update-checker', false, plugin_basename(dirname(__FILE__)) . '/languages'); //Allow HTTP requests to the metadata URL even if it's on a local host. $this->metadataHost = @parse_url($this->metadataUrl, PHP_URL_HOST); add_filter('http_request_host_is_external', array($this, 'allowMetadataHost'), 10, 2); } /** * Explicitly allow HTTP requests to the metadata URL. * * WordPress has a security feature where the HTTP API will reject all requests that are sent to * another site hosted on the same server as the current site (IP match), a local host, or a local * IP, unless the host exactly matches the current site. * * This feature is opt-in (at least in WP 4.4). Apparently some people enable it. * * That can be a problem when you're developing your plugin and you decide to host the update information * on the same server as your test site. Update requests will mysteriously fail. * * We fix that by adding an exception for the metadata host. * * @param bool $allow * @param string $host * @return bool */ public function allowMetadataHost($allow, $host) { if ( strtolower($host) === strtolower($this->metadataHost) ) { return true; } return $allow; } /** * Retrieve plugin info from the configured API endpoint. * * @uses wp_remote_get() * * @param array $queryArgs Additional query arguments to append to the request. Optional. * @return FileorganizerInfo_3_2 */ public function requestInfo($queryArgs = array()){ //Query args to append to the URL. Plugins can add their own by using a filter callback (see addQueryArgFilter()). $installedVersion = $this->getInstalledVersion(); $queryArgs['installed_version'] = ($installedVersion !== null) ? $installedVersion : ''; $queryArgs = apply_filters('puc_request_info_query_args-'.$this->slug, $queryArgs); //Various options for the wp_remote_get() call. Plugins can filter these, too. $options = array( 'timeout' => 10, //seconds 'headers' => array( 'Accept' => 'application/json' ), ); $options = apply_filters('puc_request_info_options-'.$this->slug, $options); //The plugin info should be at 'http://your-api.com/url/here/$slug/info.json' $url = $this->metadataUrl; if ( !empty($queryArgs) ){ $url = add_query_arg($queryArgs, $url); } $result = wp_remote_get( $url, $options ); //Try to parse the response $status = $this->validateApiResponse($result); $pluginInfo = null; if ( !is_wp_error($status) ){ $pluginInfo = FileorganizerInfo_3_2::fromJson($result['body']); if ( $pluginInfo !== null ) { $pluginInfo->filename = $this->pluginFile; $pluginInfo->slug = $this->slug; } } else { $this->triggerError( sprintf('The URL %s does not point to a valid plugin metadata file. ', $url) . $status->get_error_message(), E_USER_WARNING ); } $pluginInfo = apply_filters('puc_request_info_result-'.$this->slug, $pluginInfo, $result); return $pluginInfo; } /** * Check if $result is a successful update API response. * * @param array|WP_Error $result * @return true|WP_Error */ private function validateApiResponse($result) { if ( is_wp_error($result) ) { /** @var WP_Error $result */ return new WP_Error($result->get_error_code(), 'WP HTTP Error: ' . $result->get_error_message()); } if ( !isset($result['response']['code']) ) { return new WP_Error('puc_no_response_code', 'wp_remote_get() returned an unexpected result.'); } if ( $result['response']['code'] !== 200 ) { return new WP_Error( 'puc_unexpected_response_code', 'HTTP response code is ' . $result['response']['code'] . ' (expected: 200)' ); } if ( empty($result['body']) ) { return new WP_Error('puc_empty_response', 'The metadata file appears to be empty.'); } return true; } /** * Retrieve the latest update (if any) from the configured API endpoint. * * @uses FileorganizerUpdateChecker::requestInfo() * * @return FileorganizerUpdate_3_2 An instance of FileorganizerUpdate, or NULL when no updates are available. */ public function requestUpdate(){ //For the sake of simplicity, this function just calls requestInfo() //and transforms the result accordingly. $pluginInfo = $this->requestInfo(array('checking_for_updates' => '1')); if ( $pluginInfo == null ){ return null; } $update = FileorganizerUpdate_3_2::fromFileorganizerInfo($pluginInfo); //Keep only those translation updates that apply to this site. $update->translations = $this->filterApplicableTranslations($update->translations); return $update; } /** * Filter a list of translation updates and return a new list that contains only updates * that apply to the current site. * * @param array $translations * @return array */ private function filterApplicableTranslations($translations) { $languages = array_flip(array_values(get_available_languages())); $installedTranslations = wp_get_installed_translations('plugins'); if ( isset($installedTranslations[$this->slug]) ) { $installedTranslations = $installedTranslations[$this->slug]; } else { $installedTranslations = array(); } $applicableTranslations = array(); foreach($translations as $translation) { //Does it match one of the available core languages? $isApplicable = array_key_exists($translation->language, $languages); //Is it more recent than an already-installed translation? if ( isset($installedTranslations[$translation->language]) ) { $updateTimestamp = strtotime($translation->updated); $installedTimestamp = strtotime($installedTranslations[$translation->language]['PO-Revision-Date']); $isApplicable = $updateTimestamp > $installedTimestamp; } if ( $isApplicable ) { $applicableTranslations[] = $translation; } } return $applicableTranslations; } /** * Get the currently installed version of the plugin. * * @return string Version number. */ public function getInstalledVersion(){ if ( isset($this->cachedInstalledVersion) ) { return $this->cachedInstalledVersion; } $pluginHeader = $this->getPluginHeader(); if ( isset($pluginHeader['Version']) ) { $this->cachedInstalledVersion = $pluginHeader['Version']; return $pluginHeader['Version']; } else { //This can happen if the filename points to something that is not a plugin. $this->triggerError( sprintf( "Can't to read the Version header for '%s'. The filename is incorrect or is not a plugin.", $this->pluginFile ), E_USER_WARNING ); return null; } } /** * Get plugin's metadata from its file header. * * @return array */ protected function getPluginHeader() { if ( !is_file($this->pluginAbsolutePath) ) { //This can happen if the plugin filename is wrong. $this->triggerError( sprintf( "Can't to read the plugin header for '%s'. The file does not exist.", $this->pluginFile ), E_USER_WARNING ); return array(); } if ( !function_exists('get_plugin_data') ){ /** @noinspection PhpIncludeInspection */ require_once( ABSPATH . '/wp-admin/includes/plugin.php' ); } return get_plugin_data($this->pluginAbsolutePath, false, false); } /** * Check for plugin updates. * The results are stored in the DB option specified in $optionName. * * @return FileorganizerUpdate_3_2|null */ public function checkForUpdates(){ $installedVersion = $this->getInstalledVersion(); //Fail silently if we can't find the plugin or read its header. if ( $installedVersion === null ) { $this->triggerError( sprintf('Skipping update check for %s - installed version unknown.', $this->pluginFile), E_USER_WARNING ); return null; } $state = $this->getUpdateState(); if ( empty($state) ){ $state = new stdClass; $state->lastCheck = 0; $state->checkedVersion = ''; $state->update = null; } $state->lastCheck = time(); $state->checkedVersion = $installedVersion; $this->setUpdateState($state); //Save before checking in case something goes wrong $state->update = $this->requestUpdate(); $this->setUpdateState($state); return $this->getUpdate(); } /** * Load the update checker state from the DB. * * @return stdClass|null */ public function getUpdateState() { $state = get_site_option($this->optionName, null); if ( empty($state) || !is_object($state)) { $state = null; } if ( isset($state, $state->update) && is_object($state->update) ) { $state->update = FileorganizerUpdate_3_2::fromObject($state->update); } return $state; } /** * Persist the update checker state to the DB. * * @param StdClass $state * @return void */ private function setUpdateState($state) { if ( isset($state->update) && is_object($state->update) && method_exists($state->update, 'toStdClass') ) { $update = $state->update; /** @var FileorganizerUpdate_3_2 $update */ $state->update = $update->toStdClass(); } update_site_option($this->optionName, $state); } /** * Reset update checker state - i.e. last check time, cached update data and so on. * * Call this when your plugin is being uninstalled, or if you want to * clear the update cache. */ public function resetUpdateState() { delete_site_option($this->optionName); } /** * Intercept plugins_api() calls that request information about our plugin and * use the configured API endpoint to satisfy them. * * @see plugins_api() * * @param mixed $result * @param string $action * @param array|object $args * @return mixed */ public function injectInfo($result, $action = null, $args = null){ $relevant = ($action == 'plugin_information') && isset($args->slug) && ( ($args->slug == $this->slug) || ($args->slug == dirname($this->pluginFile)) ); if ( !$relevant ) { return $result; } $pluginInfo = $this->requestInfo(); $pluginInfo = apply_filters('puc_pre_inject_info-' . $this->slug, $pluginInfo); if ( $pluginInfo ) { return $pluginInfo->toWpFormat(); } return $result; } /** * Insert the latest update (if any) into the update list maintained by WP. * * @param StdClass $updates Update list. * @return StdClass Modified update list. */ public function injectUpdate($updates){ //Is there an update to insert? $update = $this->getUpdate(); //No update notifications for mu-plugins unless explicitly enabled. The MU plugin file //is usually different from the main plugin file so the update wouldn't show up properly anyway. if ( $this->isUnknownMuPlugin() ) { $update = null; } if ( !empty($update) ) { //Let plugins filter the update info before it's passed on to WordPress. $update = apply_filters('puc_pre_inject_update-' . $this->slug, $update); $updates = $this->addUpdateToList($updates, $update); } else { //Clean up any stale update info. $updates = $this->removeUpdateFromList($updates); } return $updates; } /** * @param StdClass|null $updates * @param FileorganizerUpdate_3_2 $updateToAdd * @return StdClass */ private function addUpdateToList($updates, $updateToAdd) { if ( !is_object($updates) ) { $updates = new stdClass(); $updates->response = array(); } $wpUpdate = $updateToAdd->toWpFormat(); $pluginFile = $this->pluginFile; if ( $this->isMuPlugin() ) { //WP does not support automatic update installation for mu-plugins, but we can still display a notice. $wpUpdate->package = null; $pluginFile = $this->muPluginFile; } $updates->response[$pluginFile] = $wpUpdate; return $updates; } /** * @param stdClass|null $updates * @return stdClass|null */ private function removeUpdateFromList($updates) { if ( isset($updates, $updates->response) ) { unset($updates->response[$this->pluginFile]); if ( !empty($this->muPluginFile) ) { unset($updates->response[$this->muPluginFile]); } } return $updates; } /** * Insert translation updates into the list maintained by WordPress. * * @param stdClass $updates * @return stdClass */ public function injectTranslationUpdates($updates) { $translationUpdates = $this->getTranslationUpdates(); if ( empty($translationUpdates) ) { return $updates; } //Being defensive. if ( !is_object($updates) ) { $updates = new stdClass(); } if ( !isset($updates->translations) ) { $updates->translations = array(); } //In case there's a name collision with a plugin hosted on wordpress.org, //remove any preexisting updates that match our plugin. $translationType = 'plugin'; $filteredTranslations = array(); foreach($updates->translations as $translation) { if ( ($translation['type'] === $translationType) && ($translation['slug'] === $this->slug) ) { continue; } $filteredTranslations[] = $translation; } $updates->translations = $filteredTranslations; //Add our updates to the list. foreach($translationUpdates as $update) { $convertedUpdate = array_merge( array( 'type' => $translationType, 'slug' => $this->slug, 'autoupdate' => 0, //AFAICT, WordPress doesn't actually use the "version" field for anything. //But lets make sure it's there, just in case. 'version' => isset($update->version) ? $update->version : ('1.' . strtotime($update->updated)), ), (array)$update ); $updates->translations[] = $convertedUpdate; } return $updates; } /** * Rename the update directory to match the existing plugin directory. * * When WordPress installs a plugin or theme update, it assumes that the ZIP file will contain * exactly one directory, and that the directory name will be the same as the directory where * the plugin/theme is currently installed. * * GitHub and other repositories provide ZIP downloads, but they often use directory names like * "project-branch" or "project-tag-hash". We need to change the name to the actual plugin folder. * * This is a hook callback. Don't call it from a plugin. * * @param string $source The directory to copy to /wp-content/plugins. Usually a subdirectory of $remoteSource. * @param string $remoteSource WordPress has extracted the update to this directory. * @param WP_Upgrader $upgrader * @return string|WP_Error */ public function fixDirectoryName($source, $remoteSource, $upgrader) { global $wp_filesystem; /** @var WP_Filesystem_Base $wp_filesystem */ //Basic sanity checks. if ( !isset($source, $remoteSource, $upgrader, $upgrader->skin, $wp_filesystem) ) { return $source; } //If WordPress is upgrading anything other than our plugin, leave the directory name unchanged. if ( !$this->isPluginBeingUpgraded($upgrader) ) { return $source; } //Rename the source to match the existing plugin directory. $pluginDirectoryName = dirname($this->pluginFile); if ( $pluginDirectoryName === '.' ) { return $source; } $correctedSource = trailingslashit($remoteSource) . $pluginDirectoryName . '/'; if ( $source !== $correctedSource ) { //The update archive should contain a single directory that contains the rest of plugin files. Otherwise, //WordPress will try to copy the entire working directory ($source == $remoteSource). We can't rename //$remoteSource because that would break WordPress code that cleans up temporary files after update. if ( $this->isBadDirectoryStructure($remoteSource) ) { return new WP_Error( 'puc-incorrect-directory-structure', sprintf( 'The directory structure of the update is incorrect. All plugin files should be inside ' . 'a directory named %s, not at the root of the ZIP file.', htmlentities($this->slug) ) ); } /** @var WP_Upgrader_Skin $upgrader->skin */ $upgrader->skin->feedback(sprintf( 'Renaming %s to %s…', '' . basename($source) . '', '' . $pluginDirectoryName . '' )); if ( $wp_filesystem->move($source, $correctedSource, true) ) { $upgrader->skin->feedback('Plugin directory successfully renamed.'); return $correctedSource; } else { return new WP_Error( 'puc-rename-failed', 'Unable to rename the update to match the existing plugin directory.' ); } } return $source; } /** * Check for incorrect update directory structure. An update must contain a single directory, * all other files should be inside that directory. * * @param string $remoteSource Directory path. * @return bool */ private function isBadDirectoryStructure($remoteSource) { global $wp_filesystem; /** @var WP_Filesystem_Base $wp_filesystem */ $sourceFiles = $wp_filesystem->dirlist($remoteSource); if ( is_array($sourceFiles) ) { $sourceFiles = array_keys($sourceFiles); $firstFilePath = trailingslashit($remoteSource) . $sourceFiles[0]; return (count($sourceFiles) > 1) || (!$wp_filesystem->is_dir($firstFilePath)); } //Assume it's fine. return false; } /** * Is there and update being installed RIGHT NOW, for this specific plugin? * * @param WP_Upgrader|null $upgrader The upgrader that's performing the current update. * @return bool */ public function isPluginBeingUpgraded($upgrader = null) { return $this->upgraderStatus->isPluginBeingUpgraded($this->pluginFile, $upgrader); } /** * Get the details of the currently available update, if any. * * If no updates are available, or if the last known update version is below or equal * to the currently installed version, this method will return NULL. * * Uses cached update data. To retrieve update information straight from * the metadata URL, call requestUpdate() instead. * * @return FileorganizerUpdate_3_2|null */ public function getUpdate() { $state = $this->getUpdateState(); /** @var StdClass $state */ //Is there an update available? if ( isset($state, $state->update) ) { $update = $state->update; //Check if the update is actually newer than the currently installed version. $installedVersion = $this->getInstalledVersion(); if ( ($installedVersion !== null) && version_compare($update->version, $installedVersion, '>') ){ $update->filename = $this->pluginFile; return $update; } } return null; } /** * Get a list of available translation updates. * * This method will return an empty array if there are no updates. * Uses cached update data. * * @return array */ public function getTranslationUpdates() { $state = $this->getUpdateState(); if ( isset($state, $state->update, $state->update->translations) ) { return $state->update->translations; } return array(); } /** * Remove all cached translation updates. * * @see wp_clean_update_cache */ public function clearCachedTranslationUpdates() { $state = $this->getUpdateState(); if ( isset($state, $state->update, $state->update->translations) ) { $state->update->translations = array(); $this->setUpdateState($state); } } /** * Add a "Check for updates" link to the plugin row in the "Plugins" page. By default, * the new link will appear after the "Visit plugin site" link. * * You can change the link text by using the "puc_manual_check_link-$slug" filter. * Returning an empty string from the filter will disable the link. * * @param array $pluginMeta Array of meta links. * @param string $pluginFile * @return array */ public function addCheckForUpdatesLink($pluginMeta, $pluginFile) { $isRelevant = ($pluginFile == $this->pluginFile) || (!empty($this->muPluginFile) && $pluginFile == $this->muPluginFile); if ( $isRelevant && current_user_can('update_plugins') ) { $linkUrl = wp_nonce_url( add_query_arg( array( 'puc_check_for_updates' => 1, 'puc_slug' => $this->slug, ), self_admin_url('plugins.php') ), 'puc_check_for_updates' ); $linkText = apply_filters('puc_manual_check_link-' . $this->slug, __('Check for updates', 'plugin-update-checker')); if ( !empty($linkText) ) { $final_link = sprintf('%s', esc_attr($linkUrl), $linkText); $pluginMeta[] = apply_filters('puc_manual_final_check_link-' . $this->slug, $final_link); } } return $pluginMeta; } /** * Check for updates when the user clicks the "Check for updates" link. * @see self::addCheckForUpdatesLink() * * @return void */ public function handleManualCheck() { $shouldCheck = isset($_GET['puc_check_for_updates'], $_GET['puc_slug']) && $_GET['puc_slug'] == $this->slug && current_user_can('update_plugins') && check_admin_referer('puc_check_for_updates'); if ( $shouldCheck ) { $update = $this->checkForUpdates(); $status = ($update === null) ? 'no_update' : 'update_available'; wp_redirect(add_query_arg( array( 'puc_update_check_result' => $status, 'puc_slug' => $this->slug, ), self_admin_url('plugins.php') )); } } /** * Display the results of a manual update check. * @see self::handleManualCheck() * * You can change the result message by using the "puc_manual_check_message-$slug" filter. */ public function displayManualCheckResult() { if ( isset($_GET['puc_update_check_result'], $_GET['puc_slug']) && ($_GET['puc_slug'] == $this->slug) ) { $status = strval($_GET['puc_update_check_result']); if ( $status == 'no_update' ) { $message = __('This plugin is up to date.', 'plugin-update-checker'); } else if ( $status == 'update_available' ) { $message = __('A new version of this plugin is available.', 'plugin-update-checker'); } else { $message = sprintf(__('Unknown update checker status "%s"', 'plugin-update-checker'), htmlentities($status)); } printf( '

%s

', apply_filters('puc_manual_check_message-' . $this->slug, $message, $status) ); } } /** * Check if the plugin file is inside the mu-plugins directory. * * @return bool */ protected function isMuPlugin() { static $cachedResult = null; if ( $cachedResult === null ) { //Convert both paths to the canonical form before comparison. $muPluginDir = realpath(WPMU_PLUGIN_DIR); $pluginPath = realpath($this->pluginAbsolutePath); if(!empty($muPluginDir)){ $cachedResult = (strpos($pluginPath, $muPluginDir) === 0); }else{ $cachedResult = false; } } return $cachedResult; } /** * MU plugins are partially supported, but only when we know which file in mu-plugins * corresponds to this plugin. * * @return bool */ protected function isUnknownMuPlugin() { return empty($this->muPluginFile) && $this->isMuPlugin(); } /** * Clear the cached plugin version. This method can be set up as a filter (hook) and will * return the filter argument unmodified. * * @param mixed $filterArgument * @return mixed */ public function clearCachedVersion($filterArgument = null) { $this->cachedInstalledVersion = null; return $filterArgument; } /** * Get absolute path to the main plugin file. * * @return string */ public function getAbsolutePath() { return $this->pluginAbsolutePath; } /** * Register a callback for filtering query arguments. * * The callback function should take one argument - an associative array of query arguments. * It should return a modified array of query arguments. * * @uses add_filter() This method is a convenience wrapper for add_filter(). * * @param callable $callback * @return void */ public function addQueryArgFilter($callback){ add_filter('puc_request_info_query_args-'.$this->slug, $callback); } /** * Register a callback for filtering arguments passed to wp_remote_get(). * * The callback function should take one argument - an associative array of arguments - * and return a modified array or arguments. See the WP documentation on wp_remote_get() * for details on what arguments are available and how they work. * * @uses add_filter() This method is a convenience wrapper for add_filter(). * * @param callable $callback * @return void */ public function addHttpRequestArgFilter($callback){ add_filter('puc_request_info_options-'.$this->slug, $callback); } /** * Register a callback for filtering the plugin info retrieved from the external API. * * The callback function should take two arguments. If the plugin info was retrieved * successfully, the first argument passed will be an instance of FileorganizerInfo. Otherwise, * it will be NULL. The second argument will be the corresponding return value of * wp_remote_get (see WP docs for details). * * The callback function should return a new or modified instance of FileorganizerInfo or NULL. * * @uses add_filter() This method is a convenience wrapper for add_filter(). * * @param callable $callback * @return void */ public function addResultFilter($callback){ add_filter('puc_request_info_result-'.$this->slug, $callback, 10, 2); } /** * Register a callback for one of the update checker filters. * * Identical to add_filter(), except it automatically adds the "puc_" prefix * and the "-$plugin_slug" suffix to the filter name. For example, "request_info_result" * becomes "puc_request_info_result-your_plugin_slug". * * @param string $tag * @param callable $callback * @param int $priority * @param int $acceptedArgs */ public function addFilter($tag, $callback, $priority = 10, $acceptedArgs = 1) { add_filter('puc_' . $tag . '-' . $this->slug, $callback, $priority, $acceptedArgs); } /** * Initialize the update checker Debug Bar plugin/add-on thingy. */ public function initDebugBarPanel() { $debugBarPlugin = dirname(__FILE__) . '/debug-bar-plugin.php'; if ( class_exists('Debug_Bar', false) && file_exists($debugBarPlugin) ) { /** @noinspection PhpIncludeInspection */ require_once $debugBarPlugin; $this->debugBarPlugin = new Fileorganizer_PucDebugBarPlugin_3_2($this); } } /** * Trigger a PHP error, but only when $debugMode is enabled. * * @param string $message * @param int $errorType */ protected function triggerError($message, $errorType) { if ( $this->debugMode ) { trigger_error($message, $errorType); } } } endif; if ( !class_exists('FileorganizerInfo_3_2', false) ): /** * A container class for holding and transforming various plugin metadata. * * @author Janis Elsts * @copyright 2016 * @version 3.2 * @access public */ class FileorganizerInfo_3_2 { //Most fields map directly to the contents of the plugin's info.json file. //See the relevant docs for a description of their meaning. public $name; public $slug; public $version; public $homepage; public $sections = array(); public $banners; public $translations = array(); public $download_url; public $author; public $author_homepage; public $requires; public $tested; public $upgrade_notice; public $rating; public $num_ratings; public $downloaded; public $active_installs; public $last_updated; public $id = 0; //The native WP.org API returns numeric plugin IDs, but they're not used for anything. public $filename; //Plugin filename relative to the plugins directory. /** * Create a new instance of FileorganizerInfo from JSON-encoded plugin info * returned by an external update API. * * @param string $json Valid JSON string representing plugin info. * @return FileorganizerInfo_3_2|null New instance of FileorganizerInfo, or NULL on error. */ public static function fromJson($json){ /** @var StdClass $apiResponse */ $apiResponse = json_decode($json); if ( empty($apiResponse) || !is_object($apiResponse) ){ trigger_error( "Failed to parse plugin metadata. Try validating your .json file with http://jsonlint.com/", E_USER_NOTICE ); return null; } $valid = self::validateMetadata($apiResponse); if ( is_wp_error($valid) ){ trigger_error($valid->get_error_message(), E_USER_NOTICE); return null; } $info = new self(); foreach(get_object_vars($apiResponse) as $key => $value){ $info->$key = $value; } //json_decode decodes assoc. arrays as objects. We want it as an array. $info->sections = (array)$info->sections; return $info; } /** * Very, very basic validation. * * @param StdClass $apiResponse * @return bool|WP_Error */ protected static function validateMetadata($apiResponse) { if ( !isset($apiResponse->name, $apiResponse->version) || empty($apiResponse->name) || empty($apiResponse->version) ) { return new WP_Error( 'puc-invalid-metadata', "The plugin metadata file does not contain the required 'name' and/or 'version' keys." ); } return true; } /** * Transform plugin info into the format used by the native WordPress.org API * * @return object */ public function toWpFormat(){ $info = new stdClass; //The custom update API is built so that many fields have the same name and format //as those returned by the native WordPress.org API. These can be assigned directly. $sameFormat = array( 'name', 'slug', 'version', 'requires', 'tested', 'rating', 'upgrade_notice', 'num_ratings', 'downloaded', 'active_installs', 'homepage', 'last_updated', ); foreach($sameFormat as $field){ if ( isset($this->$field) ) { $info->$field = $this->$field; } else { $info->$field = null; } } //Other fields need to be renamed and/or transformed. $info->download_link = $this->download_url; $info->author = $this->getFormattedAuthor(); $info->sections = array_merge(array('description' => ''), $this->sections); if ( !empty($this->banners) ) { //WP expects an array with two keys: "high" and "low". Both are optional. //Docs: https://wordpress.org/plugins/about/faq/#banners $info->banners = is_object($this->banners) ? get_object_vars($this->banners) : $this->banners; $info->banners = array_intersect_key($info->banners, array('high' => true, 'low' => true)); } return $info; } protected function getFormattedAuthor() { if ( !empty($this->author_homepage) ){ return sprintf('%s', $this->author_homepage, $this->author); } return $this->author; } } endif; if ( !class_exists('FileorganizerUpdate_3_2', false) ): /** * A simple container class for holding information about an available update. * * @author Janis Elsts * @copyright 2016 * @version 3.2 * @access public */ class FileorganizerUpdate_3_2 { public $id = 0; public $slug; public $version; public $homepage; public $download_url; public $upgrade_notice; public $tested; public $translations = array(); public $filename; //Plugin filename relative to the plugins directory. private static $fields = array( 'id', 'slug', 'version', 'homepage', 'tested', 'download_url', 'upgrade_notice', 'filename', 'translations' ); /** * Create a new instance of FileorganizerUpdate from its JSON-encoded representation. * * @param string $json * @return FileorganizerUpdate_3_2|null */ public static function fromJson($json){ //Since update-related information is simply a subset of the full plugin info, //we can parse the update JSON as if it was a plugin info string, then copy over //the parts that we care about. $pluginInfo = FileorganizerInfo_3_2::fromJson($json); if ( $pluginInfo != null ) { return self::fromFileorganizerInfo($pluginInfo); } else { return null; } } /** * Create a new instance of FileorganizerUpdate based on an instance of FileorganizerInfo. * Basically, this just copies a subset of fields from one object to another. * * @param FileorganizerInfo_3_2 $info * @return FileorganizerUpdate_3_2 */ public static function fromFileorganizerInfo($info){ return self::fromObject($info); } /** * Create a new instance of FileorganizerUpdate by copying the necessary fields from * another object. * * @param StdClass|FileorganizerInfo_3_2|FileorganizerUpdate_3_2 $object The source object. * @return FileorganizerUpdate_3_2 The new copy. */ public static function fromObject($object) { $update = new self(); $fields = self::$fields; if ( !empty($object->slug) ) { $fields = apply_filters('puc_retain_fields-' . $object->slug, $fields); } foreach($fields as $field){ if (property_exists($object, $field)) { $update->$field = $object->$field; } } return $update; } /** * Create an instance of StdClass that can later be converted back to * a FileorganizerUpdate. Useful for serialization and caching, as it avoids * the "incomplete object" problem if the cached value is loaded before * this class. * * @return StdClass */ public function toStdClass() { $object = new stdClass(); $fields = self::$fields; if ( !empty($this->slug) ) { $fields = apply_filters('puc_retain_fields-' . $this->slug, $fields); } foreach($fields as $field){ if (property_exists($this, $field)) { $object->$field = $this->$field; } } return $object; } /** * Transform the update into the format used by WordPress native plugin API. * * @return object */ public function toWpFormat(){ $update = new stdClass; $update->id = $this->id; $update->slug = $this->slug; $update->new_version = $this->version; $update->url = $this->homepage; $update->package = $this->download_url; $update->tested = $this->tested; $update->plugin = $this->filename; if ( !empty($this->upgrade_notice) ){ $update->upgrade_notice = $this->upgrade_notice; } return $update; } } endif; if ( !class_exists('Fileorganizer_PucScheduler_3_2', false) ): /** * The scheduler decides when and how often to check for updates. * It calls @see FileorganizerUpdateChecker::checkForUpdates() to perform the actual checks. * * @version 3.2 */ class Fileorganizer_PucScheduler_3_2 { public $checkPeriod = 12; //How often to check for updates (in hours). public $throttleRedundantChecks = false; //Check less often if we already know that an update is available. public $throttledCheckPeriod = 72; /** * @var FileorganizerUpdateChecker_3_2 */ protected $updateChecker; private $cronHook = null; /** * Scheduler constructor. * * @param FileorganizerUpdateChecker_3_2 $updateChecker * @param int $checkPeriod How often to check for updates (in hours). */ public function __construct($updateChecker, $checkPeriod) { $this->updateChecker = $updateChecker; $this->checkPeriod = $checkPeriod; //Set up the periodic update checks $this->cronHook = 'check_plugin_updates-' . $this->updateChecker->slug; if ( $this->checkPeriod > 0 ){ //Trigger the check via Cron. //Try to use one of the default schedules if possible as it's less likely to conflict //with other plugins and their custom schedules. $defaultSchedules = array( 1 => 'hourly', 12 => 'twicedaily', 24 => 'daily', ); if ( array_key_exists($this->checkPeriod, $defaultSchedules) ) { $scheduleName = $defaultSchedules[$this->checkPeriod]; } else { //Use a custom cron schedule. $scheduleName = 'every' . $this->checkPeriod . 'hours'; add_filter('cron_schedules', array($this, '_addCustomSchedule')); } if ( !wp_next_scheduled($this->cronHook) && !defined('WP_INSTALLING') ) { wp_schedule_event(time(), $scheduleName, $this->cronHook); } add_action($this->cronHook, array($this, 'maybeCheckForUpdates')); register_deactivation_hook($this->updateChecker->pluginFile, array($this, '_removeUpdaterCron')); //In case Cron is disabled or unreliable, we also manually trigger //the periodic checks while the user is browsing the Dashboard. add_action( 'admin_init', array($this, 'maybeCheckForUpdates') ); //Like WordPress itself, we check more often on certain pages. /** @see wp_update_plugins */ add_action('load-update-core.php', array($this, 'maybeCheckForUpdates')); add_action('load-plugins.php', array($this, 'maybeCheckForUpdates')); add_action('load-update.php', array($this, 'maybeCheckForUpdates')); //This hook fires after a bulk update is complete. add_action('upgrader_process_complete', array($this, 'maybeCheckForUpdates'), 11, 0); } else { //Periodic checks are disabled. wp_clear_scheduled_hook($this->cronHook); } } /** * Check for updates if the configured check interval has already elapsed. * Will use a shorter check interval on certain admin pages like "Dashboard -> Updates" or when doing cron. * * You can override the default behaviour by using the "puc_check_now-$slug" filter. * The filter callback will be passed three parameters: * - Current decision. TRUE = check updates now, FALSE = don't check now. * - Last check time as a Unix timestamp. * - Configured check period in hours. * Return TRUE to check for updates immediately, or FALSE to cancel. * * This method is declared public because it's a hook callback. Calling it directly is not recommended. */ public function maybeCheckForUpdates(){ if ( empty($this->checkPeriod) ){ return; } $state = $this->updateChecker->getUpdateState(); $shouldCheck = empty($state) || !isset($state->lastCheck) || ( (time() - $state->lastCheck) >= $this->getEffectiveCheckPeriod() ); //Let plugin authors substitute their own algorithm. $shouldCheck = apply_filters( 'puc_check_now-' . $this->updateChecker->slug, $shouldCheck, (!empty($state) && isset($state->lastCheck)) ? $state->lastCheck : 0, $this->checkPeriod ); if ( $shouldCheck ) { $this->updateChecker->checkForUpdates(); } } /** * Calculate the actual check period based on the current status and environment. * * @return int Check period in seconds. */ protected function getEffectiveCheckPeriod() { $currentFilter = current_filter(); if ( in_array($currentFilter, array('load-update-core.php', 'upgrader_process_complete')) ) { //Check more often when the user visits "Dashboard -> Updates" or does a bulk update. $period = 60; } else if ( in_array($currentFilter, array('load-plugins.php', 'load-update.php')) ) { //Also check more often on the "Plugins" page and /wp-admin/update.php. $period = 3600; } else if ( $this->throttleRedundantChecks && ($this->updateChecker->getUpdate() !== null) ) { //Check less frequently if it's already known that an update is available. $period = $this->throttledCheckPeriod * 3600; } else if ( defined('DOING_CRON') && constant('DOING_CRON') ) { //WordPress cron schedules are not exact, so lets do an update check even //if slightly less than $checkPeriod hours have elapsed since the last check. $cronFuzziness = 20 * 60; $period = $this->checkPeriod * 3600 - $cronFuzziness; } else { $period = $this->checkPeriod * 3600; } return $period; } /** * Add our custom schedule to the array of Cron schedules used by WP. * * @param array $schedules * @return array */ public function _addCustomSchedule($schedules){ if ( $this->checkPeriod && ($this->checkPeriod > 0) ){ $scheduleName = 'every' . $this->checkPeriod . 'hours'; $schedules[$scheduleName] = array( 'interval' => $this->checkPeriod * 3600, 'display' => sprintf('Every %d hours', $this->checkPeriod), ); } return $schedules; } /** * Remove the scheduled cron event that the library uses to check for updates. * * @return void */ public function _removeUpdaterCron(){ wp_clear_scheduled_hook($this->cronHook); } /** * Get the name of the update checker's WP-cron hook. Mostly useful for debugging. * * @return string */ public function getCronHookName() { return $this->cronHook; } } endif; if ( !class_exists('Fileorganizer_PucUpgraderStatus_3_2', false) ): /** * A utility class that helps figure out which plugin WordPress is upgrading. * * It may seem strange to have an separate class just for that, but the task is surprisingly complicated. * Core classes like Plugin_Upgrader don't expose the plugin file name during an in-progress update (AFAICT). * This class uses a few workarounds and heuristics to get the file name. */ class Fileorganizer_PucUpgraderStatus_3_2 { private $upgradedPluginFile = null; //The plugin that is currently being upgraded by WordPress. public function __construct() { //Keep track of which plugin WordPress is currently upgrading. add_filter('upgrader_pre_install', array($this, 'setUpgradedPlugin'), 10, 2); add_filter('upgrader_package_options', array($this, 'setUpgradedPluginFromOptions'), 10, 1); add_filter('upgrader_post_install', array($this, 'clearUpgradedPlugin'), 10, 1); add_action('upgrader_process_complete', array($this, 'clearUpgradedPlugin'), 10, 1); } /** * Is there and update being installed RIGHT NOW, for a specific plugin? * * Caution: This method is unreliable. WordPress doesn't make it easy to figure out what it is upgrading, * and upgrader implementations are liable to change without notice. * * @param string $pluginFile The plugin to check. * @param WP_Upgrader|null $upgrader The upgrader that's performing the current update. * @return bool True if the plugin identified by $pluginFile is being upgraded. */ public function isPluginBeingUpgraded($pluginFile, $upgrader = null) { if ( isset($upgrader) ) { $upgradedPluginFile = $this->getPluginBeingUpgradedBy($upgrader); if ( !empty($upgradedPluginFile) ) { $this->upgradedPluginFile = $upgradedPluginFile; } } return ( !empty($this->upgradedPluginFile) && ($this->upgradedPluginFile === $pluginFile) ); } /** * Get the file name of the plugin that's currently being upgraded. * * @param Plugin_Upgrader|WP_Upgrader $upgrader * @return string|null */ private function getPluginBeingUpgradedBy($upgrader) { if ( !isset($upgrader, $upgrader->skin) ) { return null; } //Figure out which plugin is being upgraded. $pluginFile = null; $skin = $upgrader->skin; if ( $skin instanceof Plugin_Upgrader_Skin ) { if ( isset($skin->plugin) && is_string($skin->plugin) && ($skin->plugin !== '') ) { $pluginFile = $skin->plugin; } } elseif ( isset($skin->plugin_info) && is_array($skin->plugin_info) ) { //This case is tricky because Bulk_Plugin_Upgrader_Skin (etc) doesn't actually store the plugin //filename anywhere. Instead, it has the plugin headers in $plugin_info. So the best we can //do is compare those headers to the headers of installed plugins. $pluginFile = $this->identifyPluginByHeaders($skin->plugin_info); } return $pluginFile; } /** * Identify an installed plugin based on its headers. * * @param array $searchHeaders The plugin file header to look for. * @return string|null Plugin basename ("foo/bar.php"), or NULL if we can't identify the plugin. */ private function identifyPluginByHeaders($searchHeaders) { if ( !function_exists('get_plugins') ){ /** @noinspection PhpIncludeInspection */ require_once( ABSPATH . '/wp-admin/includes/plugin.php' ); } $installedPlugins = get_plugins(); $matches = array(); foreach($installedPlugins as $pluginBasename => $headers) { $diff1 = array_diff_assoc($headers, $searchHeaders); $diff2 = array_diff_assoc($searchHeaders, $headers); if ( empty($diff1) && empty($diff2) ) { $matches[] = $pluginBasename; } } //It's possible (though very unlikely) that there could be two plugins with identical //headers. In that case, we can't unambiguously identify the plugin that's being upgraded. if ( count($matches) !== 1 ) { return null; } return reset($matches); } /** * @access private * * @param mixed $input * @param array $hookExtra * @return mixed Returns $input unaltered. */ public function setUpgradedPlugin($input, $hookExtra) { if (!empty($hookExtra['plugin']) && is_string($hookExtra['plugin'])) { $this->upgradedPluginFile = $hookExtra['plugin']; } else { $this->upgradedPluginFile = null; } return $input; } /** * @access private * * @param array $options * @return array */ public function setUpgradedPluginFromOptions($options) { if (isset($options['hook_extra']['plugin']) && is_string($options['hook_extra']['plugin'])) { $this->upgradedPluginFile = $options['hook_extra']['plugin']; } else { $this->upgradedPluginFile = null; } return $options; } /** * @access private * * @param mixed $input * @return mixed Returns $input unaltered. */ public function clearUpgradedPlugin($input = null) { $this->upgradedPluginFile = null; return $input; } } endif; if ( !class_exists('Fileorganizer_PucFactory', false) ): /** * A factory that builds instances of other classes from this library. * * When multiple versions of the same class have been loaded (e.g. FileorganizerUpdateChecker 1.2 * and 1.3), this factory will always use the latest available version. Register class * versions by calling {@link Fileorganizer_PucFactory::addVersion()}. * * At the moment it can only build instances of the FileorganizerUpdateChecker class. Other classes * are intended mainly for internal use and refer directly to specific implementations. If you * want to instantiate one of them anyway, you can use {@link Fileorganizer_PucFactory::getLatestClassVersion()} * to get the class name and then create it with new $class(...). */ class Fileorganizer_PucFactory { protected static $classVersions = array(); protected static $sorted = false; /** * Create a new instance of FileorganizerUpdateChecker. * * @see FileorganizerUpdateChecker::__construct() * * @param $metadataUrl * @param $pluginFile * @param string $slug * @param int $checkPeriod * @param string $optionName * @param string $muPluginFile * @return FileorganizerUpdateChecker_3_2 */ public static function buildUpdateChecker($metadataUrl, $pluginFile, $slug = '', $checkPeriod = 12, $optionName = '', $muPluginFile = '') { $class = self::getLatestClassVersion('FileorganizerUpdateChecker'); return new $class($metadataUrl, $pluginFile, $slug, $checkPeriod, $optionName, $muPluginFile); } /** * Get the specific class name for the latest available version of a class. * * @param string $class * @return string|null */ public static function getLatestClassVersion($class) { if ( !self::$sorted ) { self::sortVersions(); } if ( isset(self::$classVersions[$class]) ) { return reset(self::$classVersions[$class]); } else { return null; } } /** * Sort available class versions in descending order (i.e. newest first). */ protected static function sortVersions() { foreach ( self::$classVersions as $class => $versions ) { uksort($versions, array(__CLASS__, 'compareVersions')); self::$classVersions[$class] = $versions; } self::$sorted = true; } protected static function compareVersions($a, $b) { return -version_compare($a, $b); } /** * Register a version of a class. * * @access private This method is only for internal use by the library. * * @param string $generalClass Class name without version numbers, e.g. 'FileorganizerUpdateChecker'. * @param string $versionedClass Actual class name, e.g. 'FileorganizerUpdateChecker_1_2'. * @param string $version Version number, e.g. '1.2'. */ public static function addVersion($generalClass, $versionedClass, $version) { if ( !isset(self::$classVersions[$generalClass]) ) { self::$classVersions[$generalClass] = array(); } self::$classVersions[$generalClass][$version] = $versionedClass; self::$sorted = false; } } endif; //Register classes defined in this file with the factory. Fileorganizer_PucFactory::addVersion('FileorganizerUpdateChecker', 'FileorganizerUpdateChecker_3_2', '3.2'); Fileorganizer_PucFactory::addVersion('FileorganizerUpdate', 'FileorganizerUpdate_3_2', '3.2'); Fileorganizer_PucFactory::addVersion('FileorganizerInfo', 'FileorganizerInfo_3_2', '3.2'); Fileorganizer_PucFactory::addVersion('Fileorganizer_PucGitHubChecker', 'Fileorganizer_PucGitHubChecker_3_2', '3.2'); main/role_restrictions.php000064400000026106147600405370011765 0ustar00

$restriction){ $operations_ = !empty($restriction['restrict_operations']) ? $restriction['restrict_operations'] : array(); $rm_pos = array_search('rm', $operations_); if(!empty($rm_pos)){ $operations_[$rm_pos] = 'Remove'; } $operations = !empty($operations_) && is_array($operations_) ? implode(', ',$operations_) : __('NA'); $icons = ['administrator'=>'dashicons-shield', 'editor'=>'dashicons-edit-page', 'author'=>'dashicons-edit-large', 'contributor'=>'dashicons-welcome-learn-more', 'customer'=>'dashicons-admin-users', 'subscriber'=>'dashicons-rss', 'shop_manager'=>'dashicons-store' ]; $usr_role = !empty($restriction['user_role']) ? $restriction ['user_role'] : __('NA.'); $private_path = !empty($restriction['private_dir']) ? $restriction['private_dir'] : ''; $hasInvalidPath = false; if(!empty($private_path) && empty($settings['disable_path_restriction']) && !fileorganizer_validate_path($private_path)){ $hasInvalidPath = true; } echo ''; } }else{ echo ''; } ?>
'.ucwords($usr_role).'
'.ucwords($operations).' '.( empty($restriction['private_dir']) ? __('NA') : $restriction['private_dir']).( $hasInvalidPath ? ' ' : '').' '.(empty($restriction['restrict_dirs']) ? __('NA') : $restriction['restrict_dirs']).' '.(empty($restriction['restrict_files']) ? __('NA') : $restriction['restrict_files']).' '.(empty($restriction['disable_toolbar']) ? __('No') : __('Yes') ).' '.(empty($restriction['disable_context_menu']) ? __('No') : __('Yes')).'
User Role Restrictions not found!

'.__( ucwords(($operation == 'rm' ? 'remove' : $operation)) ).'
'; } ?>


Note: It will override "File Manager Path" settings. '. __("File manager path restriction is enabled, access outside of your WordPress installation path is not permitted.").''; } ?>

Note: The extensions are case-sensitive and must be separated by vertical bar (|) without any spaces. Example: .jpg, JPG' ); ?>

options = empty($options) ? array() : $options; } fileorganizer_pro_update_checker(); // Load license fileorganizer_pro_load_license(); if(wp_doing_ajax()){ add_action('wp_ajax_fileorganizer_pro_version_notice', 'fileorganizer_pro_version_notice'); } if(current_user_can('activate_plugins')){ add_action('admin_notices', 'fileorganizer_pro_free_version_nag'); } // Check for updates include_once(FILEORGANIZER_PRO_DIR.'/main/plugin-update-checker.php'); $fileorganizer_updater = Fileorganizer_PucFactory::buildUpdateChecker(fileorganizer_pro_api_url().'/updates.php?version='.FILEORGANIZER_PRO_VERSION, FILEORGANIZER_PRO_FILE); // Add the license key to query arguments $fileorganizer_updater->addQueryArgFilter('fileorganizer_pro_updater_filter_args'); // Show the text to install the license key add_filter('puc_manual_final_check_link-fileorganizer-pro', 'fileorganizer_pro_updater_check_link', 10, 1); } // Ajax function function fileorganizer_pro_version_notice(){ check_admin_referer('fileorganizer_version_notice', 'security'); if(!current_user_can('activate_plugins')){ wp_send_json_error(__('You do not have required access to do this action', 'fileorganizer')); } $type = ''; if(!empty($_REQUEST['type'])){ $type = sanitize_text_field(wp_unslash($_REQUEST['type'])); } if(empty($type)){ wp_send_json_error(__('Unknow version difference type', 'fileorganizer')); } update_option('fileorganizer_version_'. $type .'_nag', time() + WEEK_IN_SECONDS); wp_send_json_success(); } function fileorganizer_pro_free_version_nag(){ if(!defined('FILEORGANIZER_VERSION')){ return; } $dismissed_free = (int) get_option('fileorganizer_version_free_nag'); $dismissed_pro = (int) get_option('fileorganizer_version_pro_nag'); // Checking if time has passed since the dismiss. if(!empty($dismissed_free) && time() < $dismissed_pro && !empty($dismissed_pro) && time() < $dismissed_pro){ return; } $showing_error = false; if(version_compare(FILEORGANIZER_VERSION, FILEORGANIZER_PRO_VERSION) > 0 && (empty($dismissed_pro) || time() > $dismissed_pro)){ $showing_error = true; echo '

'.esc_html__('You are using an older version of FileOrganizer Pro. We recommend updating to the latest version to ensure seamless and uninterrupted use of the application.', 'fileorganizer').'

'; }elseif(version_compare(FILEORGANIZER_VERSION, FILEORGANIZER_PRO_VERSION) < 0 && (empty($dismissed_free) || time() > $dismissed_free)){ $showing_error = true; echo '

'.esc_html__('You are using an older version of FileOrganizer. We recommend updating to the latest free version to ensure smooth and uninterrupted use of the application.', 'fileorganizer').'

'; } if(!empty($showing_error)){ wp_register_script('fileorganizer-pro-version-notice', '', array('jquery'), FILEORGANIZER_PRO_VERSION, true ); wp_enqueue_script('fileorganizer-pro-version-notice'); wp_add_inline_script('fileorganizer-pro-version-notice', ' function fileorganizer_pro_dismiss_notice(e){ e.preventDefault(); let target = jQuery(e.target); if(!target.hasClass("notice-dismiss")){ return; } let jEle = target.closest("#fileorganizer-pro-version-notice"), type = jEle.data("type"); jEle.slideUp(); jQuery.post("'.admin_url('admin-ajax.php').'", { security : "'.wp_create_nonce('fileorganizer_version_notice').'", action: "fileorganizer_pro_version_notice", type: type }, function(res){ if(!res["success"]){ alert(res["data"]); } }).fail(function(data){ alert("There seems to be some issue dismissing this alert"); }); }'); } } // Add hook for capabilites add_filter('fileorganizer_get_capability', 'fileorganizer_pro_get_capability'); function fileorganizer_pro_get_capability($capability){ global $fileorganizer; if(empty($fileorganizer->options['user_roles']) || !is_array($fileorganizer->options['user_roles'])){ return $capability; } $current_user = wp_get_current_user(); $roles = $current_user->roles; $is_allowed = array_intersect($roles, $fileorganizer->options['user_roles']); if(count($is_allowed) > 0){ $capability = 'read'; } return $capability; } add_filter('fileorganizer_manager_config', 'fileorganizer_pro_manager_config', 10 , 2); function fileorganizer_pro_manager_config($config){ global $fileorganizer; $data = []; $current_user = wp_get_current_user(); // Do restrict user? if(!empty($fileorganizer->options['user_restrictions']) && is_array($fileorganizer->options['user_restrictions'])){ foreach($fileorganizer->options['user_restrictions'] as $restrictions){ if(!empty($restrictions['user']) && $restrictions['user'] == $current_user->user_login){ $data = $restrictions; break; } } } // Do restrict user roles? if(!empty($fileorganizer->options['user_roles_restrictions']) && is_array($fileorganizer->options['user_roles_restrictions']) && empty($data)){ foreach($current_user->roles as $role){ foreach($fileorganizer->options['user_roles_restrictions'] as $restrictions){ if(!empty($restrictions['user_role']) && $restrictions['user_role'] == $role){ $data = $restrictions; break 2; } } } } $restrictions = []; // Hide directories? if(!empty($data['restrict_dirs'])) { $dirs = explode('|',$data['restrict_dirs']); foreach($dirs as $dir){ if(empty($dir)){ continue; } $restrictions[] = array( 'pattern' => '!^\/'.$dir.'$!', 'read' => false, 'write' => false, 'hidden' => true, 'locked' => false ); } } // Hide files? if(!empty($data['restrict_files'])) { $files = explode('|',$data['restrict_files']); foreach($files as $file){ if(empty($file)){ continue; } $restrictions[] = array( 'pattern' => '/'.$file.'$/', 'read' => false, 'write' => false, 'hidden' => false, 'locked' => true ); } } // Is set private dir? if(!empty($data['private_dir'])){ $path = $data['private_dir']; if(empty($fileorganizer->options['disable_path_restriction'])){ $path = fileorganizer_validate_path($path) ? $path : ABSPATH; } $config[0]['path'] = $path; } // Disable operations $disable_commands = array( 'help', 'preference', 'hide' ); $config[0]['disabled'] = $disable_commands; // restrict commands if(!empty($data['restrict_operations']) && is_array($data['restrict_operations'])){ $config[0]['disabled'] = array_merge($disable_commands, $data['restrict_operations']); } // Set max upload size if(!empty($fileorganizer->options['max_upload_size'])){ $config[0]['uploadMaxSize'] = $fileorganizer->options['max_upload_size'].'M'; } // Set restrictions $config[0]['attributes'] = array_merge($config[0]['attributes'], $restrictions); // Trash enabled? if(isset($config[1])){ $config[1]['attributes'] = $config[0]['attributes']; $config[1]['disabled'] = $config[0]['disabled']; } return $config; } add_filter('fileorganizer_elfinder_script', 'fileorganizer_pro_elfinder_script'); function fileorganizer_pro_elfinder_script($config){ global $fileorganizer; $data = array(); $current_user = wp_get_current_user(); // Do restrict user? if(!empty($fileorganizer->options['user_restrictions']) && is_array($fileorganizer->options['user_restrictions'])){ foreach($fileorganizer->options['user_restrictions'] as $restrictions){ if(!empty($restrictions['user']) && $restrictions['user'] == $current_user->user_login){ $data = $restrictions; break; } } } // Do restrict user roles? if(!empty($fileorganizer->options['user_roles_restrictions']) && is_array($fileorganizer->options['user_roles_restrictions']) && empty($data)){ foreach($current_user->roles as $role){ foreach($fileorganizer->options['user_roles_restrictions'] as $restrictions){ if(!empty($restrictions['user_role']) && $restrictions['user_role'] == $role){ $data = $restrictions; break 2; } } } } // To remove preference form contextmenu $context_menu_content = 'cwd: ["undo", "redo", "|", "back", "up", "reload", "|", "upload", "mkdir", "mkfile", "paste", "|", "empty", "hide", "|", "view", "sort", "selectall", "colwidth", "|", "places", "info", "chmod", "netunmount", "|", "fullscreen"]'; if(!empty($data['disable_context_menu'])){ $context_menu_content = 'files: [], navbar: [], cwd: []'; } $config = ' uiOptions : { toolbar: '.(!empty($data['disable_toolbar']) && $data['disable_toolbar'] == 'yes' ? '[]' :'{}').', toolbarExtra : { autoHideUA: [], displayTextLabel: false, preferenceInContextmenu: false, }, }, contextmenu: { '.$context_menu_content.' },'; return $config; } function fileorganizer_pro_deactivate(){ delete_option('fileorganizer_pro_version'); delete_option('fileorganizer_free_installed'); delete_option('fileorganizer_version_pro_nag'); delete_option('fileorganizer_version_free_nag'); }readme.txt000064400000012165147600405370006555 0ustar00=== FileOrganizer - Manage WordPress and Website Files === Contributors: softaculous Tags: FileOrganizer, file manager, wordpress file manager, file explorer, upload files, File Manage, Edit Files, Delete Files, elfinder, ftp, file transfer, update, create, delete, view, edit, rename, editor, explorer, softaculous, webuzo, Control Panel, Admin Requires at least: 5.5 Tested up to: 6.7 Requires PHP: 5.5 Stable tag: 1.1.4 License: LGPL v2.1 License URI: http://www.gnu.org/licenses/lgpl-2.1.html FileOrganizer is a plugin that helps you to manage all files in your WordPress Site. FileOrganizer provides you the ability to edit, delete, upload, download, copy and paste files and folders. == Description == FileOrganizer is a lightweight and easy-to-use file management plugin for WordPress. Organize and manage your WordPress files with FileOrganizer without any control panel or FTP access. You can access, upload, download, edit, delete, zip, cut, copy, and paste files even outside the WordPress root directory. ==Free Features== * **Files and directory Management:** Supports all file operations on a remote server such as create files or directory, upload, download, rename, copy, etc. * **Drag & drop:** Easy drag-and-drop file transfer for uploading and moving files. * **Built-in editor:** FileOrganizer has a built-in editor for editing code and files. * **Archive creation / extraction:** Create or extract archives with one click(.zip, .tar) * **File Previews**: Supports previews for common file types including PDFs, pictures, videos, audio, and thumbnails. * **Responsive**: FileOrganizer is compatible with tablets and smartphones. * **Search**: The built-in search feature makes it simple to find your files. * **File View**: Offers both icons and list views for easy navigation. * **Image Thumbnails**: Display thumbnails for image files. * **Support Shortcuts**: Common keyboard shortcuts such as cut, copy, paste, etc. are available. * **Toolbar**: Rich Toolbar and context menu are available. * **Directory Size**: Calculates size of the directory. * **File/Directory Info**: File or directory information can retrived by simply right-clicking a file and choosing Get Info. == Info == Do you have questions related to FileOrganizer ? Use the following links : 1. [Docs](https://fileorganizer.net/docs) 3. [Help Desk](https://fileorganizer.deskuss.com) 2. [Support Forum](http://wordpress.org/support/plugin/fileOrganizer) == Frequently Asked Questions == = How to install FileOrganizer = Go To your WordPress install -> Plugins -> Add New Button -> In Search Box, Look For FileOrganizer -> Click on Install. == Screenshots == 1. File Manager Screen 2. Edit File 3. Delete File 4. Make a folder 5. Download archived zip file 6. File archive as ZIP == Changelog == = 1.1.4 (Nov 18, 2024) = * [Task] Tested with WordPress 6.7. = 1.1.3 (Oct 22, 2024) = * [Task] Improved compatibility with Softaculous plugin. = 1.1.2 (Sep 20, 2024) = * [Task] Improved License handling. = 1.1.1 (Sep 11, 2024) = * [Task] Added notice for "Allowed User Roles" setting. = 1.1.0 (Sep 10, 2024) = * [Task] Added additional checks for file uploading. = 1.0.9 (July 19, 2024) = * [Task] Structural changes. * [Task] Tested with WordPress 6.6 = 1.0.8 (June 06, 2024) = * [Security-Fix] A Directory listing issue has been fixed. (Researcher Emad, WordFence) = 1.0.7 (Apr 11, 2024) = * [Security-Fix] An XSS vulnerability has been fixed. Please upgrade to this version as soon as possible! = 1.0.6 (Dec 15, 2023) = * [Security-fix] There was a privilege check failure which has been fixed. = 1.0.5 (Nov 29, 2023) = * [Task] Tested compatibility with WordPress 6.4. No functional changes made. The plugin is fully compatible with WordPress 6.4. = 1.0.4 (sep 19, 2023) = * [Improvement] FileOrganizer file manager is now only accessible for users with the 'Activate Plugins' role. And in the Pro version other roles can be granted file manager access by the Supper administrator. = 1.0.3 (sep 8, 2023) = * [Feature] FileOrganizer optimized for WordPress multi-site. Now you can use FileOrganizer for multi-site. * [Pro-Feature] Added settings to prevent FileOrganizer from setting paths outside of your WordPress installation. And the free version does not allow the user to set a path outside your WordPress installation. * [Improvement] The UI of all themes has been improved. * [Improvement] Added CodeMirror editor to improve your editing experience. = 1.0.2 (Apr 27, 2023) = * [Feature] Added theme option for file manager, now you can change theme of file manager. * [Feature] Added some settings to configure FileOrganizer file manager. * [Feature] Added option to enable Trash. Now you can Enable/Disable Trash feature for File Manager from Settings page. * [Pro-Feature] Added "Allowed user roles" option to allow selected users to access File Manager. * [Pro-Feature] Added options to restrict features and set limited access to FileOrganizer by users and user roles. = 1.0.1 = * [Bug-fix] In some cases, backslashes were added before quotes when editing a PHP file. This is fixed. = 1.0.0 = * First release