File manager - Edit - /home/matthif/www/wp-content/languages/dashboard.tar
Back
elFinderVolumeLocalFileSystem.class.php 0000666 00000136366 15226760543 0014316 0 ustar 00 <?php // Implement similar functionality in PHP 5.2 or 5.3 // http://php.net/manual/class.recursivecallbackfilteriterator.php#110974 if (!class_exists('RecursiveCallbackFilterIterator', false)) { class RecursiveCallbackFilterIterator extends RecursiveFilterIterator { private $callback; public function __construct(RecursiveIterator $iterator, $callback) { $this->callback = $callback; parent::__construct($iterator); } public function accept() { return call_user_func($this->callback, parent::current(), parent::key(), parent::getInnerIterator()); } public function getChildren() { return new self($this->getInnerIterator()->getChildren(), $this->callback); } } } /** * elFinder driver for local filesystem. * * @author Dmitry (dio) Levashov * @author Troex Nevelin **/ class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver { /** * Driver id * Must be started from letter and contains [a-z0-9] * Used as part of volume id * * @var string **/ protected $driverId = 'l'; /** * Required to count total archive files size * * @var int **/ protected $archiveSize = 0; /** * Is checking stat owner * * @var boolean */ protected $statOwner = false; /** * Path to quarantine directory * * @var string */ private $quarantine; /** * Constructor * Extend options with required fields * * @author Dmitry (dio) Levashov */ public function __construct() { $this->options['alias'] = ''; // alias to replace root dir name $this->options['dirMode'] = 0755; // new dirs mode $this->options['fileMode'] = 0644; // new files mode $this->options['rootCssClass'] = 'elfinder-navbar-root-local'; $this->options['followSymLinks'] = true; $this->options['detectDirIcon'] = ''; // file name that is detected as a folder icon e.g. '.diricon.png' $this->options['keepTimestamp'] = array('copy', 'move'); // keep timestamp at inner filesystem allowed 'copy', 'move' and 'upload' $this->options['substituteImg'] = true; // support substitute image with dim command $this->options['statCorrector'] = null; // callable to correct stat data `function(&$stat, $path, $statOwner, $volumeDriveInstance){}` if (DIRECTORY_SEPARATOR === '/') { // Linux $this->options['acceptedName'] = '/^[^\.\/\x00][^\/\x00]*$/'; } else { // Windows $this->options['acceptedName'] = '/^[^\.\/\x00\\\:*?"<>|][^\/\x00\\\:*?"<>|]*$/'; } } /*********************************************************************/ /* INIT AND CONFIGURE */ /*********************************************************************/ /** * Prepare driver before mount volume. * Return true if volume is ready. * * @return bool **/ protected function init() { // Normalize directory separator for windows if (DIRECTORY_SEPARATOR !== '/') { foreach (array('path', 'tmbPath', 'tmpPath', 'quarantine') as $key) { if (!empty($this->options[$key])) { $this->options[$key] = str_replace('/', DIRECTORY_SEPARATOR, $this->options[$key]); } } // PHP >= 7.1 Supports UTF-8 path on Windows if (version_compare(PHP_VERSION, '7.1', '>=')) { $this->options['encoding'] = ''; $this->options['locale'] = ''; } } if (!$cwd = getcwd()) { return $this->setError('elFinder LocalVolumeDriver requires a result of getcwd().'); } // detect systemRoot if (!isset($this->options['systemRoot'])) { if ($cwd[0] === DIRECTORY_SEPARATOR || $this->root[0] === DIRECTORY_SEPARATOR) { $this->systemRoot = DIRECTORY_SEPARATOR; } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $this->root, $m)) { $this->systemRoot = $m[1]; } else if (preg_match('/^([a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . ')/', $cwd, $m)) { $this->systemRoot = $m[1]; } } $this->root = $this->getFullPath($this->root, $cwd); if (!empty($this->options['startPath'])) { $this->options['startPath'] = $this->getFullPath($this->options['startPath'], $this->root); } if (is_null($this->options['syncChkAsTs'])) { $this->options['syncChkAsTs'] = true; } if (is_null($this->options['syncCheckFunc'])) { $this->options['syncCheckFunc'] = array($this, 'localFileSystemInotify'); } // check 'statCorrector' if (empty($this->options['statCorrector']) || !is_callable($this->options['statCorrector'])) { $this->options['statCorrector'] = null; } return true; } /** * Configure after successfull mount. * * @return void * @throws elFinderAbortException * @author Dmitry (dio) Levashov */ protected function configure() { $hiddens = array(); $root = $this->stat($this->root); // check thumbnails path if (!empty($this->options['tmbPath'])) { if (strpos($this->options['tmbPath'], DIRECTORY_SEPARATOR) === false) { $hiddens['tmb'] = $this->options['tmbPath']; $this->options['tmbPath'] = $this->_abspath($this->options['tmbPath']); } else { $this->options['tmbPath'] = $this->_normpath($this->options['tmbPath']); } } // check temp path if (!empty($this->options['tmpPath'])) { if (strpos($this->options['tmpPath'], DIRECTORY_SEPARATOR) === false) { $hiddens['temp'] = $this->options['tmpPath']; $this->options['tmpPath'] = $this->_abspath($this->options['tmpPath']); } else { $this->options['tmpPath'] = $this->_normpath($this->options['tmpPath']); } } // check quarantine path $_quarantine = ''; if (!empty($this->options['quarantine'])) { if (strpos($this->options['quarantine'], DIRECTORY_SEPARATOR) === false) { $_quarantine = $this->_abspath($this->options['quarantine']); $this->options['quarantine'] = ''; } else { $this->options['quarantine'] = $this->_normpath($this->options['quarantine']); } } else { $_quarantine = $this->_abspath('.quarantine'); } is_dir($_quarantine) && self::localRmdirRecursive($_quarantine); parent::configure(); // check tmbPath if (!$this->tmbPath && isset($hiddens['tmb'])) { unset($hiddens['tmb']); } // if no thumbnails url - try detect it if ($root['read'] && !$this->tmbURL && $this->URL) { if (strpos($this->tmbPath, $this->root) === 0) { $this->tmbURL = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($this->tmbPath, strlen($this->root) + 1)); if (preg_match("|[^/?&=]$|", $this->tmbURL)) { $this->tmbURL .= '/'; } } } // set $this->tmp by options['tmpPath'] $this->tmp = ''; if (!empty($this->options['tmpPath'])) { if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'], $this->options['dirMode'], true)) && is_writable($this->options['tmpPath'])) { $this->tmp = $this->options['tmpPath']; } else { if (isset($hiddens['temp'])) { unset($hiddens['temp']); } } } if (!$this->tmp && ($tmp = elFinder::getStaticVar('commonTempPath'))) { $this->tmp = $tmp; } // check quarantine dir $this->quarantine = ''; if (!empty($this->options['quarantine'])) { if ((is_dir($this->options['quarantine']) || mkdir($this->options['quarantine'], $this->options['dirMode'], true)) && is_writable($this->options['quarantine'])) { $this->quarantine = $this->options['quarantine']; } else { if (isset($hiddens['quarantine'])) { unset($hiddens['quarantine']); } } } else if ($_path = elFinder::getCommonTempPath()) { $this->quarantine = $_path; } if (!$this->quarantine) { if (!$this->tmp) { $this->archivers['extract'] = array(); $this->disabled[] = 'extract'; } else { $this->quarantine = $this->tmp; } } if ($hiddens) { foreach ($hiddens as $hidden) { $this->attributes[] = array( 'pattern' => '~^' . preg_quote(DIRECTORY_SEPARATOR . $hidden, '~') . '$~', 'read' => false, 'write' => false, 'locked' => true, 'hidden' => true ); } } if (!empty($this->options['keepTimestamp'])) { $this->options['keepTimestamp'] = array_flip($this->options['keepTimestamp']); } $this->statOwner = (!empty($this->options['statOwner'])); // enable WinRemoveTailDots plugin on Windows server if (DIRECTORY_SEPARATOR !== '/') { if (!isset($this->options['plugin'])) { $this->options['plugin'] = array(); } $this->options['plugin']['WinRemoveTailDots'] = array('enable' => true); } } /** * Long pooling sync checker * This function require server command `inotifywait` * If `inotifywait` need full path, Please add `define('ELFINER_INOTIFYWAIT_PATH', '/PATH_TO/inotifywait');` into connector.php * * @param string $path * @param int $standby * @param number $compare * * @return number|bool * @throws elFinderAbortException */ public function localFileSystemInotify($path, $standby, $compare) { if (isset($this->sessionCache['localFileSystemInotify_disable'])) { return false; } $path = realpath($path); $mtime = filemtime($path); if (!$mtime) { return false; } if ($mtime != $compare) { return $mtime; } $inotifywait = defined('ELFINER_INOTIFYWAIT_PATH') ? ELFINER_INOTIFYWAIT_PATH : 'inotifywait'; $standby = max(1, intval($standby)); $cmd = $inotifywait . ' ' . escapeshellarg($path) . ' -t ' . $standby . ' -e moved_to,moved_from,move,close_write,delete,delete_self'; $this->procExec($cmd, $o, $r); if ($r === 0) { // changed clearstatcache(); if (file_exists($path)) { $mtime = filemtime($path); // error on busy? return $mtime ? $mtime : time(); } else { // target was removed return 0; } } else if ($r === 2) { // not changed (timeout) return $compare; } // error // cache to $_SESSION $this->sessionCache['localFileSystemInotify_disable'] = true; $this->session->set($this->id, $this->sessionCache); return false; } /*********************************************************************/ /* FS API */ /*********************************************************************/ /*********************** paths/urls *************************/ /** * Return parent directory path * * @param string $path file path * * @return string * @author Dmitry (dio) Levashov **/ protected function _dirname($path) { return dirname($path); } /** * Return file name * * @param string $path file path * * @return string * @author Dmitry (dio) Levashov **/ protected function _basename($path) { return basename($path); } /** * Join dir name and file name and retur full path * * @param string $dir * @param string $name * * @return string * @author Dmitry (dio) Levashov **/ protected function _joinPath($dir, $name) { $dir = rtrim($dir, DIRECTORY_SEPARATOR); $path = realpath($dir . DIRECTORY_SEPARATOR . $name); // realpath() returns FALSE if the file does not exist if ($path === false || strpos($path, $this->root) !== 0) { if (DIRECTORY_SEPARATOR !== '/') { $dir = str_replace('/', DIRECTORY_SEPARATOR, $dir); $name = str_replace('/', DIRECTORY_SEPARATOR, $name); } // Directory traversal measures if (strpos($dir, '..' . DIRECTORY_SEPARATOR) !== false || substr($dir, -2) == '..') { $dir = $this->root; } if (strpos($name, '..' . DIRECTORY_SEPARATOR) !== false) { $name = basename($name); } $path = $dir . DIRECTORY_SEPARATOR . $name; } return $path; } /** * Return normalized path, this works the same as os.path.normpath() in Python * * @param string $path path * * @return string * @author Troex Nevelin **/ protected function _normpath($path) { if (empty($path)) { return '.'; } $changeSep = (DIRECTORY_SEPARATOR !== '/'); if ($changeSep) { $drive = ''; if (preg_match('/^([a-zA-Z]:)(.*)/', $path, $m)) { $drive = $m[1]; $path = $m[2] ? $m[2] : '/'; } $path = str_replace(DIRECTORY_SEPARATOR, '/', $path); } if (strpos($path, '/') === 0) { $initial_slashes = true; } else { $initial_slashes = false; } if (($initial_slashes) && (strpos($path, '//') === 0) && (strpos($path, '///') === false)) { $initial_slashes = 2; } $initial_slashes = (int)$initial_slashes; $comps = explode('/', $path); $new_comps = array(); foreach ($comps as $comp) { if (in_array($comp, array('', '.'))) { continue; } if (($comp != '..') || (!$initial_slashes && !$new_comps) || ($new_comps && (end($new_comps) == '..'))) { array_push($new_comps, $comp); } elseif ($new_comps) { array_pop($new_comps); } } $comps = $new_comps; $path = implode('/', $comps); if ($initial_slashes) { $path = str_repeat('/', $initial_slashes) . $path; } if ($changeSep) { $path = $drive . str_replace('/', DIRECTORY_SEPARATOR, $path); } return $path ? $path : '.'; } /** * Return file path related to root dir * * @param string $path file path * * @return string * @author Dmitry (dio) Levashov **/ protected function _relpath($path) { if ($path === $this->root) { return ''; } else { if (strpos($path, $this->root) === 0) { return ltrim(substr($path, strlen($this->root)), DIRECTORY_SEPARATOR); } else { // for link return $path; } } } /** * Convert path related to root dir into real path * * @param string $path file path * * @return string * @author Dmitry (dio) Levashov **/ protected function _abspath($path) { if ($path === DIRECTORY_SEPARATOR) { return $this->root; } else { $path = $this->_normpath($path); if (strpos($path, $this->systemRoot) === 0) { return $path; } else if (DIRECTORY_SEPARATOR !== '/' && preg_match('/^[a-zA-Z]:' . preg_quote(DIRECTORY_SEPARATOR, '/') . '/', $path)) { return $path; } else { return $this->_joinPath($this->root, $path); } } } /** * Return fake path started from root dir * * @param string $path file path * * @return string * @author Dmitry (dio) Levashov **/ protected function _path($path) { return $this->rootName . ($path == $this->root ? '' : $this->separator . $this->_relpath($path)); } /** * Return true if $path is children of $parent * * @param string $path path to check * @param string $parent parent path * * @return bool * @author Dmitry (dio) Levashov **/ protected function _inpath($path, $parent) { $cwd = getcwd(); $real_path = $this->getFullPath($path, $cwd); $real_parent = $this->getFullPath($parent, $cwd); if ($real_path && $real_parent) { return $real_path === $real_parent || strpos($real_path, rtrim($real_parent, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR) === 0; } return false; } /***************** file stat ********************/ /** * Return stat for given path. * Stat contains following fields: * - (int) size file size in b. required * - (int) ts file modification time in unix time. required * - (string) mime mimetype. required for folders, others - optionally * - (bool) read read permissions. required * - (bool) write write permissions. required * - (bool) locked is object locked. optionally * - (bool) hidden is object hidden. optionally * - (string) alias for symlinks - link target path relative to root path. optionally * - (string) target for symlinks - link target path. optionally * If file does not exists - returns empty array or false. * * @param string $path file path * * @return array|false * @author Dmitry (dio) Levashov **/ protected function _stat($path) { $stat = array(); if (!file_exists($path) && !is_link($path)) { return $stat; } //Verifies the given path is the root or is inside the root. Prevents directory traveral. if (!$this->_inpath($path, $this->root)) { return $stat; } $stat['isowner'] = false; $linkreadable = false; if ($path != $this->root && is_link($path)) { if (!$this->options['followSymLinks']) { return array(); } if (!($target = $this->readlink($path)) || $target == $path) { if (is_null($target)) { $stat = array(); return $stat; } else { $stat['mime'] = 'symlink-broken'; $target = readlink($path); $lstat = lstat($path); $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']); $linkreadable = !empty($ostat['isowner']); } } $stat['alias'] = $this->_path($target); $stat['target'] = $target; } $readable = is_readable($path); if ($readable) { $size = sprintf('%u', filesize($path)); $stat['ts'] = filemtime($path); if ($this->statOwner) { $fstat = stat($path); $uid = $fstat['uid']; $gid = $fstat['gid']; $stat['perm'] = substr((string)decoct($fstat['mode']), -4); $stat = array_merge($stat, $this->getOwnerStat($uid, $gid)); } } if (($dir = is_dir($path)) && $this->options['detectDirIcon']) { $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon']; if ($this->URL && file_exists($favicon)) { $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1)); } } if (!isset($stat['mime'])) { $stat['mime'] = $dir ? 'directory' : $this->mimetype($path); } //logical rights first $stat['read'] = ($linkreadable || $readable) ? null : false; $stat['write'] = is_writable($path) ? null : false; if (is_null($stat['read'])) { if ($dir) { $stat['size'] = 0; } else if (isset($size)) { $stat['size'] = $size; } } if ($this->options['statCorrector']) { call_user_func_array($this->options['statCorrector'], array(&$stat, $path, $this->statOwner, $this)); } return $stat; } /** * Get stat `owner`, `group` and `isowner` by `uid` and `gid` * Sub-fuction of _stat() and _scandir() * * @param integer $uid * @param integer $gid * * @return array stat */ protected function getOwnerStat($uid, $gid) { static $names = null; static $phpuid = null; if (is_null($names)) { $names = array('uid' => array(), 'gid' => array()); } if (is_null($phpuid)) { if (is_callable('posix_getuid')) { $phpuid = posix_getuid(); } else { $phpuid = 0; } } $stat = array(); if ($uid) { $stat['isowner'] = ($phpuid == $uid); if (isset($names['uid'][$uid])) { $stat['owner'] = $names['uid'][$uid]; } else if (is_callable('posix_getpwuid')) { $pwuid = posix_getpwuid($uid); $stat['owner'] = $names['uid'][$uid] = $pwuid['name']; } else { $stat['owner'] = $names['uid'][$uid] = $uid; } } if ($gid) { if (isset($names['gid'][$gid])) { $stat['group'] = $names['gid'][$gid]; } else if (is_callable('posix_getgrgid')) { $grgid = posix_getgrgid($gid); $stat['group'] = $names['gid'][$gid] = $grgid['name']; } else { $stat['group'] = $names['gid'][$gid] = $gid; } } return $stat; } /** * Return true if path is dir and has at least one childs directory * * @param string $path dir path * * @return bool * @author Dmitry (dio) Levashov **/ protected function _subdirs($path) { $dirs = false; if (is_dir($path) && is_readable($path)) { if (class_exists('FilesystemIterator', false)) { $dirItr = new ParentIterator( new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS | FilesystemIterator::CURRENT_AS_SELF | (defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') ? RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0) ) ); $dirItr->rewind(); if ($dirItr->hasChildren()) { $dirs = true; $name = $dirItr->getSubPathName(); while ($dirItr->valid()) { if (!$this->attr($path . DIRECTORY_SEPARATOR . $name, 'read', null, true)) { $dirs = false; $dirItr->next(); $name = $dirItr->getSubPathName(); continue; } $dirs = true; break; } } } else { $path = strtr($path, array('[' => '\\[', ']' => '\\]', '*' => '\\*', '?' => '\\?')); return (bool)glob(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR); } } return $dirs; } /** * Return object width and height * Usualy used for images, but can be realize for video etc... * * @param string $path file path * @param string $mime file mime type * * @return string * @author Dmitry (dio) Levashov **/ protected function _dimensions($path, $mime) { clearstatcache(); return strpos($mime, 'image') === 0 && is_readable($path) && filesize($path) && ($s = getimagesize($path)) !== false ? $s[0] . 'x' . $s[1] : false; } /******************** file/dir content *********************/ /** * Return symlink target file * * @param string $path link path * * @return string * @author Dmitry (dio) Levashov **/ protected function readlink($path) { if (!($target = readlink($path))) { return null; } if (strpos($target, $this->systemRoot) !== 0) { $target = $this->_joinPath(dirname($path), $target); } if (!file_exists($target)) { return false; } return $target; } /** * Return files list in directory. * * @param string $path dir path * * @return array * @throws elFinderAbortException * @author Dmitry (dio) Levashov */ protected function _scandir($path) { elFinder::checkAborted(); $files = array(); $cache = array(); $dirWritable = is_writable($path); $dirItr = array(); $followSymLinks = $this->options['followSymLinks']; try { $dirItr = new DirectoryIterator($path); } catch (UnexpectedValueException $e) { } foreach ($dirItr as $file) { try { if ($file->isDot()) { continue; } $files[] = $fpath = $file->getPathname(); $br = false; $stat = array(); $stat['isowner'] = false; $linkreadable = false; if ($file->isLink()) { if (!$followSymLinks) { continue; } if (!($target = $this->readlink($fpath)) || $target == $fpath) { if (is_null($target)) { $stat = array(); $br = true; } else { $_path = $fpath; $stat['mime'] = 'symlink-broken'; $target = readlink($_path); $lstat = lstat($_path); $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']); $linkreadable = !empty($ostat['isowner']); $dir = false; $stat['alias'] = $this->_path($target); $stat['target'] = $target; } } else { $dir = is_dir($target); $stat['alias'] = $this->_path($target); $stat['target'] = $target; $stat['mime'] = $dir ? 'directory' : $this->mimetype($stat['alias']); } } else { if (($dir = $file->isDir()) && $this->options['detectDirIcon']) { $path = $file->getPathname(); $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon']; if ($this->URL && file_exists($favicon)) { $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1)); } } $stat['mime'] = $dir ? 'directory' : $this->mimetype($fpath); } $size = sprintf('%u', $file->getSize()); $stat['ts'] = $file->getMTime(); if (!$br) { if ($this->statOwner && !$linkreadable) { $uid = $file->getOwner(); $gid = $file->getGroup(); $stat['perm'] = substr((string)decoct($file->getPerms()), -4); $stat = array_merge($stat, $this->getOwnerStat($uid, $gid)); } //logical rights first $stat['read'] = ($linkreadable || $file->isReadable()) ? null : false; $stat['write'] = $file->isWritable() ? null : false; $stat['locked'] = $dirWritable ? null : true; if (is_null($stat['read'])) { $stat['size'] = $dir ? 0 : $size; } if ($this->options['statCorrector']) { call_user_func_array($this->options['statCorrector'], array(&$stat, $fpath, $this->statOwner, $this)); } } $cache[] = array($fpath, $stat); } catch (RuntimeException $e) { continue; } } if ($cache) { $cache = $this->convEncOut($cache, false); foreach ($cache as $d) { $this->updateCache($d[0], $d[1]); } } return $files; } /** * Open file and return file pointer * * @param string $path file path * @param string $mode * * @return false|resource * @internal param bool $write open file for writing * @author Dmitry (dio) Levashov */ protected function _fopen($path, $mode = 'rb') { return fopen($path, $mode); } /** * Close opened file * * @param resource $fp file pointer * @param string $path * * @return bool * @author Dmitry (dio) Levashov */ protected function _fclose($fp, $path = '') { return (is_resource($fp) && fclose($fp)); } /******************** file/dir manipulations *************************/ /** * Create dir and return created dir path or false on failed * * @param string $path parent dir path * @param string $name new directory name * * @return string|bool * @author Dmitry (dio) Levashov **/ protected function _mkdir($path, $name) { $path = $this->_joinPath($path, $name); if (mkdir($path)) { chmod($path, $this->options['dirMode']); return $path; } return false; } /** * Create file and return it's path or false on failed * * @param string $path parent dir path * @param string $name new file name * * @return string|bool * @author Dmitry (dio) Levashov **/ protected function _mkfile($path, $name) { $path = $this->_joinPath($path, $name); if (($fp = fopen($path, 'w'))) { fclose($fp); chmod($path, $this->options['fileMode']); return $path; } return false; } /** * Create symlink * * @param string $source file to link to * @param string $targetDir folder to create link in * @param string $name symlink name * * @return bool * @author Dmitry (dio) Levashov **/ protected function _symlink($source, $targetDir, $name) { return $this->localFileSystemSymlink($source, $this->_joinPath($targetDir, $name)); } /** * Copy file into another file * * @param string $source source file path * @param string $targetDir target directory path * @param string $name new file name * * @return bool * @author Dmitry (dio) Levashov **/ protected function _copy($source, $targetDir, $name) { $mtime = filemtime($source); $target = $this->_joinPath($targetDir, $name); if ($ret = copy($source, $target)) { isset($this->options['keepTimestamp']['copy']) && $mtime && touch($target, $mtime); } return $ret; } /** * Move file into another parent dir. * Return new file path or false. * * @param string $source source file path * @param $targetDir * @param string $name file name * * @return bool|string * @internal param string $target target dir path * @author Dmitry (dio) Levashov */ protected function _move($source, $targetDir, $name) { $mtime = filemtime($source); $target = $this->_joinPath($targetDir, $name); if ($ret = rename($source, $target) ? $target : false) { isset($this->options['keepTimestamp']['move']) && $mtime && touch($target, $mtime); } return $ret; } /** * Remove file * * @param string $path file path * * @return bool * @author Dmitry (dio) Levashov **/ protected function _unlink($path) { return is_file($path) && unlink($path); } /** * Remove dir * * @param string $path dir path * * @return bool * @author Dmitry (dio) Levashov **/ protected function _rmdir($path) { return rmdir($path); } /** * Create new file and write into it from file pointer. * Return new file path or false on error. * * @param resource $fp file pointer * @param string $dir target dir path * @param string $name file name * @param array $stat file stat (required by some virtual fs) * * @return bool|string * @author Dmitry (dio) Levashov **/ protected function _save($fp, $dir, $name, $stat) { $path = $this->_joinPath($dir, $name); $meta = stream_get_meta_data($fp); $uri = isset($meta['uri']) ? $meta['uri'] : ''; if ($uri && !preg_match('#^[a-zA-Z0-9]+://#', $uri) && !is_link($uri)) { fclose($fp); $mtime = filemtime($uri); $isCmdPaste = ($this->ARGS['cmd'] === 'paste'); $isCmdCopy = ($isCmdPaste && empty($this->ARGS['cut'])); if (($isCmdCopy || !rename($uri, $path)) && !copy($uri, $path)) { return false; } // keep timestamp on upload if ($mtime && $this->ARGS['cmd'] === 'upload') { touch($path, isset($this->options['keepTimestamp']['upload']) ? $mtime : time()); } } else { if (file_put_contents($path, $fp, LOCK_EX) === false) { return false; } } chmod($path, $this->options['fileMode']); return $path; } /** * Get file contents * * @param string $path file path * * @return string|false * @author Dmitry (dio) Levashov **/ protected function _getContents($path) { return file_get_contents($path); } /** * Write a string to a file * * @param string $path file path * @param string $content new file content * * @return bool * @author Dmitry (dio) Levashov **/ protected function _filePutContents($path, $content) { return (file_put_contents($path, $content, LOCK_EX) !== false); } /** * Detect available archivers * * @return void * @throws elFinderAbortException */ protected function _checkArchivers() { $this->archivers = $this->getArchivers(); return; } /** * chmod availability * * @param string $path * @param string $mode * * @return bool */ protected function _chmod($path, $mode) { $modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf("%04o", $mode)); return chmod($path, $modeOct); } /** * Recursive symlinks search * * @param string $path file/dir path * * @return bool * @throws Exception * @author Dmitry (dio) Levashov */ protected function _findSymlinks($path) { return self::localFindSymlinks($path); } /** * Extract files from archive * * @param string $path archive path * @param array $arc archiver command and arguments (same as in $this->archivers) * * @return array|string|boolean * @throws elFinderAbortException * @author Dmitry (dio) Levashov, * @author Alexey Sukhotin */ protected function _extract($path, $arc) { if ($this->quarantine) { $dir = $this->quarantine . DIRECTORY_SEPARATOR . md5(basename($path) . mt_rand()); $archive = (isset($arc['toSpec']) || $arc['cmd'] === 'phpfunction') ? '' : $dir . DIRECTORY_SEPARATOR . basename($path); if (!mkdir($dir)) { return false; } // insurance unexpected shutdown register_shutdown_function(array($this, 'rmdirRecursive'), realpath($dir)); chmod($dir, 0777); // copy in quarantine if (!is_readable($path) || ($archive && !copy($path, $archive))) { return false; } // extract in quarantine try { $this->unpackArchive($path, $arc, $archive ? true : $dir); } catch(Exception $e) { return $this->setError($e->getMessage()); } // get files list try { $ls = self::localScandir($dir); } catch (Exception $e) { return false; } // no files - extract error ? if (empty($ls)) { return false; } $this->archiveSize = 0; // find symlinks and check extracted items $checkRes = $this->checkExtractItems($dir); if ($checkRes['symlinks']) { self::localRmdirRecursive($dir); return $this->setError(array_merge($this->error, array(elFinder::ERROR_ARC_SYMLINKS))); } $this->archiveSize = $checkRes['totalSize']; if ($checkRes['rmNames']) { foreach ($checkRes['rmNames'] as $name) { $this->addError(elFinder::ERROR_SAVE, $name); } } // check max files size if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) { $this->delTree($dir); return $this->setError(elFinder::ERROR_ARC_MAXSIZE); } $extractTo = $this->extractToNewdir; // 'auto', ture or false // archive contains one item - extract in archive dir $name = ''; $src = $dir . DIRECTORY_SEPARATOR . $ls[0]; if (($extractTo === 'auto' || !$extractTo) && count($ls) === 1 && is_file($src)) { $name = $ls[0]; } else if ($extractTo === 'auto' || $extractTo) { // for several files - create new directory // create unique name for directory $src = $dir; $splits = elFinder::splitFileExtention(basename($path)); $name = $splits[0]; $test = dirname($path) . DIRECTORY_SEPARATOR . $name; if (file_exists($test) || is_link($test)) { $name = $this->uniqueName(dirname($path), $name, '-', false); } } if ($name !== '') { $result = dirname($path) . DIRECTORY_SEPARATOR . $name; if (!rename($src, $result)) { $this->delTree($dir); return false; } } else { $dstDir = dirname($path); $result = array(); foreach ($ls as $name) { $target = $dstDir . DIRECTORY_SEPARATOR . $name; if (self::localMoveRecursive($dir . DIRECTORY_SEPARATOR . $name, $target, true, $this->options['copyJoin'])) { $result[] = $target; } } if (!$result) { $this->delTree($dir); return false; } } is_dir($dir) && $this->delTree($dir); return (is_array($result) || file_exists($result)) ? $result : false; } //TODO: Add return statement here return false; } /** * Create archive and return its path * * @param string $dir target dir * @param array $files files names list * @param string $name archive name * @param array $arc archiver options * * @return string|bool * @throws elFinderAbortException * @author Dmitry (dio) Levashov, * @author Alexey Sukhotin */ protected function _archive($dir, $files, $name, $arc) { return $this->makeArchive($dir, $files, $name, $arc); } /******************** Over write functions *************************/ /** * File path of local server side work file path * * @param string $path * * @return string * @author Naoki Sawada */ protected function getWorkFile($path) { return $path; } /** * Delete dirctory trees * * @param string $localpath path need convert encoding to server encoding * * @return boolean * @throws elFinderAbortException * @author Naoki Sawada */ protected function delTree($localpath) { return $this->rmdirRecursive($localpath); } /** * Return fileinfo based on filename * For item ID based path file system * Please override if needed on each drivers * * @param string $path file cache * * @return array|boolean false */ protected function isNameExists($path) { $exists = file_exists($this->convEncIn($path)); // restore locale $this->convEncOut(); return $exists ? $this->stat($path) : false; } /******************** Over write (Optimized) functions *************************/ /** * Recursive files search * * @param string $path dir path * @param string $q search string * @param array $mimes * * @return array * @throws elFinderAbortException * @author Dmitry (dio) Levashov * @author Naoki Sawada */ protected function doSearch($path, $q, $mimes) { if (!empty($this->doSearchCurrentQuery['matchMethod']) || $this->encoding || !class_exists('FilesystemIterator', false)) { // has custom match method or non UTF-8, use elFinderVolumeDriver::doSearch() return parent::doSearch($path, $q, $mimes); } $result = array(); $timeout = $this->options['searchTimeout'] ? $this->searchStart + $this->options['searchTimeout'] : 0; if ($timeout && $timeout < time()) { $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path))); return $result; } elFinder::extendTimeLimit($this->options['searchTimeout'] + 30); $match = array(); try { $iterator = new RecursiveIteratorIterator( new RecursiveCallbackFilterIterator( new RecursiveDirectoryIterator($path, FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::SKIP_DOTS | ((defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') && $this->options['followSymLinks']) ? RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0) ), array($this, 'localFileSystemSearchIteratorFilter') ), RecursiveIteratorIterator::SELF_FIRST, RecursiveIteratorIterator::CATCH_GET_CHILD ); foreach ($iterator as $key => $node) { if ($timeout && ($this->error || $timeout < time())) { !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($node->getPath))); break; } if ($node->isDir()) { if ($this->stripos($node->getFilename(), $q) !== false) { $match[] = $key; } } else { $match[] = $key; } } } catch (Exception $e) { } if ($match) { foreach ($match as $p) { if ($timeout && ($this->error || $timeout < time())) { !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode(dirname($p)))); break; } $stat = $this->stat($p); if (!$stat) { // invalid links continue; } if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) { continue; } if ((!$mimes || $stat['mime'] !== 'directory')) { $stat['path'] = $this->path($stat['hash']); if ($this->URL && !isset($stat['url'])) { $_path = str_replace(DIRECTORY_SEPARATOR, '/', substr($p, strlen($this->root) + 1)); $stat['url'] = $this->URL . str_replace('%2F', '/', rawurlencode($_path)); } $result[] = $stat; } } } return $result; } /******************** Original local functions ************************ * * @param $file * @param $key * @param $iterator * * @return bool */ public function localFileSystemSearchIteratorFilter($file, $key, $iterator) { /* @var FilesystemIterator $file */ /* @var RecursiveDirectoryIterator $iterator */ $name = $file->getFilename(); if ($this->doSearchCurrentQuery['excludes']) { foreach ($this->doSearchCurrentQuery['excludes'] as $exclude) { if ($this->stripos($name, $exclude) !== false) { return false; } } } if ($iterator->hasChildren()) { if ($this->options['searchExDirReg'] && preg_match($this->options['searchExDirReg'], $key)) { return false; } return (bool)$this->attr($key, 'read', null, true); } return ($this->stripos($name, $this->doSearchCurrentQuery['q']) === false) ? false : true; } /** * Creates a symbolic link * * @param string $target The target * @param string $link The link * * @return boolean ( result of symlink() ) */ protected function localFileSystemSymlink($target, $link) { $res = false; if (function_exists('symlink') and is_callable('symlink')) { $errlev = error_reporting(); error_reporting($errlev ^ E_WARNING); if ($res = symlink(realpath($target), $link)) { $res = is_readable($link); } error_reporting($errlev); } return $res; } } // END class changelog_handler.php 0000666 00000011211 15226760543 0010713 0 ustar 00 <?php /** * Changleog Handler * * Handles parsing for Changelog files. * * @package Neve\Admin\Dashboard */ namespace Neve\Admin\Dashboard; /** * Class Changelog_Handler * * @package Neve\Admin\Dashboard */ class Changelog_Handler { /** * Get the parsed changelog. * * @param string $changelog_path the changelog path. * * @return array */ public function get_changelog( $changelog_path ) { if ( ! is_file( $changelog_path ) ) { return []; } if ( ! WP_Filesystem() ) { return []; } return $this->parse_changelog( $changelog_path ); } /** * Parse the changelog file. * * @param string $changelog_path the changelog path. * * @return array */ private function parse_changelog( $changelog_path ) { WP_Filesystem(); global $wp_filesystem; $changelog = $wp_filesystem->get_contents( $changelog_path ); if ( is_wp_error( $changelog ) ) { $changelog = ''; } $changelog = explode( PHP_EOL, $changelog ); $releases = []; $release_count = 0; $current_section = ''; // Holds the current section ('Improvements', 'Bug Fixes', etc.) foreach ( $changelog as $changelog_line ) { if ( strpos( $changelog_line, '**Changes:**' ) !== false || empty( $changelog_line ) ) { continue; } if ( substr( ltrim( $changelog_line ), 0, 3 ) === '###' && ! preg_match( '/###\s?(New Features|Bug Fixes)/', $changelog_line ) ) { // Extract version and date preg_match( '/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/', $changelog_line, $found_v ); preg_match( '/[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}/', $changelog_line, $found_d ); if ( isset( $found_v[0] ) && isset( $found_d[0] ) ) { $release_count++; $releases[ $release_count ] = array( 'version' => $found_v[0], 'date' => $found_d[0], ); } $current_section = ''; continue; } // Check for the new headers 'New Features', 'Bug Fixes', etc. if ( preg_match( '/###\s?(New Features|Bug Fixes)/', $changelog_line, $section_matches ) ) { $current_section = strtolower( $section_matches[1] ); continue; } // Extracting items based on the new changelog structure if ( $current_section === 'bug fixes' ) { $changelog_line = preg_replace( '/-\s?\*\*(.*?):\*\*/', '', $changelog_line ); $changelog_line = $this->parse_md_and_clean( $changelog_line ); $releases[ $release_count ]['fixes'][] = $changelog_line; continue; } if ( $current_section === 'new features' ) { $changelog_line = preg_replace( '/-\s?\*\*(.*?):\*\*/', '', $changelog_line ); $changelog_line = $this->parse_md_and_clean( $changelog_line ); $releases[ $release_count ]['features'][] = $changelog_line; continue; } // Legacy structure handling for feats and fixes if ( preg_match( '/[*|-]?\s?(\[fix]|\[Fix]|fix|Fix)[:]?\s?(\b|(?=\[))/', $changelog_line ) ) { $changelog_line = preg_replace( '/[*|-]?\s?(\[fix]|\[Fix]|fix|Fix)[:]?\s?(\b|(?=\[))/', '', $changelog_line ); $releases[ $release_count ]['fixes'][] = $this->parse_md_and_clean( $changelog_line ); continue; } if ( preg_match( '/[*|-]?\s?(\[feat]|\[Feat]|feat|Feat)[:]?\s?(\b|(?=\[))/', $changelog_line ) ) { $changelog_line = preg_replace( '/[*|-]?\s?(\[feat]|\[Feat]|feat|Feat)[:]?\s?(\b|(?=\[))/', '', $changelog_line ); $releases[ $release_count ]['features'][] = $this->parse_md_and_clean( $changelog_line ); continue; } $changelog_line = $this->parse_md_and_clean( $changelog_line ); if ( empty( $changelog_line ) ) { continue; } if ( ! isset( $releases[ $release_count ]['tweaks'] ) ) { $releases[ $release_count ]['tweaks'] = []; } $releases[ $release_count ]['tweaks'][] = $changelog_line; } return array_values( $releases ); } /** * Parse markdown links, convert bold markers (**) to <b> tags, and cleanup string. * * @param string $string changelog line. * * @return string */ private function parse_md_and_clean( $string ) { // Drop spaces, starting lines | asterisks. $string = trim( $string ); $string = ltrim( $string, '*' ); $string = ltrim( $string, '-' ); // Replace markdown links with <a> tags. $string = preg_replace_callback( '/\[(.*?)]\((.*?)\)/', function ( $matches ) { return '<a href="' . $matches[2] . '" target="_blank" rel="noopener"><i class="dashicons dashicons-external"></i>' . $matches[1] . '</a>'; }, htmlspecialchars( $string ) ); // Convert bold markdown (**text**) to <b>text</b>. $string = preg_replace( '/\*\*(.*?)\*\*/', '<b>$1</b>', $string ); return $string; } } plugin_helper.php 0000666 00000007202 15226760543 0010131 0 ustar 00 <?php /** * Plugin Action Helper * * @package Neve\Admin\Dashboard */ namespace Neve\Admin\Dashboard; /** * Class Plugin_Helper * * @package Neve\Admin\Dashboard */ class Plugin_Helper { /** * Check plugin state. * * @param string $slug - plugin slug. * * @return string */ public function get_plugin_state( $slug ) { $plugin_link_suffix = $this->get_plugin_path( $slug ); if ( file_exists( WP_PLUGIN_DIR . '/' . $plugin_link_suffix ) ) { return is_plugin_active( $plugin_link_suffix ) ? 'deactivate' : 'activate'; } return 'install'; } /** * Get plugin path based on plugin slug. * * @param string $slug - plugin slug. * * @return string */ public function get_plugin_path( $slug ) { switch ( $slug ) { case 'mailin': return $slug . '/sendinblue.php'; case 'wpforms-lite': return $slug . '/wpforms.php'; case 'intergeo-maps': case 'visualizer': case 'translatepress-multilingual': return $slug . '/index.php'; case 'beaver-builder-lite-version': return $slug . '/fl-builder.php'; case 'adblock-notify-by-bweb': return $slug . '/adblock-notify.php'; case 'feedzy-rss-feeds': return $slug . '/feedzy-rss-feed.php'; case 'wp-cloudflare-page-cache': return $slug . '/wp-cloudflare-super-page-cache.php'; default: return $slug . '/' . $slug . '.php'; } } /** * Call plugin api * * @param string $slug plugin slug. * * @return object */ public function get_plugin_details( $slug ) { include_once ABSPATH . 'wp-admin/includes/plugin-install.php'; return plugins_api( 'plugin_information', array( 'slug' => $slug, 'fields' => array( 'downloaded' => false, 'rating' => false, 'description' => false, 'short_description' => true, 'donate_link' => false, 'tags' => false, 'sections' => false, 'homepage' => false, 'added' => false, 'last_updated' => false, 'compatibility' => false, 'tested' => false, 'requires' => false, 'downloadlink' => false, 'icons' => false, 'banners' => true, ), ) ); } /** * Get Plugin Action link. * * @param string $slug plugin slug. * @param string $action action [activate, deactivate]. * @return string */ public function get_plugin_action_link( $slug, $action = 'activate' ) { if ( ! in_array( $action, [ 'activate', 'deactivate' ] ) ) { return ''; } return add_query_arg( array( 'action' => $action, 'plugin' => rawurlencode( $this->get_plugin_path( $slug ) ), 'plugin_status' => 'all', 'paged' => '1', '_wpnonce' => wp_create_nonce( $action . '-plugin_' . $this->get_plugin_path( $slug ) ), ), esc_url( 'plugins.php' ) ); } /** * Get plugin version. * * @param string $slug plugin slug. * @return string | bool */ public function get_plugin_version( $slug, $default = false ) { $plugin_file = $this->get_plugin_path( $slug ); if ( ! is_plugin_active( $plugin_file ) ) { return $default; } $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin_file ); if ( ! array_key_exists( 'Version', $plugin_data ) ) { return $default; } return $plugin_data['Version']; } /** * Check if current plugin is activated network-wide * * @param string $slug plugin slug. * @return bool */ public function get_is_network_wide( $slug ) { if ( ! is_multisite() ) { return false; } $plugin_file = $this->get_plugin_path( $slug ); return is_plugin_active_for_network( $plugin_file ); } } main.php 0000666 00000076571 15226760543 0006237 0 ustar 00 <?php /** * Main class of the Neve Dashboard * * @package neve */ namespace Neve\Admin\Dashboard; use Neve\Core\Limited_Offers; use Neve\Core\Theme_Info; /** * Class Main * * @package Neve\Admin\Dashboard */ class Main { use Theme_Info; /** * Changelog Handler. * * @var Changelog_Handler */ private $cl_handler; /** * Plugin Helper instance. * * @var Plugin_Helper */ private $plugin_helper; /** * Current theme args. * * @var array */ private $theme_args = []; /** * Useful plugins array. * * @var array */ private $useful_plugins = [ 'optimole-wp', 'wp-landing-kit', 'otter-blocks', 'wp-cloudflare-page-cache', 'templates-patterns-collection', 'themeisle-companion', 'translatepress-multilingual', 'amp', ]; /** * Plugins Cache key. * * @var string */ private $plugins_cache_key = 'neve_dash_useful_plugins'; /** * Plugins Cache Hash key. * * @var string */ private $plugins_cache_hash_key = 'neve_dash_useful_plugins_hash'; /** * Main constructor. */ public function __construct() { $this->plugin_helper = new Plugin_Helper(); $this->cl_handler = new Changelog_Handler(); } /** * Run WordPress attached to actions. */ public function init() { $this->setup_config(); add_action( 'init', [ $this, 'setup_config' ] ); add_action( 'admin_menu', [ $this, 'register' ] ); add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ] ); add_action( 'init', array( $this, 'register_settings' ) ); add_action( 'init', array( $this, 'register_about_page' ), 1 ); } /** * Add the about page with respect to the white label settings. * * @return void */ public function register_about_page() { $theme = wp_get_theme(); $filtered_name = apply_filters( 'ti_wl_theme_name', $theme->__get( 'Name' ) ); $slug = $theme->__get( 'stylesheet' ); if ( empty( $slug ) || empty( $filtered_name ) ) { return; } // We check if the name is different from the filtered name, // if it is, the whitelabel is in use and we should not add the about page. // this check allows for child themes to use the about page. if ( $filtered_name !== $theme->__get( 'Name' ) ) { return; } add_filter( 'neve_about_us_metadata', function () use ( $filtered_name ) { return [ // Top-level page in the dashboard sidebar 'location' => 'neve-welcome', // Logo to display on the page 'logo' => get_template_directory_uri() . '/assets/img/dashboard/logo.svg', // Condition to show or hide the upgrade menu in the sidebar 'has_upgrade_menu' => ! defined( 'NEVE_PRO_VERSION' ), // Add predefined product pages to the about page. 'product_pages' => [ 'otter-page' ], // Upgrade menu item link & text 'upgrade_link' => tsdk_utmify( esc_url( 'https://themeisle.com/themes/neve/upgrade/' ), 'aboutfilter', 'nevedashboard' ), 'upgrade_text' => __( 'Upgrade', 'neve' ) . ' ' . $filtered_name, ]; } ); } /** * Register Logger Setting */ public function register_settings() { register_setting( 'neve_settings', 'neve_logger_flag', [ 'type' => 'string', 'show_in_rest' => true, 'default' => '', ] ); } /** * Setup the class props based on current theme. */ public function setup_config() { $theme = wp_get_theme(); $this->theme_args['name'] = apply_filters( 'ti_wl_theme_name', $theme->__get( 'Name' ) ); $this->theme_args['template'] = $theme->get( 'Template' ); $this->theme_args['version'] = $theme->__get( 'Version' ); $this->theme_args['description'] = apply_filters( 'ti_wl_theme_description', $theme->__get( 'Description' ) ); $this->theme_args['slug'] = $theme->__get( 'stylesheet' ); } /** * Register theme options page. * * @return void */ public function register() { $theme = $this->theme_args; if ( empty( $theme['name'] ) || empty( $theme['slug'] ) ) { return; } $theme_page = ! empty( $theme['template'] ) ? $theme['template'] . '-welcome' : $theme['slug'] . '-welcome'; $icon = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI3LjQuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAzMiAzMiIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMzIgMzI7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCS5zdDB7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojRkZGRkZGO30KPC9zdHlsZT4KPHBhdGggY2xhc3M9InN0MCIgZD0iTTAsMS42QzAsMC43LDAuNywwLDEuNiwwaDI4LjhDMzEuMywwLDMyLDAuNywzMiwxLjZWMzBjMCwwLjktMC43LDEuNi0xLjYsMS42SDEuNkMwLjcsMzEuNSwwLDMwLjgsMCwzMFYxLjZ6CgkgTTEzLDE1Ljh2Ny41SDkuMVY4YzAtMC4xLDAtMC4xLDAuMS0wLjJjMCwwLDAuMSwwLDAuMiwwLjFsOS42LDcuOFY4LjJoMy44djE1LjJjMCwwLjEsMCwwLjEtMC4xLDAuMmMwLDAtMC4xLDAtMC4yLTAuMUwxMywxNS44egoJIE0yMi44LDI1LjdIOS4xVjI3aDEzLjdWMjUuN3oiLz4KPC9zdmc+Cg=='; if ( $theme['name'] !== 'Neve' ) { $icon = 'dashicons-admin-appearance'; } $neve_icon = apply_filters( 'neve_menu_icon', $icon ); $priority = apply_filters( 'neve_menu_priority', 59 ); // The position of the menu item, 60 is the position of the Appearance menu. $capability = 'manage_options'; // Place a theme page in the Appearance menu, for older versions of Neve Pro or TPC. to maintain backwards compatibility. if ( ( defined( 'NEVE_PRO_VERSION' ) && version_compare( NEVE_PRO_VERSION, '2.6.1', '<=' ) ) || ( defined( 'TIOB_VERSION' ) && version_compare( TIOB_VERSION, '1.1.38', '<=' ) ) ) { add_theme_page( /* translators: %s - Theme name */ sprintf( __( '%s Options', 'neve' ), wp_kses_post( $theme['name'] ) ), /* translators: %s - Theme name */ sprintf( __( '%s Options', 'neve' ), wp_kses_post( $theme['name'] ) ), $capability, 'admin.php?page=neve-welcome' ); } add_menu_page( // phpcs:ignore WPThemeReview.PluginTerritory.NoAddAdminPages.add_menu_pages_add_menu_page wp_kses_post( $theme['name'] ), wp_kses_post( $theme['name'] ), $capability, $theme_page, [ $this, 'render' ], $neve_icon, // The URL to the icon to be used for this menu $priority ); // Add Dashboard submenu. Same slug as parent to allow renaming the automatic submenu that is added. add_submenu_page( // phpcs:ignore WPThemeReview.PluginTerritory.NoAddAdminPages.add_menu_pages_add_submenu_page $theme_page, /* translators: %s - Theme name */ sprintf( __( '%s Options', 'neve' ), wp_kses_post( $theme['name'] ) ), /* translators: %s - Theme name */ sprintf( __( '%s Options', 'neve' ), wp_kses_post( $theme['name'] ) ), $capability, $theme_page, [ $this, 'render' ] ); $this->copy_customizer_page( $theme_page ); if ( ! defined( 'NEVE_PRO_VERSION' ) || 'valid' !== apply_filters( 'product_neve_license_status', false ) ) { // Add Custom Layout submenu for upsell. add_submenu_page( // phpcs:ignore WPThemeReview.PluginTerritory.NoAddAdminPages.add_menu_pages_add_submenu_page $theme_page, __( 'Custom Layouts', 'neve' ), __( 'Custom Layouts', 'neve' ), $capability, 'admin.php?page=neve-welcome#custom-layouts' ); } } /** * Copy the customizer page to the dashboard. * * @param string $theme_page The theme page slug. * * @return void */ private function copy_customizer_page( $theme_page ) { global $submenu; if ( ! isset( $submenu['themes.php'] ) ) { return; } $themes_menu = $submenu['themes.php']; if ( empty( $themes_menu ) ) { return; } $customize_pos = array_search( 'customize', array_column( $themes_menu, 1 ) ); if ( false === $customize_pos ) { return; } $themes_page_keys = array_keys( $themes_menu ); if ( ! isset( $themes_page_keys[ $customize_pos ] ) ) { return; } $customizer_menu_item = array_splice( $themes_menu, $customize_pos, 1 ); $customizer_menu_item = reset( $customizer_menu_item ); if ( empty( $customizer_menu_item ) ) { return; } add_submenu_page( // phpcs:ignore WPThemeReview.PluginTerritory.NoAddAdminPages.add_menu_pages_add_submenu_page $theme_page, $customizer_menu_item[0], $customizer_menu_item[0], 'manage_options', 'customize.php' ); } /** * Render the application stub. * * @return void */ public function render() { echo '<div id="neve-dashboard"></div>'; } /** * Load css and scripts for the about page */ public function enqueue() { $screen = get_current_screen(); if ( ! isset( $screen->id ) ) { return; } $theme = $this->theme_args; $theme_page = ! empty( $theme['template'] ) ? $theme['template'] . '-welcome' : $theme['slug'] . '-welcome'; if ( $screen->id !== 'toplevel_page_' . $theme_page ) { return; } $build_path = get_template_directory_uri() . '/assets/apps/dashboard/build/'; $dependencies = ( include get_template_directory() . '/assets/apps/dashboard/build/dashboard.asset.php' ); wp_register_style( 'neve-dash-style', $build_path . 'style-dashboard.css', [ 'wp-components', 'neve-components' ], $dependencies['version'] ); wp_style_add_data( 'neve-dash-style', 'rtl', 'replace' ); wp_enqueue_style( 'neve-dash-style' ); wp_register_script( 'neve-dash-script', $build_path . 'dashboard.js', array_merge( $dependencies['dependencies'], [ 'updates' ] ), $dependencies['version'], true ); wp_localize_script( 'neve-dash-script', 'neveDash', apply_filters( 'neve_dashboard_page_data', $this->get_localization() ) ); wp_enqueue_script( 'neve-dash-script' ); if ( function_exists( 'wp_set_script_translations' ) ) { wp_set_script_translations( 'neve-dash-script', 'neve' ); } } /** * Get localization data for the dashboard script. * * @return array */ private function get_localization() { $offer = new Limited_Offers(); $old_about_config = apply_filters( 'ti_about_config_filter', [ 'useful_plugins' => true ] ); $theme_name = apply_filters( 'ti_wl_theme_name', $this->theme_args['name'] ); $plugin_name = apply_filters( 'ti_wl_plugin_name', 'Neve Pro' ); $plugin_name_addon = apply_filters( 'ti_wl_plugin_name', 'Neve Pro Addon' ); $data = [ 'nonce' => wp_create_nonce( 'wp_rest' ), 'version' => 'v' . NEVE_VERSION, 'assets' => get_template_directory_uri() . '/assets/img/dashboard/', 'hasOldPro' => (bool) ( defined( 'NEVE_PRO_VERSION' ) && version_compare( NEVE_PRO_VERSION, '1.1.11', '<' ) ), 'isRTL' => is_rtl(), 'isValidLicense' => $this->has_valid_addons(), 'notifications' => $this->get_notifications(), 'customizerShortcuts' => $this->get_customizer_shortcuts(), 'plugins' => $this->get_useful_plugins(), 'featureData' => $this->get_free_pro_features(), 'showFeedbackNotice' => $this->should_show_feedback_notice(), 'allfeaturesNeveProURL' => tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'seeallfeatures', 'freevspropage' ), 'startSitesgetNeveProURL' => tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'welcomestartersitescard', 'nevedashboard' ), 'customLayoutsNeveProURL' => tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'customlayoutscard', 'nevedashboard' ), 'upgradeURL' => apply_filters( 'neve_upgrade_link_from_child_theme_filter', tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'getpronow', 'freevspropage' ) ), 'supportURL' => esc_url( 'https://wordpress.org/support/theme/neve/' ), 'docsURL' => esc_url( 'https://docs.themeisle.com/article/946-neve-doc' ), 'codexURL' => esc_url( 'https://codex.nevewp.com/' ), 'strings' => [ 'proTabTitle' => wp_kses_post( $plugin_name ), /* translators: %s - Theme name */ 'header' => sprintf( __( '%s Options', 'neve' ), wp_kses_post( $theme_name ) ), /* translators: %s - Theme name */ 'starterSitesCardDescription' => sprintf( __( '%s now comes with a sites library with various designs to pick from. Visit our collection of demos that are constantly being added.', 'neve' ), wp_kses_post( $theme_name ) ), 'starterSitesCardUpsellMessage' => esc_html__( 'Upgrade to the Pro version and get instant access to all Premium Starter Sites — including Expert Sites — and much more.', 'neve' ), /* translators: %s - Theme name */ 'starterSitesTabDescription' => sprintf( __( 'With %s, you can choose from multiple unique demos, specially designed for you, that can be installed with a single click. You just need to choose your favorite, and we will take care of everything else.', 'neve' ), wp_kses_post( $theme_name ) ), /* translators: 1 - Theme name, 2 - Cloud Templates & Patterns Collection */ 'starterSitesUnavailableActive' => sprintf( __( 'In order to be able to import any starter sites for %1$s you would need to have the %2$s plugin active.', 'neve' ), wp_kses_post( $theme_name ), 'Cloud Templates & Patterns Collection' ), /* translators: %s - Theme name */ 'starterSitesUnavailableUpdate' => sprintf( __( 'In order to be able to import any starter sites for %1$s you would need to have the %2$s plugin updated to the latest version.', 'neve' ), wp_kses_post( $theme_name ), 'Cloud Templates & Patterns Collection' ), /* translators: %s - Theme name */ 'supportCardDescription' => sprintf( __( 'We want to make sure you have the best experience using %1$s, and that is why we have gathered all the necessary information here for you. We hope you will enjoy using %1$s as much as we enjoy creating great products.', 'neve' ), wp_kses_post( $theme_name ) ), /* translators: %s - Theme name */ 'docsCardDescription' => sprintf( __( 'Need more details? Please check our full documentation for detailed information on how to use %s.', 'neve' ), wp_kses_post( $theme_name ) ), /* translators: %s - "Neve Pro Addon" */ 'licenseCardHeading' => sprintf( __( '%s license', 'neve' ), wp_kses_post( $plugin_name_addon ) ), /* translators: %s - "Neve Pro Addon" */ 'updateOldPro' => sprintf( __( 'Please update %s to the latest version and then refresh this page to have access to the options.', 'neve' ), wp_kses_post( $plugin_name_addon ) ), /* translators: %1$s - Author link - Themeisle */ 'licenseCardDescription' => sprintf( // translators: store name (Themeisle) __( 'Enter your license from %1$s purchase history in order to get plugin updates', 'neve' ), '<a target="_blank" rel="external noreferrer noopener" href="https://store.themeisle.com/">ThemeIsle<span class="components-visually-hidden">' . esc_html__( '(opens in a new tab)', 'neve' ) . '</span><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20" class="components-external-link__icon" role="img" aria-hidden="true" focusable="false" style="fill: #0073AA"><path d="M18.2 17c0 .7-.6 1.2-1.2 1.2H7c-.7 0-1.2-.6-1.2-1.2V7c0-.7.6-1.2 1.2-1.2h3.2V4.2H7C5.5 4.2 4.2 5.5 4.2 7v10c0 1.5 1.2 2.8 2.8 2.8h10c1.5 0 2.8-1.2 2.8-2.8v-3.6h-1.5V17zM14.9 3v1.5h3.7l-6.4 6.4 1.1 1.1 6.4-6.4v3.7h1.5V3h-6.3z"></path></svg></a>' ), ], 'changelog' => $this->cl_handler->get_changelog( get_template_directory() . '/CHANGELOG.md' ), 'onboarding' => [], 'hasFileSystem' => WP_Filesystem(), 'hidePluginsTab' => apply_filters( 'neve_hide_useful_plugins', ! array_key_exists( 'useful_plugins', $old_about_config ) ), 'tpcPath' => defined( 'TIOB_PATH' ) ? TIOB_PATH . 'template-patterns-collection.php' : 'template-patterns-collection/template-patterns-collection.php', 'tpcAdminURL' => admin_url( 'admin.php?page=tiob-starter-sites' ), 'pluginsURL' => esc_url( admin_url( 'plugins.php' ) ), 'getPluginStateBaseURL' => esc_url( rest_url( '/nv/v1/dashboard/plugin-state/' ) ), 'canInstallPlugins' => current_user_can( 'install_plugins' ), 'canActivatePlugins' => current_user_can( 'activate_plugins' ), 'deal' => ! defined( 'NEVE_PRO_VERSION' ) ? $offer->get_localized_data() : array(), ]; if ( defined( 'NEVE_PRO_PATH' ) ) { $installed_plugins = get_plugins(); $is_otter_installed = array_key_exists( 'otter-pro/otter-pro.php', $installed_plugins ); $is_sparks_installed = array_key_exists( 'sparks-for-woocommerce/sparks-for-woocommerce.php', $installed_plugins ); $data['changelogPro'] = $this->cl_handler->get_changelog( NEVE_PRO_PATH . '/CHANGELOG.md' ); $data['isOtterProInstalled'] = $is_otter_installed; $data['otterProInstall'] = $is_otter_installed ? esc_url( wp_nonce_url( admin_url( 'plugins.php?action=activate&plugin=otter-pro%2Fotter-pro.php&plugin_status=all&paged=1&s' ), 'activate-plugin_otter-pro/otter-pro.php' ) ) : esc_url( wp_nonce_url( admin_url( 'admin-post.php?action=install_otter_pro' ), 'install_otter_pro' ) ); $data['sparksInstallActivateEndpoint'] = $is_sparks_installed ? esc_url( wp_nonce_url( admin_url( 'plugins.php?action=activate&plugin=sparks-for-woocommerce%2Fsparks-for-woocommerce.php&plugin_status=all&paged=1&s' ), 'activate-plugin_sparks-for-woocommerce/sparks-for-woocommerce.php' ) ) : esc_url( wp_nonce_url( admin_url( 'admin-post.php?action=install_sparks' ), 'install_sparks' ) ); $data['moduleObserver'] = array( 'customLayouts' => array( 'labelSubMenu' => __( 'Custom Layouts', 'neve' ), 'linkSubMenu' => 'edit.php?post_type=neve_custom_layouts', ), ); } if ( isset( $_GET['onboarding'] ) && $_GET['onboarding'] === 'yes' ) { $data['isOnboarding'] = true; } return $data; } /** * Get the notifications for plugin and theme updates. * * @return array */ public function get_notifications() { $notifications = []; $slug = 'neve'; $themes_update = get_site_transient( 'update_themes' ); $plugin_folder = defined( 'NEVE_PRO_BASEFILE' ) ? basename( dirname( NEVE_PRO_BASEFILE ) ) : null; $plugin_path = $plugin_folder ? $plugin_folder . '/neve-pro-addon.php' : null; if ( isset( $themes_update->response[ $slug ] ) ) { $update = $themes_update->response[ $slug ]; $notifications['neve'] = [ // translators: s - theme name (Neve). 'text' => sprintf( __( 'New theme update for %1$s! Please update to %2$s.', 'neve' ), wp_kses_post( $this->theme_args['name'] ), wp_kses_post( $update['new_version'] ) ), 'update' => [ 'type' => 'theme', 'slug' => $slug, ], 'cta' => __( 'Update Now', 'neve' ), 'type' => ( $plugin_path && is_plugin_active( $plugin_path ) ) ? 'warning' : null, ]; } if ( $plugin_path ) { $plugins_update = get_site_transient( 'update_plugins' ); if ( is_plugin_active( $plugin_path ) && isset( $plugins_update->response[ $plugin_path ] ) ) { $update = $plugins_update->response[ $plugin_path ]; $notifications['neve-pro-addon'] = [ 'text' => sprintf( // translators: s - Pro plugin name (Neve Pro) __( 'New plugin update for %1$s! Please update to %2$s.', 'neve' ), wp_kses_post( apply_filters( 'ti_wl_plugin_name', 'Neve Pro' ) ), wp_kses_post( $update->new_version ) ), 'update' => [ 'type' => 'plugin', 'slug' => 'neve-pro-addon', 'path' => $plugin_path, ], 'cta' => __( 'Update Now', 'neve' ), 'type' => 'warning', ]; } } if ( $this->show_branding_notice() ) { $notifications['branding-discount'] = [ 'text' => sprintf( // translators: s - Discount Code __( 'From 3.3.0 we decided to remove the copyright component from the free version. You can continue using it if you rollback to 3.2.x or you can upgrade to pro, using a one time 50%% discount: %s', 'neve' ), wp_kses_post( '<code>NEVEBRANDING50</code>' ) ), 'url' => tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'copyrightnotice', 'nevedashboard' ), 'targetBlank' => true, 'cta' => __( 'Upgrade', 'neve' ), ]; } if ( count( $notifications ) === 1 && is_plugin_active( $plugin_path ) ) { foreach ( $notifications as $key => $notification ) { /* translators: 1 - Theme Name (Neve), 2 - Plugin Name (Neve Pro) */ $notifications[ $key ]['text'] = sprintf( __( 'We recommend that both %1$s and %2$s are updated to the latest version to ensure optimal intercompatibility.', 'neve' ), wp_kses_post( $this->theme_args['name'] ), apply_filters( 'ti_wl_plugin_name', 'Neve Pro' ) ); } } $notifications = apply_filters( 'neve_dashboard_notifications', $notifications ); return $notifications; } /** * Should branding notice be shown. * * @return bool */ private function show_branding_notice() { if ( $this->has_valid_addons() ) { return false; } return time() < strtotime( '2022-07-06' ); } /** * Get the Customizer Shortcut Links. * * @return array */ private function get_customizer_shortcuts() { return [ [ 'text' => __( 'Upload Logo', 'neve' ), 'link' => add_query_arg( [ 'autofocus[control]' => 'custom_logo' ], admin_url( 'customize.php' ) ), ], [ 'text' => __( 'Set Colors', 'neve' ), 'link' => add_query_arg( [ 'autofocus[section]' => 'neve_colors_background_section' ], admin_url( 'customize.php' ) ), ], [ 'text' => __( 'Customize Fonts', 'neve' ), 'link' => add_query_arg( [ 'autofocus[control]' => 'neve_headings_font_family' ], admin_url( 'customize.php' ) ), ], [ 'text' => __( 'Layout Options', 'neve' ), 'link' => add_query_arg( [ 'autofocus[panel]' => 'neve_layout' ], admin_url( 'customize.php' ) ), ], [ 'text' => __( 'Header Options', 'neve' ), 'link' => add_query_arg( [ 'autofocus[panel]' => 'hfg_header' ], admin_url( 'customize.php' ) ), ], [ 'text' => __( 'Blog Layouts', 'neve' ), 'link' => add_query_arg( [ 'autofocus[section]' => 'neve_blog_archive_layout' ], admin_url( 'customize.php' ) ), ], [ 'text' => __( 'Footer Options', 'neve' ), 'link' => add_query_arg( [ 'autofocus[panel]' => 'hfg_footer' ], admin_url( 'customize.php' ) ), ], [ 'text' => __( 'Content / Sidebar', 'neve' ), 'link' => add_query_arg( [ 'autofocus[section]' => 'neve_sidebar' ], admin_url( 'customize.php' ) ), ], ]; } /** * Get doc link. * * @param string $utm_term utm term to use for doc link. * @param string $url url to doc. * @return string */ private function get_doc_link( $utm_term, $url ) { return tsdk_utmify( $url, $utm_term, 'freevspropage' ); } /** * Get the pro features for the free v pro table. * * @return array */ private function get_free_pro_features() { return [ [ 'title' => __( 'Header/Footer builder', 'neve' ), 'description' => __( 'Easily build your header and footer by dragging and dropping all the important elements in the real-time WordPress Customizer. More advanced options are available in PRO.', 'neve' ), 'inLite' => true, 'docsLink' => $this->get_doc_link( 'Header/Footer builder', 'https://docs.themeisle.com/category/1251-neve-header-builder' ), ], [ 'title' => __( 'Page Builder Compatibility', 'neve' ), 'description' => __( 'Neve is fully compatible with Gutenberg, the new WordPress editor and for all of you page builder fans, Neve has full compatibility with Elementor, Beaver Builder, and all the other popular page builders.', 'neve' ), 'inLite' => true, 'docsLink' => $this->get_doc_link( 'Page Builder Compatibility', 'https://docs.themeisle.com/article/946-neve-doc#pagebuilders' ), ], [ 'title' => __( 'Header Booster', 'neve' ), 'description' => __( 'Take the header builder to a new level with new awesome components: socials, contact, breadcrumbs, language switcher, multiple HTML, sticky and transparent menu, page header builder and many more.', 'neve' ), 'inLite' => false, 'docsLink' => $this->get_doc_link( 'Header Booster', 'https://docs.themeisle.com/article/1057-header-booster-documentation' ), ], [ 'title' => __( 'Page Header Builder', 'neve' ), 'description' => __( 'The Page Header is the horizontal area that sits directly below the header and contains the page/post title. Easily design an attractive Page Header area using our dedicated builder.', 'neve' ), 'inLite' => false, 'docsLink' => $this->get_doc_link( 'Page Header Builder', 'https://docs.themeisle.com/article/1262-neve-page-header' ), ], [ 'title' => __( 'Custom Layouts', 'neve' ), 'description' => __( 'Powerful Custom Layouts builder which allows you to easily create your own header, footer or custom content on any of the hook locations available in the theme.', 'neve' ), 'inLite' => false, 'docsLink' => $this->get_doc_link( 'Custom Layouts', 'https://docs.themeisle.com/article/1062-custom-layouts-module' ), ], [ 'title' => __( 'Blog Booster', 'neve' ), 'description' => __( 'Give a huge boost to your entire blogging experience with features specially designed for increased user experience.', 'neve' ) . ' ' . __( 'Sharing, custom article sorting, comments integrations, number of minutes needed to read an article and many more.', 'neve' ), 'inLite' => false, 'docsLink' => $this->get_doc_link( 'Blog Booster', 'https://docs.themeisle.com/article/1059-blog-booster-documentation' ), ], [ 'title' => __( 'Elementor Booster', 'neve' ), 'description' => __( 'Leverage the true flexibility of Elementor with powerful addons and templates that you can import with just one click.', 'neve' ), 'inLite' => false, 'docsLink' => $this->get_doc_link( 'Elementor Booster', 'https://docs.themeisle.com/article/1063-elementor-booster-module-documentation' ), ], [ 'title' => __( 'WooCommerce Booster', 'neve' ), 'description' => __( 'Empower your online store with awesome new features, specially designed for a smooth WooCommerce integration.', 'neve' ) . ' ' . __( 'Wishlist, quick view, video products, advanced reviews, multiple dedicated layouts and many more.', 'neve' ), 'inLite' => false, 'docsLink' => $this->get_doc_link( 'WooCommerce Booster', 'https://docs.themeisle.com/article/1058-woocommerce-booster-documentation' ), ], [ 'title' => __( 'LifterLMS Booster', 'neve' ), 'description' => __( 'Make your LifterLMS pages look stunning with our PRO design options. Specially created to help you set up your online courses with minimum customizations.', 'neve' ), 'inLite' => false, 'docsLink' => $this->get_doc_link( 'LifterLMS Booster', 'https://docs.themeisle.com/article/1084-lifterlms-booster-documentation' ), ], [ 'title' => __( 'Typekit(Adobe) Fonts', 'neve' ), 'description' => __( "The module allows for an easy way of enabling new awesome Adobe (previous Typekit) Fonts in Neve's Typography options.", 'neve' ), 'inLite' => false, 'docsLink' => $this->get_doc_link( 'Typekit(Adobe) Fonts', 'https://docs.themeisle.com/article/1085-typekit-fonts-documentation' ), ], [ 'title' => __( 'White Label', 'neve' ), 'description' => __( "For any developer or agency out there building websites for their own clients, we've made it easy to present the theme as your own.", 'neve' ), 'inLite' => false, 'docsLink' => $this->get_doc_link( 'White Label', 'https://docs.themeisle.com/article/1061-white-label-module-documentation' ), ], [ 'title' => __( 'Scroll To Top', 'neve' ), 'description' => __( 'Simple but effective module to help you navigate back to the top of the really long pages.', 'neve' ), 'inLite' => false, 'docsLink' => $this->get_doc_link( 'Scroll To Top', 'https://docs.themeisle.com/article/1060-scroll-to-top-module-documentation' ), ], [ 'title' => __( 'See all PRO features', 'neve' ), 'presentational' => true, ], ]; } /** * Get the useful plugin data. * * @return array */ private function get_useful_plugins() { $available = get_transient( $this->plugins_cache_key ); $hash = get_transient( $this->plugins_cache_hash_key ); $current_hash = substr( md5( wp_json_encode( $this->useful_plugins ) ), 0, 5 ); if ( $available !== false && $hash === $current_hash ) { $available = json_decode( $available, true ); foreach ( $available as $slug => $args ) { $available[ $slug ]['cta'] = ( $args['cta'] === 'external' ) ? 'external' : $this->plugin_helper->get_plugin_state( $slug ); $available[ $slug ]['path'] = $this->plugin_helper->get_plugin_path( $slug ); $available[ $slug ]['activate'] = $this->plugin_helper->get_plugin_action_link( $slug ); $available[ $slug ]['deactivate'] = $this->plugin_helper->get_plugin_action_link( $slug, 'deactivate' ); $available[ $slug ]['network'] = $this->plugin_helper->get_is_network_wide( $slug ); $available[ $slug ]['version'] = ! empty( $available[ $slug ]['version'] ) ? $this->plugin_helper->get_plugin_version( $slug, $available[ $slug ]['version'] ) : ''; } return $available; } $data = []; foreach ( $this->useful_plugins as $slug ) { if ( array_key_exists( $slug, $this->get_external_plugins_data() ) ) { $data[ $slug ] = $this->get_external_plugins_data()[ $slug ]; continue; } $current_plugin = $this->plugin_helper->get_plugin_details( $slug ); if ( $current_plugin instanceof \WP_Error ) { continue; } $data[ $slug ] = [ 'banner' => $current_plugin->banners['low'], 'name' => html_entity_decode( $current_plugin->name ), 'description' => html_entity_decode( $current_plugin->short_description ), 'version' => $current_plugin->version, 'author' => html_entity_decode( wp_strip_all_tags( $current_plugin->author ) ), 'cta' => $this->plugin_helper->get_plugin_state( $slug ), 'path' => $this->plugin_helper->get_plugin_path( $slug ), 'activate' => $this->plugin_helper->get_plugin_action_link( $slug ), 'deactivate' => $this->plugin_helper->get_plugin_action_link( $slug, 'deactivate' ), 'network' => $this->plugin_helper->get_is_network_wide( $slug ), ]; } set_transient( $this->plugins_cache_hash_key, $current_hash ); set_transient( $this->plugins_cache_key, wp_json_encode( $data ) ); return $data; } /** * Check if feedback notice should be shown after 14 days since activation. * * @return bool */ private function should_show_feedback_notice() { $activated_time = get_option( 'neve_install' ); if ( ! empty( $activated_time ) ) { if ( time() - intval( $activated_time ) > 14 * DAY_IN_SECONDS ) { return true; } } return false; } /** * Get data of external plugins that are not hosted on wp.org. * * @return array */ private function get_external_plugins_data() { $plugins = array( 'wp-landing-kit' => array( 'banner' => NEVE_ASSETS_URL . 'img/dashboard/wp-landing.jpg', 'name' => 'WP Landing Kit', 'description' => 'Turn WordPress into a landing page powerhouse with Landing Kit. Map domains to pages or any other published resource.', 'author' => 'Themeisle', 'cta' => 'external', 'url' => tsdk_utmify( 'https://wplandingkit.com/', 'recommendedplugins', 'nevedashboard' ), 'premium' => true, ), ); return $plugins; } }
| ver. 1.4 |
Github
|
.
| PHP 5.4.45 | Generation time: 0 |
proxy
|
phpinfo
|
Settings