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
'
';
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[] = ' '.$char.' ';
}
}
# Parse navigation menu
echo '';
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
''.
'
'.
''.html::escapeHTML(__('Modules list')).' ';
if (in_array('name', $cols)) {
echo
''.__('Name').' ';
}
if (in_array('version', $cols)) {
echo
''.__('Version').' ';
}
if (in_array('current_version', $cols)) {
echo
''.__('Current version').' ';
}
if (in_array('desc', $cols)) {
echo
''.__('Details').' ';
}
if (in_array('distrib', $cols)) {
echo ' ';
}
if (!empty($actions) && $this->core->auth->isSuperAdmin()) {
echo
''.__('Action').' ';
}
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
''.sprintf(
' ',
html::escapeHTML($id), file_exists($module['root'].'/icon.png') ? 'index.php?pf='.$id.'/icon.png' : 'images/module.png'
).' ';
}
# Link to config file
$config = in_array('config', $cols) && !empty($module['root']) && file_exists(path::real($module['root'].'/_config.php'));
echo
''.($config ?
''.html::escapeHTML($module['name']).' ' :
html::escapeHTML($module['name'])
).' ';
if (in_array('version', $cols)) {
echo
''.html::escapeHTML($module['version']).' ';
}
if (in_array('current_version', $cols)) {
echo
''.html::escapeHTML($module['current_version']).' ';
}
if (in_array('desc', $cols)) {
echo
''.html::escapeHTML($module['desc']).' ';
}
if (in_array('distrib', $cols)) {
echo
''.(self::isDistributedModule($id) ?
' '
: '').' ';
}
if (!empty($actions) && $this->core->auth->isSuperAdmin()) {
$buttons = $this->getActions($id, $module, $actions);
echo
''.
''.
' ';
}
echo
' ';
$count++;
}
echo
'
';
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
'';
# 'Fetch module' form
echo
'';
}
/**
*
* 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
'';
}
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 .=
'
';
}
$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 .=
'
';
}
$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);
}
}