475 lines
12 KiB
PHP
475 lines
12 KiB
PHP
<?php
|
|
/*
|
|
* Gallery - a web based photo album viewer and editor
|
|
* Copyright (C) 2000-2007 Bharat Mediratta
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or (at
|
|
* your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
/**
|
|
* This view lets you make very simple callbacks to the framework to get very specific data.
|
|
* Eventually this will probably get refactored into a much more sophisticated framework.
|
|
*
|
|
* @package GalleryCore
|
|
* @subpackage UserInterface
|
|
* @author Bharat Mediratta <bharat@menalto.com>
|
|
* @version $Revision: 15513 $
|
|
*/
|
|
class PluginCallbackView extends GalleryView {
|
|
/**
|
|
* @var object GalleryRepository
|
|
* @access private
|
|
*/
|
|
var $_repository;
|
|
|
|
/**
|
|
* @see GalleryView::isImmediate
|
|
*/
|
|
function isImmediate() {
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @see GalleryView::isControllerLike
|
|
*/
|
|
function isControllerLike() {
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @see GalleryView::renderImmediate
|
|
*/
|
|
function renderImmediate($status, $error) {
|
|
global $gallery;
|
|
$session =& $gallery->getSession();
|
|
$storage =& $gallery->getStorage();
|
|
|
|
$command = GalleryUtilities::getRequestVariables('command');
|
|
if (!headers_sent()) {
|
|
header("Content-type: text/plain; charset=UTF-8");
|
|
}
|
|
|
|
$result = array();
|
|
list ($ret, $beforeStates) = $this->getPluginStates();
|
|
if ($ret) {
|
|
$result['status'] = 'error';
|
|
$storage->rollbackTransaction(); /* ignore errors here */
|
|
$ret->putInSession();
|
|
}
|
|
|
|
if (!$ret) {
|
|
$ret = $this->handleCallback($command, $result);
|
|
if ($ret) {
|
|
$result['status'] = 'error';
|
|
$storage->rollbackTransaction(); /* ignore errors here */
|
|
$ret->putInSession();
|
|
} else {
|
|
/* Make sure this change is isolated from any potential failures we get below. */
|
|
$ret = $storage->checkPoint();
|
|
if ($ret) {
|
|
$result['status'] = 'error';
|
|
$ret->putInSession();
|
|
}
|
|
}
|
|
|
|
if ($result['status'] == 'redirect') {
|
|
$urlGenerator =& $gallery->getUrlGenerator();
|
|
$result['redirect'] =
|
|
$urlGenerator->generateUrl($result['redirect'],
|
|
array('htmlEntities' => 0, 'forceServerRelativeUrl' => 1));
|
|
}
|
|
}
|
|
|
|
if (!$ret) {
|
|
list ($ret, $afterStates) = $this->getPluginStates();
|
|
if ($ret) {
|
|
$result['status'] = 'error';
|
|
$storage->rollbackTransaction(); /* ignore errors here */
|
|
$ret->putInSession();
|
|
} else {
|
|
$result = array_merge(
|
|
$result, $this->calculateStateChanges($beforeStates, $afterStates));
|
|
}
|
|
}
|
|
|
|
GalleryCoreApi::requireOnce('lib/JSON/JSON.php');
|
|
$json = new Services_JSON();
|
|
print $json->encode($result);
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Given two sets of states, figure out what's changed from before to after.
|
|
*
|
|
* @param array $beforeStates (moduleId => state, ...)
|
|
* @param array $afterStates (moduleId => state, ...)
|
|
* @return array changed states (moduleId => state, ...)
|
|
* @static
|
|
*/
|
|
function calculateStateChanges($beforeStates, $afterStates) {
|
|
$states = array();
|
|
$deleted = array();
|
|
foreach (array('module', 'theme') as $type) {
|
|
foreach ($beforeStates[$type] as $moduleId => $state) {
|
|
if (!isset($afterStates[$type][$moduleId])) {
|
|
$deleted[$type][$moduleId] = 1;
|
|
} else if ($afterStates[$type][$moduleId] != $state) {
|
|
$states[$type][$moduleId] = $afterStates[$type][$moduleId];
|
|
}
|
|
}
|
|
}
|
|
return array('states' => $states, 'deleted' => $deleted);
|
|
}
|
|
|
|
/**
|
|
* Handle the specific callback, and store its result in the given output array.
|
|
*
|
|
* @param string $command (eg. "installModule")
|
|
* @param array $result the location for result data to be sent back to the browser
|
|
* @return object GalleryStatus a status code
|
|
* @static
|
|
*/
|
|
function handleCallback($command, &$result) {
|
|
global $gallery;
|
|
$platform =& $gallery->getPlatform();
|
|
|
|
$ret = GalleryCoreApi::assertUserIsSiteAdministrator();
|
|
if ($ret) {
|
|
return $ret;
|
|
}
|
|
|
|
$result = array();
|
|
list ($pluginType, $pluginId) =
|
|
GalleryUtilities::getRequestVariables('pluginType', 'pluginId');
|
|
|
|
list ($ret, $plugin) = GalleryCoreApi::loadPlugin($pluginType, $pluginId, true);
|
|
if ($ret) {
|
|
return $ret;
|
|
}
|
|
|
|
list ($ret, $isActive) = $plugin->isActive();
|
|
if ($ret) {
|
|
return $ret;
|
|
}
|
|
|
|
switch($command) {
|
|
case 'activate':
|
|
|
|
if ($pluginType == 'module') {
|
|
list ($ret, $needsConfiguration) = $plugin->needsConfiguration();
|
|
if ($ret) {
|
|
return $ret;
|
|
}
|
|
} else {
|
|
/* Themes don't need configuration */
|
|
$needsConfiguration = false;
|
|
}
|
|
|
|
if ($isActive || $needsConfiguration) {
|
|
/* UI shouldn't let us come here anyway */
|
|
$result['status'] = 'invalid';
|
|
return null;
|
|
}
|
|
|
|
list ($ret, $redirect) = $plugin->activate();
|
|
if ($ret) {
|
|
return $ret;
|
|
}
|
|
|
|
if ($redirect) {
|
|
$result['status'] = 'redirect';
|
|
$result['redirect'] = $redirect;
|
|
} else {
|
|
$result['status'] = 'success';
|
|
}
|
|
break;
|
|
|
|
case 'deactivate':
|
|
if (!$isActive) {
|
|
/* UI shouldn't let us come here anyway */
|
|
$result['status'] = 'invalid';
|
|
return null;
|
|
}
|
|
|
|
if ($pluginType == 'theme') {
|
|
list ($ret, $defaultThemeId) = GalleryCoreApi::getPluginParameter(
|
|
'module', 'core', 'default.theme');
|
|
if ($ret) {
|
|
return $ret;
|
|
}
|
|
|
|
if ($plugin->getId() == $defaultThemeId) {
|
|
/* UI shouldn't let us come here anyway */
|
|
$result['status'] = 'invalid';
|
|
}
|
|
}
|
|
|
|
if (empty($result['status'])) {
|
|
list ($ret, $redirect) = $plugin->deactivate();
|
|
if ($ret) {
|
|
return $ret;
|
|
}
|
|
|
|
if ($redirect) {
|
|
$result['status'] = 'redirect';
|
|
$result['redirect'] = $redirect;
|
|
} else {
|
|
$result['status'] = 'success';
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'upgrade':
|
|
case 'install':
|
|
$ret = $plugin->installOrUpgrade();
|
|
if ($ret) {
|
|
return $ret;
|
|
}
|
|
|
|
if ($pluginType == 'module') {
|
|
list ($ret, $autoConfigured) = $plugin->autoConfigure();
|
|
if ($ret) {
|
|
return $ret;
|
|
}
|
|
} else {
|
|
/* Themes don't need this step */
|
|
$autoConfigured = true;
|
|
}
|
|
|
|
|
|
if ($autoConfigured) {
|
|
list ($ret, $redirect) = $plugin->activate();
|
|
if ($ret) {
|
|
if ($ret->getErrorCode() & ERROR_CONFIGURATION_REQUIRED) {
|
|
/*
|
|
* Some modules don't override autoConfigure which defaults to success.
|
|
* Show the "Modules needs configuration" message.
|
|
*/
|
|
} else {
|
|
return $ret;
|
|
}
|
|
}
|
|
|
|
if ($redirect) {
|
|
$result['status'] = 'redirect';
|
|
$result['redirect'] = $redirect;
|
|
} else {
|
|
$result['status'] = 'success';
|
|
}
|
|
} else {
|
|
$result['status'] = 'success';
|
|
}
|
|
|
|
if (empty($this->_repository)) {
|
|
GalleryCoreApi::requireOnce('modules/core/classes/GalleryRepository.class');
|
|
$this->_repository = new GalleryRepository();
|
|
$this->_repository->init(''); /* bogus repository, but that should be ok */
|
|
}
|
|
|
|
$ret = $this->_repository->scanPlugin($pluginType, $pluginId);
|
|
if ($ret && !($ret->getErrorCode() & ERROR_STORAGE_FAILURE)) {
|
|
/*
|
|
* Something is wrong with this plugin. Maybe it's a 3rd party plugin w/o MANIFEST
|
|
* file, maybe it has no revisions in the po files, maybe the module.inc is foobar.
|
|
* Just log and ignore it.
|
|
*/
|
|
if ($gallery->getDebug()) {
|
|
$gallery->debug_r($ret);
|
|
}
|
|
} else if ($ret) {
|
|
return $ret;
|
|
}
|
|
|
|
break;
|
|
|
|
case 'uninstall':
|
|
if ($isActive) {
|
|
list ($ret, $redirect) = $plugin->deactivate();
|
|
if ($ret) {
|
|
return $ret;
|
|
}
|
|
} else {
|
|
$redirect = false;
|
|
}
|
|
|
|
if ($redirect) {
|
|
$result['status'] = 'redirect';
|
|
$results['redirect'] = $redirect;
|
|
} else {
|
|
$ret = $plugin->uninstall();
|
|
if ($ret) {
|
|
return $ret;
|
|
}
|
|
$result['status'] = 'success';
|
|
}
|
|
break;
|
|
|
|
case 'delete':
|
|
if ($isActive) {
|
|
list ($ret, $redirect) = $plugin->deactivate();
|
|
if ($ret) {
|
|
return $ret;
|
|
}
|
|
} else {
|
|
$redirect = false;
|
|
}
|
|
|
|
if ($redirect) {
|
|
$result['status'] = 'redirect';
|
|
$results['redirect'] = $redirect;
|
|
} else {
|
|
$ret = $plugin->uninstall();
|
|
if ($ret) {
|
|
return $ret;
|
|
}
|
|
|
|
$path = sprintf(
|
|
"%s/%ss/%s", dirname(dirname(dirname(__FILE__))), $pluginType, $pluginId);
|
|
$success = @$platform->recursiveRmdir($path);
|
|
if (!$success) {
|
|
$result['status'] = 'fail';
|
|
} else {
|
|
$ret = GalleryCoreApi::removeMapEntry(
|
|
'GalleryPluginPackageMap',
|
|
array('pluginType' => $pluginType, 'pluginId' => $pluginId));
|
|
if ($ret) {
|
|
return $ret;
|
|
}
|
|
|
|
$result['status'] = 'success';
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'configure':
|
|
$result['status'] = 'redirect';
|
|
$result['redirect'] = array('view' => 'core.SiteAdmin',
|
|
'subView' => $plugin->getConfigurationView());
|
|
break;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get the state ('active', 'inactive', 'uninstalled', etc) of all modules
|
|
*
|
|
* @return array object GalleryStatus a status code
|
|
* array(moduleId => state, ...)
|
|
* @static
|
|
*/
|
|
function getPluginStates() {
|
|
$states = array();
|
|
|
|
foreach (array('module', 'theme') as $type) {
|
|
list ($ret, $pluginStatus) = GalleryCoreApi::fetchPluginStatus($type, true);
|
|
if ($ret) {
|
|
return array($ret, null);
|
|
}
|
|
foreach ($pluginStatus as $pluginId => $status) {
|
|
list ($ret, $plugin) = GalleryCoreApi::loadPlugin($type, $pluginId, true);
|
|
if ($ret) {
|
|
return array($ret, null);
|
|
}
|
|
|
|
list ($ret, $states[$type][$pluginId]) =
|
|
$this->getPluginState($type, $plugin, $status);
|
|
if ($ret) {
|
|
return array($ret, null);
|
|
}
|
|
}
|
|
}
|
|
|
|
return array(null, $states);
|
|
}
|
|
|
|
/**
|
|
* Get the state ('active', 'inactive', 'uninstalled', etc) of a given module
|
|
*
|
|
* @param string $type ('module' or 'theme')
|
|
* @param object GalleryPlugin $plugin
|
|
* @param array $status status of the plugin (from GalleryCoreApi::fetchPluginStatus)
|
|
* @return array object GalleryStatus a status code
|
|
* string a state
|
|
* @static
|
|
*/
|
|
function getPluginState($type, $plugin, $status) {
|
|
if ($type == 'module' && $plugin->getId() == 'core') {
|
|
return array(null, 'active');
|
|
}
|
|
|
|
$coreApiCompatible = GalleryUtilities::isCompatibleWithApi(
|
|
$plugin->getRequiredCoreApi(), GalleryCoreApi::getApiVersion());
|
|
|
|
/* TODO: refactor this into type specific wrapper methods around getPluginState() */
|
|
switch ($type) {
|
|
case 'module':
|
|
$pluginApiCompatible = GalleryUtilities::isCompatibleWithApi(
|
|
$plugin->getRequiredModuleApi(), GalleryModule::getApiVersion());
|
|
break;
|
|
|
|
case 'theme':
|
|
$pluginApiCompatible = GalleryUtilities::isCompatibleWithApi(
|
|
$plugin->getRequiredThemeApi(), GalleryTheme::getApiVersion());
|
|
break;
|
|
}
|
|
|
|
if ($coreApiCompatible && $pluginApiCompatible) {
|
|
if (empty($status['active'])) {
|
|
$version = $status['version'];
|
|
$state = 'inactive';
|
|
|
|
/*
|
|
* If the database versions doesn't match the module
|
|
* version, we need to get the user to install the module.
|
|
*/
|
|
if ($version != $plugin->getVersion()) {
|
|
if (empty($version)) {
|
|
$state = 'uninstalled';
|
|
} else {
|
|
$state = 'unupgraded';
|
|
}
|
|
} else {
|
|
if ($type == 'module') {
|
|
/*
|
|
* The versions match, but the module can still demand
|
|
* to be configured before being activated.
|
|
*/
|
|
list ($ret, $needsConfig) = $plugin->needsConfiguration();
|
|
if ($ret) {
|
|
return array($ret, null);
|
|
}
|
|
} else {
|
|
$needsConfig = false;
|
|
}
|
|
|
|
if ($needsConfig) {
|
|
$state = 'unconfigured';
|
|
} else {
|
|
$state = 'inactive';
|
|
}
|
|
}
|
|
} else {
|
|
$state = 'active';
|
|
}
|
|
} else {
|
|
$state = 'incompatible';
|
|
}
|
|
|
|
return array(null, $state);
|
|
}
|
|
}
|
|
?>
|