1 */ // Define the well-known file name for filter configuration data. define('PLUGIN_FILTER_DATAFILE', 'filterConfig.xml'); class PKPPlugin { /** @var $pluginPath string Path name to files for this plugin */ var $pluginPath; /** @var $pluginCategory string Category name this plugin is registered to*/ var $pluginCategory; /** * Constructor */ function PKPPlugin() { } /* * Public Plugin API (Registration and Initialization) */ /** * Load and initialize the plug-in and register plugin hooks. * * For backwards compatibility this method will be called whenever * the plug-in's category is being loaded. If, however, registerOn() * returns an array then this method will only be called when * the plug-in is enabled and an entry in the result set of * registerOn() matches the current request operation. An empty array * matches all request operations. * * @param $category String Name of category plugin was registered to * @param $path String The path the plugin was found in * @return boolean True iff plugin registered successfully; if false, * the plugin will not be executed. */ function register($category, $path) { $this->pluginPath = $path; $this->pluginCategory = $category; if ($this->getInstallSchemaFile()) { HookRegistry::register ('Installer::postInstall', array(&$this, 'updateSchema')); } if ($this->getInstallSitePluginSettingsFile()) { HookRegistry::register ('Installer::postInstall', array(&$this, 'installSiteSettings')); } if ($this->getInstallEmailTemplatesFile()) { HookRegistry::register ('Installer::postInstall', array(&$this, 'installEmailTemplates')); } if ($this->getInstallEmailTemplateDataFile()) { HookRegistry::register ('Installer::postInstall', array(&$this, 'installEmailTemplateData')); HookRegistry::register ('PKPLocale::installLocale', array(&$this, 'installLocale')); } if ($this->getInstallDataFile()) { HookRegistry::register ('Installer::postInstall', array(&$this, 'installData')); } if ($this->getContextSpecificPluginSettingsFile()) { HookRegistry::register ($this->_getContextSpecificInstallationHook(), array(&$this, 'installContextSpecificSettings')); } HookRegistry::register ('Installer::postInstall', array(&$this, 'installFilters')); return true; } /* * Protected methods (may be overridden by custom plugins) */ // // Plugin Display // /** * Get the name of this plugin. The name must be unique within * its category, and should be suitable for part of a filename * (ie short, no spaces, and no dependencies on cases being unique). * * @return string name of plugin */ function getName() { assert(false); } /** * Get the display name for this plugin. * * @return string */ function getDisplayName() { assert(false); } /** * Get a description of this plugin. * * @return string */ function getDescription() { assert(false); } // // Plugin Behavior and Management // /** * Return a number indicating the sequence in which this plugin * should be registered compared to others of its category. * Higher = later. * * @return integer */ function getSeq() { return 0; } /** * Site-wide plugins should override this function to return true. * * @return boolean */ function isSitePlugin() { return false; } /** * Get a list of management actions in the form of a page => value pair. * The management actions from this list are passed to the manage() function * when called. * * @return array */ function getManagementVerbs() { return null; } /** * Perform a management function. * * @param $verb string * @param $args array * @param $message string If a message is returned from this by-ref argument then * it will be displayed as a notification if (and only if) the method returns * false. * @param $messageParams array additional notification settings * @param $request Request * @return boolean will redirect to the plugin category page if false, otherwise * will remain on the same page */ function manage($verb, $args, &$message, &$messageParams, $request = null) { return false; } /** * Determine whether or not this plugin should be hidden from the * management interface. Useful in the case of derivative plugins, * i.e. when a generic plugin registers a feed plugin. * * @return boolean */ function getHideManagement() { return false; } // // Plugin Installation // /** * Get the filename of the ADODB schema for this plugin. * Subclasses using SQL tables should override this. * * @return string */ function getInstallSchemaFile() { return null; } /** * Get the filename of the install data for this plugin. * Subclasses using SQL tables should override this. * * @return string|array|null one or more data files to be installed. */ function getInstallDataFile() { return null; } /** * Get the filename of the settings data for this plugin to install * when the system is installed (i.e. site-level plugin settings). * Subclasses using default settings should override this. * * @return string */ function getInstallSitePluginSettingsFile() { return null; } /** * Get the filename of the settings data for this plugin to install * when a new application context (e.g. journal, conference or press) * is installed. * * Subclasses using default settings should override this. * * @return string */ function getContextSpecificPluginSettingsFile() { return null; } /** * Get the filename of the email templates for this plugin. * Subclasses using email templates should override this. * * @return string */ function getInstallEmailTemplatesFile() { return null; } /** * Get the filename of the email template data for this plugin. * Subclasses using email templates should override this. * * @return string */ function getInstallEmailTemplateDataFile() { return null; } /** * Get the filename(s) of the filter configuration data for * this plugin. Subclasses using filters can override this. * * The default implementation establishes "well known" locations * for the filter configuration. If you keep your files in these * locations then there's no need to override this method. * * @return string|array one or more file locations. */ function getInstallFilterConfigFiles() { // Construct the well-known filter configuration file names. $filterConfigFile = $this->getPluginPath().'/filter/'.PLUGIN_FILTER_DATAFILE; $filterConfigFiles = array( './lib/pkp/'.$filterConfigFile, './'.$filterConfigFile ); return $filterConfigFiles; } /* * Protected helper methods (can be used by custom plugins but * should not be overridden by custom plugins) */ /** * Get the name of the category this plugin is registered to. * @return String category */ function getCategory() { return $this->pluginCategory; } /** * Get the path this plugin's files are located in. * @return String pathname */ function getPluginPath() { return $this->pluginPath; } /** * Return the canonical template path of this plug-in * * @return string */ function getTemplatePath() { $basePath = dirname(dirname(dirname(dirname(dirname(__FILE__))))); return "file:$basePath/" . $this->getPluginPath() . '/'; } /** * Load locale data for this plugin. * * @param $locale string * @return boolean */ function addLocaleData($locale = null) { if ($locale == '') $locale = AppLocale::getLocale(); $localeFilenames = $this->getLocaleFilename($locale); if ($localeFilenames) { if (is_scalar($localeFilenames)) $localeFilenames = array($localeFilenames); foreach($localeFilenames as $localeFilename) { AppLocale::registerLocaleFile($locale, $localeFilename); } return true; } return false; } /** * Add help data for this plugin. * * @param $locale string * @return boolean */ function addHelpData($locale = null) { if ($locale == '') $locale = AppLocale::getLocale(); import('classes.help.Help'); $help =& Help::getHelp(); import('lib.pkp.classes.help.PluginHelpMappingFile'); $pluginHelpMapping = new PluginHelpMappingFile($this); $help->addMappingFile($pluginHelpMapping); return true; } /** * Retrieve a plugin setting within the given context * * @param $context array an array of context ids * @param $name the setting name */ function getContextSpecificSetting($context, $name) { if (!Config::getVar('general', 'installed')) return null; // Check that the context has the correct depth $application =& PKPApplication::getApplication(); assert(is_array($context) && $application->getContextDepth() == count($context)); // Construct the argument list and call the plug-in settings DAO $arguments = $context; $arguments[] = $this->getName(); $arguments[] = $name; $pluginSettingsDao =& DAORegistry::getDAO('PluginSettingsDAO'); return call_user_func_array(array(&$pluginSettingsDao, 'getSetting'), $arguments); } /** * Update a plugin setting within the given context. * * @param $context array an array of context ids * @param $name the setting name * @param $value mixed * @param $type string optional */ function updateContextSpecificSetting($context, $name, $value, $type = null) { // Check that the context has the correct depth $application =& PKPApplication::getApplication(); assert(is_array($context) && $application->getContextDepth() == count($context)); // Construct the argument list and call the plug-in settings DAO $arguments = $context; $arguments[] = $this->getName(); $arguments[] = $name; $arguments[] = $value; $arguments[] = $type; $pluginSettingsDao =& DAORegistry::getDAO('PluginSettingsDAO'); call_user_func_array(array(&$pluginSettingsDao, 'updateSetting'), $arguments); } /** * Load a PHP file from this plugin's installation directory. * * @param $class string */ function import($class) { require_once($this->getPluginPath() . '/' . str_replace('.', '/', $class) . '.inc.php'); } /* * Protected helper methods (for internal use only, should not * be used by custom plug-ins) * * NB: These methods may change without notice in the future! */ /** * Generate the context for this plug-in's generic * settings. This is an array with the id of the main context * (e.g. journal, conference or press) as the first entry * and all remaining entries set to 0. If the calling * application doesn't support context then the this will * return an empty array (e.g. harvester). * * For site-wide plug-ins the context will be set to 0. * * @return array */ function getSettingMainContext() { $application =& PKPApplication::getApplication(); $contextDepth = $application->getContextDepth(); $settingContext = array(); if ($contextDepth > 0) { if ($this->isSitePlugin()) { $mainContext = null; } else { $request =& $application->getRequest(); $router =& $request->getRouter(); // Try to identify the main context (e.g. journal, conference, press), // will be null if none found. $mainContext =& $router->getContext($request, 1); } // Create the context for the setting if found if ($mainContext) $settingContext[] = $mainContext->getId(); $settingContext = array_pad($settingContext, $contextDepth, CONTEXT_ID_NONE); } return $settingContext; } /** * Get the filename for the locale data for this plugin. * * @param $locale string * @return string|array the locale file names (the scalar return value is supported for * backwards compatibility only). */ function getLocaleFilename($locale) { $masterLocale = MASTER_LOCALE; $baseLocaleFilename = $this->getPluginPath() . "/locale/$locale/locale.xml"; $baseMasterLocaleFilename = $this->getPluginPath() . "/locale/$masterLocale/locale.xml"; $libPkpFilename = "lib/pkp/$baseLocaleFilename"; $masterLibPkpFilename = "lib/pkp/$baseMasterLocaleFilename"; $filenames = array(); if (file_exists($baseMasterLocaleFilename)) $filenames[] = $baseLocaleFilename; if (file_exists($masterLibPkpFilename)) $filenames[] = $libPkpFilename; return $filenames; } /** * Get the path and filename of the help mapping file, if this * plugin includes help files. * * @return string */ function getHelpMappingFilename() { return $this->getPluginPath() . DIRECTORY_SEPARATOR . 'help.xml'; } /** * Callback used to install data files. * * @param $hookName string * @param $args array * @return boolean */ function installData($hookName, $args) { $installer =& $args[0]; $result =& $args[1]; // Treat single and multiple data files uniformly. $dataFiles = $this->getInstallDataFile(); if (is_scalar($dataFiles)) $dataFiles = array($dataFiles); // Install all data files. foreach($dataFiles as $dataFile) { $sql = $installer->dataXMLParser->parseData($dataFile); if ($sql) { $result = $installer->executeSQL($sql); } else { AppLocale::requireComponents(LOCALE_COMPONENT_PKP_INSTALLER); $installer->setError(INSTALLER_ERROR_DB, str_replace('{$file}', $this->getInstallDataFile(), __('installer.installParseDBFileError'))); $result = false; } if (!$result) return false; } return false; } /** * Callback used to install settings on system install. * * @param $hookName string * @param $args array * @return boolean */ function installSiteSettings($hookName, $args) { $installer =& $args[0]; $result =& $args[1]; // All contexts are set to zero for site-wide plug-in settings $application =& PKPApplication::getApplication(); $contextDepth = $application->getContextDepth(); if ($contextDepth >0) { $arguments = array_fill(0, $contextDepth, 0); } else { $arguments = array(); } $arguments[] = $this->getName(); $arguments[] = $this->getInstallSitePluginSettingsFile(); $pluginSettingsDao =& DAORegistry::getDAO('PluginSettingsDAO'); call_user_func_array(array(&$pluginSettingsDao, 'installSettings'), $arguments); return false; } /** * Callback used to install settings on new context * (e.g. journal, conference or press) creation. * * @param $hookName string * @param $args array * @return boolean */ function installContextSpecificSettings($hookName, $args) { // Only applications that have at least one context can // install context specific settings. $application =& PKPApplication::getApplication(); $contextDepth = $application->getContextDepth(); if ($contextDepth > 0) { $context =& $args[1]; // Make sure that this is really a new context $isNewContext = isset($args[3]) ? $args[3] : true; if (!$isNewContext) return false; // Install context specific settings $pluginSettingsDao =& DAORegistry::getDAO('PluginSettingsDAO'); switch ($contextDepth) { case 1: $pluginSettingsDao->installSettings($context->getId(), $this->getName(), $this->getContextSpecificPluginSettingsFile()); break; case 2: $pluginSettingsDao->installSettings($context->getId(), 0, $this->getName(), $this->getContextSpecificPluginSettingsFile()); break; default: // No application can have a context depth > 2 assert(false); } } return false; } /** * Callback used to install email templates. * * @param $hookName string * @param $args array * @return boolean */ function installEmailTemplates($hookName, $args) { $installer =& $args[0]; /* @var $installer Installer */ $result =& $args[1]; $emailTemplateDao =& DAORegistry::getDAO('EmailTemplateDAO'); /* @var $emailTemplateDao EmailTemplateDAO */ $sql = $emailTemplateDao->installEmailTemplates($this->getInstallEmailTemplatesFile(), true, null, true); if ($sql === false) { // The template file seems to be invalid. $installer->setError(INSTALLER_ERROR_DB, str_replace('{$file}', $this->getInstallDataFile(), __('installer.installParseEmailTemplatesFileError'))); $result = false; } else { // Are there any yet uninstalled email templates? assert(is_array($sql)); if (!empty($sql)) { // Install templates. $result = $installer->executeSQL($sql); } } return false; } /** * Callback used to install email template data. * * @param $hookName string * @param $args array * @return boolean */ function installEmailTemplateData($hookName, $args) { $installer =& $args[0]; $result =& $args[1]; $emailTemplateDao =& DAORegistry::getDAO('EmailTemplateDAO'); foreach ($installer->installedLocales as $locale) { $filename = str_replace('{$installedLocale}', $locale, $this->getInstallEmailTemplateDataFile()); if (!file_exists($filename)) continue; $sql = $emailTemplateDao->installEmailTemplateData($filename, true); if ($sql) { $result = $installer->executeSQL($sql); } else { $installer->setError(INSTALLER_ERROR_DB, str_replace('{$file}', $this->getInstallDataFile(), __('installer.installParseEmailTemplatesFileError'))); $result = false; } } return false; } /** * Callback used to install email template data on locale install. * * @param $hookName string * @param $args array * @return boolean */ function installLocale($hookName, $args) { $locale =& $args[0]; $filename = str_replace('{$installedLocale}', $locale, $this->getInstallEmailTemplateDataFile()); $emailTemplateDao =& DAORegistry::getDAO('EmailTemplateDAO'); $emailTemplateDao->installEmailTemplateData($filename); return false; } /** * Callback used to install filters. * @param $hookName string * @param $args array */ function installFilters($hookName, $args) { $installer =& $args[0]; /* @var $installer Installer */ $result =& $args[1]; /* @var $result boolean */ // Get the filter configuration file name(s). $filterConfigFiles = $this->getInstallFilterConfigFiles(); if (is_scalar($filterConfigFiles)) $filterConfigFiles = array($filterConfigFiles); // Run through the config file positions and see // whether one of these exists and needs to be installed. foreach($filterConfigFiles as $filterConfigFile) { // Is there a filter configuration? if (!file_exists($filterConfigFile)) continue; // Install the filter configuration. $result = $installer->installFilterConfig($filterConfigFile); if (!$result) { // The filter configuration file seems to be invalid. $installer->setError(INSTALLER_ERROR_DB, str_replace('{$file}', $filterConfigFile, __('installer.installParseFilterConfigFileError'))); } } // Do not stop installation. return false; } /** * Called during the install process to install the plugin schema, * if applicable. * * @param $hookName string * @param $args array * @return boolean */ function updateSchema($hookName, $args) { $installer =& $args[0]; $result =& $args[1]; $schemaXMLParser = new adoSchema($installer->dbconn); $dict =& $schemaXMLParser->dict; $dict->SetCharSet($installer->dbconn->charSet); $sql = $schemaXMLParser->parseSchema($this->getInstallSchemaFile()); if ($sql) { $result = $installer->executeSQL($sql); } else { $installer->setError(INSTALLER_ERROR_DB, str_replace('{$file}', $this->getInstallSchemaFile(), __('installer.installParseDBFileError'))); $result = false; } return false; } /** * Extend the {url ...} smarty to support plugins. * * @param $params array * @param $smarty Smarty * @return string */ function smartyPluginUrl($params, &$smarty) { $path = array($this->getCategory(), $this->getName()); if (is_array($params['path'])) { $params['path'] = array_merge($path, $params['path']); } elseif (!empty($params['path'])) { $params['path'] = array_merge($path, array($params['path'])); } else { $params['path'] = $path; } return $smarty->smartyUrl($params, $smarty); } /** * Get the current version of this plugin * * @return Version */ function getCurrentVersion() { $versionDao =& DAORegistry::getDAO('VersionDAO'); $pluginPath = $this->getPluginPath(); $product = basename($pluginPath); $category = basename(dirname($pluginPath)); $installedPlugin = $versionDao->getCurrentVersion('plugins.'.$category, $product, true); if ($installedPlugin) { return $installedPlugin; } else { return false; } } /* * Private helper methods */ /** * The application specific context installation hook. * * @return string */ function _getContextSpecificInstallationHook() { $application =& PKPApplication::getApplication(); if ($application->getContextDepth() == 0) return null; $contextList = $application->getContextList(); return ucfirst(array_shift($contextList)).'SiteSettingsForm::execute'; } } ?>