core = $modules->core; $this->modules = $modules; $this->store = new dcStore($modules, $xml_url); $this->setPathInfo($modules_root); $this->setNavSpecial(__('other')); } public function newList($list_id) { $this->data = array(); $this->page_tab = ''; $this->list_id = $list_id; return $this; } protected function setPathInfo($root) { $paths = explode(PATH_SEPARATOR, $root); $path = array_pop($paths); unset($paths); $this->path = $path; if (is_dir($path) && is_writeable($path)) { $this->path_writable = true; $this->path_pattern = preg_quote($path,'!'); } return $this; } public function getPath() { return $this->path; } public function isPathWritable() { return $this->path_writable; } public function isPathDeletable($root) { return $this->path_writable && preg_match('!^'.$this->path_pattern.'!', $root) && $this->core->auth->isSuperAdmin(); } public function setPageURL($url) { $this->page_qs = strpos('?', $url) ? '&' : '?'; $this->page_url = $url; return $this; } public function getPageURL($queries='', $with_tab=true) { return $this->page_url. (!empty($queries) ? $this->page_qs : ''). (is_array($queries) ? http_build_query($queries) : $queries). ($with_tab && !empty($this->page_tab) ? '#'.$this->page_tab : ''); } public function setPageTab($tab) { $this->page_tab = $tab; return $this; } public function getPageTab() { return $this->page_tab; } public function getSearchQuery() { $query = !empty($_REQUEST['m_search']) ? trim($_REQUEST['m_search']) : null; return strlen($query) > 1 ? $query : null; } public function displaySearchForm() { $query = $this->getSearchQuery(); if (empty($this->data) && $query === null) { return $this; } echo '
'. '


'. form::field(array('m_search','m_search'), 30, 255, html::escapeHTML($query)). ' '; if ($query) { echo ' '.__('Reset search').''; } echo '

'. '
'; if ($query) { echo '

'.sprintf( __('Found %d result for search "%s":', 'Found %d results for search "%s":', count($this->data)), count($this->data), html::escapeHTML($query) ). '

'; } return $this; } public function setNavSpecial($str) { $this->nav_special = (string) $str; $this->nav_list = array_merge(str_split(self::$nav_indexes), array($this->nav_special)); return $this; } public function getNavQuery() { return isset($_REQUEST['m_nav']) && in_array($_REQUEST['m_nav'], $this->nav_list) ? $_REQUEST['m_nav'] : $this->nav_list[0]; } public function displayNavMenu() { if (empty($this->data) || $this->getSearchQuery() !== null) { return $this; } # Fetch modules required field $indexes = array(); foreach ($this->data as $id => $module) { if (!isset($module[$this->sort_field])) { continue; } $char = substr($module[$this->sort_field], 0, 1); if (!in_array($char, $this->nav_list)) { $char = $this->nav_special; } if (!isset($indexes[$char])) { $indexes[$char] = 0; } $indexes[$char]++; } $buttons = array(); foreach($this->nav_list as $char) { # Selected letter if ($this->getNavQuery() == $char) { $buttons[] = '
  • '.$char.'
  • '; } # Letter having modules elseif (!empty($indexes[$char])) { $title = sprintf(__('%d module', '%d modules', $indexes[$char]), $indexes[$char]); $buttons[] = '
  • '.$char.'
  • '; } # Letter without modules else { $buttons[] = ''; } } # Parse navigation menu echo '
    '.__('Browse index:').'
    '; return $this; } public function setSortField($field, $asc=true) { $this->sort_field = $field; $this->sort_asc = (boolean) $asc; return $this; } public function getSortQuery() { return !empty($_REQUEST['m_sort']) ? $_REQUEST['m_sort'] : $this->sort_field; } public function displaySortForm() { //not yet implemented } public function setModules($modules) { $this->data = array(); if (!empty($modules) && is_array($modules)) { foreach($modules as $id => $module) { $this->data[$id] = self::parseModuleInfo($id, $module); } } return $this; } public function getModules() { return $this->data; } public static function parseModuleInfo($id, $module) { $label = empty($module['label']) ? $id : $module['label']; $name = __(empty($module['name']) ? $label : $module['name']); return array_merge( # Default values array( 'desc' => '', 'author' => '', 'version' => 0, 'current_version' => 0, 'root' => '', 'root_writable' => false, 'permissions' => null, 'parent' => null, 'priority' => 1000, 'standalone_config' => false, 'support' => '', 'section' => '', 'tags' => '', 'details' => '', 'sshot' => '' ), # Module's values $module, # Clean up values array( 'id' => $id, 'sid' => self::sanitizeString($id), 'label' => $label, 'name' => $name, 'sname' => self::sanitizeString($name) ) ); } public static function isDistributedModule($module) { $distributed_modules = self::$distributed_modules; return is_array($distributed_modules) && in_array($module, $distributed_modules); } public static function sortModules($modules, $field, $asc=true) { $sorter = array(); foreach($modules as $id => $module) { $sorter[$id] = isset($module[$field]) ? $module[$field] : $field; } array_multisort($sorter, $asc ? SORT_ASC : SORT_DESC, $modules); return $modules; } public function displayModulesList($cols=array('name', 'config', 'version', 'desc'), $actions=array(), $nav_limit=false) { echo '
    '. ''. ''; if (in_array('name', $cols)) { echo ''; } if (in_array('version', $cols)) { echo ''; } if (in_array('current_version', $cols)) { echo ''; } if (in_array('desc', $cols)) { echo ''; } if (in_array('distrib', $cols)) { echo ''; } if (!empty($actions) && $this->core->auth->isSuperAdmin()) { echo ''; } echo ''; $sort_field = $this->getSortQuery(); # Sort modules by $sort_field (default sname) $modules = $this->getSearchQuery() === null ? self::sortModules($this->data, $sort_field, $this->sort_asc) : $this->data; $count = 0; foreach ($modules as $id => $module) { # Show only requested modules if ($nav_limit && $this->getSearchQuery() === null) { $char = substr($module[$sort_field], 0, 1); if (!in_array($char, $this->nav_list)) { $char = $this->nav_special; } if ($this->getNavQuery() != $char) { continue; } } echo ''; if (in_array('icon', $cols)) { echo ''; } # Link to config file $config = in_array('config', $cols) && !empty($module['root']) && file_exists(path::real($module['root'].'/_config.php')); echo ''; if (in_array('version', $cols)) { echo ''; } if (in_array('current_version', $cols)) { echo ''; } if (in_array('desc', $cols)) { echo ''; } if (in_array('distrib', $cols)) { echo ''; } if (!empty($actions) && $this->core->auth->isSuperAdmin()) { $buttons = $this->getActions($id, $module, $actions); echo ''; } echo ''; $count++; } echo '
    '.__('Name').''.__('Version').''.__('Current version').''.__('Details').''.__('Action').'
    '.sprintf( '%1$s', html::escapeHTML($id), file_exists($module['root'].'/icon.png') ? 'index.php?pf='.$id.'/icon.png' : 'images/module.png' ).''.($config ? ''.html::escapeHTML($module['name']).'' : html::escapeHTML($module['name']) ).''.html::escapeHTML($module['version']).''.html::escapeHTML($module['current_version']).''.html::escapeHTML($module['desc']).''.(self::isDistributedModule($id) ? ''.
					__('Module from official distribution').'' : '').''. '
    '. '
    '. $this->core->formNonce(). form::hidden(array('module'), html::escapeHTML($id)). form::hidden(array('tab'), $this->page_tab). implode(' ', $buttons). '
    '. '
    '. '
    '; if(!$count && $this->getSearchQuery() === null) { echo '

    '.__('No module matches your search.').'

    '; } } protected function getActions($id, $module, $actions) { $submits = array(); # Use loop to keep requested order foreach($actions as $action) { switch($action) { # Deactivate case 'activate': if ($module['root_writable']) { $submits[] = ''; } break; # Activate case 'deactivate': if ($module['root_writable']) { $submits[] = ''; } break; # Delete case 'delete': if ($this->isPathDeletable($module['root'])) { $submits[] = ''; } break; # Install (from store) case 'install': if ($this->path_writable) { $submits[] = ''; } break; # Update (from store) case 'update': if ($this->path_writable) { $submits[] = ''; } break; } } return $submits; } public function doActions($prefix) { if (empty($_POST) || !empty($_REQUEST['conf']) || !$this->core->auth->isSuperAdmin() || !$this->isPathWritable()) { return null; } # List actions if (!empty($_POST['module'])) { $id = $_POST['module']; if (!empty($_POST['activate'])) { $enabled = $this->modules->getDisabledModules(); if (!isset($enabled[$id])) { throw new Exception(__('No such module.')); } # --BEHAVIOR-- moduleBeforeActivate $this->core->callBehavior($prefix.'BeforeActivate', $id); $this->modules->activateModule($id); # --BEHAVIOR-- moduleAfterActivate $this->core->callBehavior($prefix.'AfterActivate', $id); dcPage::addSuccessNotice(__('Module has been successfully activated.')); http::redirect($this->getPageURL()); } elseif (!empty($_POST['deactivate'])) { if (!$this->modules->moduleExists($id)) { throw new Exception(__('No such module.')); } $module = $this->modules->getModules($id); $module['id'] = $id; if (!$module['root_writable']) { throw new Exception(__('You don\'t have permissions to deactivate this module.')); } # --BEHAVIOR-- moduleBeforeDeactivate $this->core->callBehavior($prefix.'BeforeDeactivate', $module); $this->modules->deactivateModule($id); # --BEHAVIOR-- moduleAfterDeactivate $this->core->callBehavior($prefix.'AfterDeactivate', $module); dcPage::addSuccessNotice(__('Module has been successfully deactivated.')); http::redirect($this->getPageURL()); } elseif (!empty($_POST['delete'])) { $disabled = $this->modules->getDisabledModules(); if (!isset($disabled[$id])) { if (!$this->modules->moduleExists($id)) { throw new Exception(__('No such module.')); } $module = $this->modules->getModules($id); $module['id'] = $id; if (!$this->isPathDeletable($module['root'])) { throw new Exception(__("You don't have permissions to delete this module.")); } # --BEHAVIOR-- moduleBeforeDelete $this->core->callBehavior($prefix.'BeforeDelete', $module); $this->modules->deleteModule($id); # --BEHAVIOR-- moduleAfterDelete $this->core->callBehavior($prefix.'AfterDelete', $module); } else { $this->modules->deleteModule($id, true); } dcPage::addSuccessNotice(__('Module has been successfully deleted.')); http::redirect($this->getPageURL()); } elseif (!empty($_POST['install'])) { $updated = $this->store->get(); if (!isset($updated[$id])) { throw new Exception(__('No such module.')); } $module = $updated[$id]; $module['id'] = $id; $dest = $this->getPath().'/'.basename($module['file']); # --BEHAVIOR-- moduleBeforeAdd $this->core->callBehavior($prefix.'BeforeAdd', $module); $ret_code = $this->store->process($module['file'], $dest); # --BEHAVIOR-- moduleAfterAdd $this->core->callBehavior($prefix.'AfterAdd', $module); dcPage::addSuccessNotice($ret_code == 2 ? __('Module has been successfully updated.') : __('Module has been successfully installed.') ); http::redirect($this->getPageURL()); } elseif (!empty($_POST['update'])) { $updated = $store->get(); if (!isset($updated[$id])) { throw new Exception(__('No such module.')); } if (!$this->modules->moduleExists($id)) { throw new Exception(__('No such module.')); } $module = $updated[$id]; $module['id'] = $id; if (!self::$allow_multi_install) { $dest = $module['root'].'/../'.basename($module['file']); } else { $dest = $this->getPath().'/'.basename($module['file']); if ($module['root'] != $dest) { @file_put_contents($module['root'].'/_disabled', ''); } } # --BEHAVIOR-- moduleBeforeUpdate $this->core->callBehavior($prefix.'BeforeUpdate', $module); $this->store->process($module['file'], $dest); # --BEHAVIOR-- moduleAfterUpdate $this->core->callBehavior($prefix.'AfterUpdate', $module); dcPage::addSuccessNotice(__('Module has been successfully updated.')); http::redirect($this->getPageURL()); } } # Manual actions elseif (!empty($_POST['upload_pkg']) && !empty($_FILES['pkg_file']) || !empty($_POST['fetch_pkg']) && !empty($_POST['pkg_url'])) { if (empty($_POST['your_pwd']) || !$this->core->auth->checkPassword(crypt::hmac(DC_MASTER_KEY, $_POST['your_pwd']))) { throw new Exception(__('Password verification failed')); } if (!empty($_POST['upload_pkg'])) { files::uploadStatus($_FILES['pkg_file']); $dest = $this->getPath().'/'.$_FILES['pkg_file']['name']; if (!move_uploaded_file($_FILES['pkg_file']['tmp_name'], $dest)) { throw new Exception(__('Unable to move uploaded file.')); } } else { $url = urldecode($_POST['pkg_url']); $dest = $this->getPath().'/'.basename($url); $this->store->download($url, $dest); } # --BEHAVIOR-- moduleBeforeAdd $this->core->callBehavior($prefix.'BeforeAdd', null); $ret_code = $this->store->install($dest); # --BEHAVIOR-- moduleAfterAdd $this->core->callBehavior($prefix.'AfterAdd', null); dcPage::addSuccessNotice($ret_code == 2 ? __('Module has been successfully updated.') : __('Module has been successfully installed.') ); http::redirect($this->getPageURL().'#'.$prefix); } return null; } public function displayManualForm() { if (!$this->core->auth->isSuperAdmin() || !$this->isPathWritable()) { return null; } # 'Upload module' form echo '
    '. '

    '.__('Upload a zip file').'

    '. '

    '. '

    '. '

    '. form::password(array('your_pwd','your_pwd1'),20,255).'

    '. '

    '. form::hidden(array('tab'), $this->getPageTab()). $this->core->formNonce().'

    '. '
    '; # 'Fetch module' form echo '
    '. '

    '.__('Download a zip file').'

    '. '

    '. form::field(array('pkg_url','pkg_url'),40,255).'

    '. '

    '. form::password(array('your_pwd','your_pwd2'),20,255).'

    '. '

    '. form::hidden(array('tab'), $this->getPageTab()). $this->core->formNonce().'

    '. '
    '; } /** * * We need to get configuration content in three steps * and out of this class to keep backward compatibility. * * if ($xxx->setConfigurationFile()) { * include $xxx->getConfigurationFile(); * } * $xxx->setConfigurationContent(); * ... [put here page headers and other stuff] * $xxx->getConfigurationContent(); * */ public function setConfigurationFile($id=null) { if (empty($_REQUEST['conf']) || empty($_REQUEST['module']) && !$id) { return false; } if (!empty($_REQUEST['module']) && empty($id)) { $id = $_REQUEST['module']; } if (!$this->modules->moduleExists($id)) { $core->error->add(__('Unknow module ID')); return false; } $module = $this->modules->getModules($id); $module = self::parseModuleInfo($id, $module); $file = path::real($module['root'].'/_config.php'); if (!file_exists($file)) { $core->error->add(__('This module has no configuration file.')); return false; } $this->config_module = $module; $this->config_file = $file; $this->config_content = ''; if (!defined('DC_CONTEXT_MODULE')) { define('DC_CONTEXT_MODULE', true); } return true; } public function getConfigurationFile() { if (!$this->config_file) { return null; } ob_start(); return $this->config_file; } public function setConfigurationContent() { if ($this->config_file) { $this->config_content = ob_get_contents(); } ob_end_clean(); return !empty($this->file_content); } public function getConfigurationContent() { if (!$this->config_file) { return null; } if (!$this->config_module['standalone_config']) { echo '
    '. '

    '.sprintf(__('Configure plugin "%s"'), html::escapeHTML($this->config_module['name'])).'

    '. '

    '.__('Back').'

    '; } echo $this->config_content; if (!$this->config_module['standalone_config']) { echo '

    '. form::hidden('module', $this->config_module['id']). $this->core->formNonce().'

    '. '
    '; } return true; } public static function sanitizeString($str) { return preg_replace('/[^A-Za-z0-9\@\#+_-]/', '', strtolower($str)); } } class adminThemesList extends adminModulesList { protected $page_url = 'blog_theme.php'; public function displayModulesList($cols=array('name', 'config', 'version', 'desc'), $actions=array(), $nav_limit=false) { echo '
    '; $sort_field = $this->getSortQuery(); # Sort modules by id $modules = $this->getSearchQuery() === null ? self::sortModules($this->data, $sort_field, $this->sort_asc) : $this->data; $res = ''; $count = 0; foreach ($modules as $id => $module) { # Show only requested modules if ($nav_limit && $this->getSearchQuery() === null) { $char = substr($module[$sort_field], 0, 1); if (!in_array($char, $this->nav_list)) { $char = $this->nav_special; } if ($this->getNavQuery() != $char) { continue; } } $current = $this->core->blog->settings->system->theme == $id; $distrib = self::isDistributedModule($id) ? ' dc-box' : ''; $theme_url = preg_match('#^http(s)?://#', $this->core->blog->settings->system->themes_url) ? http::concatURL($this->core->blog->settings->system->themes_url, '/'.$id) : http::concatURL($this->core->blog->url, $this->core->blog->settings->system->themes_url.'/'.$id); $has_conf = file_exists(path::real($this->core->blog->themes_path.'/'.$id).'/_config.php'); $has_css = file_exists(path::real($this->core->blog->themes_path.'/'.$id).'/style.css'); $parent = $module['parent']; $has_parent = !empty($module['parent']); if ($has_parent) { $is_parent_present = $this->modules->moduleExists($parent); } $line = '
    '; if (in_array('name', $cols)) { $line .= '

    '.html::escapeHTML($module['name']).'

    '; } if (in_array('sshot', $cols)) { # Screenshot from url if (preg_match('#^http(s)?://#', $module['sshot'])) { $sshot = $module['sshot']; } # Screenshot from installed module elseif (file_exists($this->core->blog->themes_path.'/'.$id.'/screenshot.jpg')) { $sshot = $this->getPageURL('shot='.rawurlencode($id)); } # Default screenshot else { $sshot = 'images/noscreenshot.png'; } $line .= '
    '.
				sprintf(__('%s screenshot.'), html::escapeHTML($module['name'])).'
    '; } $line .= '
    '. '

    '; if (in_array('desc', $cols)) { $line .= ''.html::escapeHTML($module['desc']).' '; } if (in_array('author', $cols)) { $line .= ''.sprintf(__('by %s'),html::escapeHTML($module['author'])).' '; } if (in_array('version', $cols)) { $line .= ''.sprintf(__('version %s'),html::escapeHTML($module['version'])).' '; } if (in_array('parent', $cols) && $has_parent) { if ($is_parent_present) { $line .= ''.sprintf(__('(built on "%s")'),html::escapeHTML($parent)).' '; } else { $line .= ''.sprintf(__('(requires "%s")'),html::escapeHTML($parent)).' '; } } if (in_array('version', $cols)) { $line .= ''.sprintf(__('version %s'),html::escapeHTML($module['version'])).' '; } $has_details = in_array('details', $cols) && !empty($module['details']); $has_support = in_array('support', $cols) && !empty($module['support']); if ($has_details || $has_support) { $line .= ''.__('Help:').' '; if ($has_details) { $line .= ''.__('Details').''; } if ($has_support) { $line .= ' - '.__('Support').''; } $line .= ''; } $line .= '

    '. '
    '; $line .= '
    '; # _GET actions if ($current && $has_css) { $line .= '

    '.__('View stylesheet').'

    '; } if ($current && $has_conf) { $line .= '

    '.__('Configure theme').'

    '; } # Plugins actions if ($current) { # --BEHAVIOR-- adminCurrentThemeDetails $line .= $this->core->callBehavior('adminCurrentThemeDetails', $this->core, $id, $module); } # _POST actions if (!empty($actions)) { $line .= '
    '. '
    '. $this->core->formNonce(). form::hidden(array('module'), html::escapeHTML($id)). form::hidden(array('tab'), $this->page_tab). implode(' ', $this->getActions($id, $module, $actions)). '
    '. '
    '; } $line .= '
    '; $line .= '
    '; $count++; $res = $current ? $line.$res : $res.$line; } echo $res. '
    '; if(!$count && $this->getSearchQuery() === null) { echo '

    '.__('No module matches your search.').'

    '; } } protected function getActions($id, $module, $actions) { $submits = array(); $this->core->blog->settings->addNamespace('system'); if ($id != $this->core->blog->settings->system->theme) { # Select theme to use on curent blog if (in_array('select', $actions) && $this->path_writable) { $submits[] = ''; } } return array_merge( $submits, parent::getActions($id, $module, $actions) ); } public function doActions($prefix) { if (!empty($_POST) && empty($_REQUEST['conf']) && $this->isPathWritable()) { # Select theme to use on curent blog if (!empty($_POST['module']) && !empty($_POST['select'])) { $id = $_POST['module']; if (!$this->modules->moduleExists($id)) { throw new Exception(__('No such module.')); } $this->core->blog->settings->addNamespace('system'); $this->core->blog->settings->system->put('theme',$id); $this->core->blog->triggerBlog(); dcPage::addSuccessNotice(__('Module has been successfully selected.')); http::redirect($this->getPageURL().'#themes'); } } return parent::doActions($prefix); } }