diff --git a/config/application.config.php b/config/application.config.php index 7bfbfb6a1e591ea2977b9c6f994ce9fb26f7bc78..ae09655cda561301ebbd624a2f7d6a09d672adfb 100644 --- a/config/application.config.php +++ b/config/application.config.php @@ -1,5 +1,5 @@ <?php -return array( +$config = array( 'modules' => array( 'VuFind', ), @@ -20,3 +20,7 @@ return array( ), ), ); +if (PHP_SAPI == 'cli') { + $config['modules'][] = 'VuFind\\CLI'; +} +return $config; \ No newline at end of file diff --git a/import/import-xsl.php b/import/import-xsl.php index 6a6094e8a0bd024629f10054f0b45ab2a91e3842..077eebde523f2afaf19a389e2a5aa57df57538cc 100644 --- a/import/import-xsl.php +++ b/import/import-xsl.php @@ -27,6 +27,7 @@ * @link http://vufind.org/wiki/importing_records Wiki */ -// Load the Zend framework -- this will automatically trigger -// CliController::harvestnafAction() based on the current filename. -require_once dirname(__FILE__) . '/../public/index.php'; +// Load the Zend framework -- this will automatically trigger the appropriate +// controller action based on directory and file names +define('CLI_DIR', __DIR__); // save directory name of current script +require_once __DIR__ . '/../public/index.php'; diff --git a/module/VuFind/CLI/Module.php b/module/VuFind/CLI/Module.php new file mode 100644 index 0000000000000000000000000000000000000000..d9de28cf96ae73454a7cf9bf63d70fd6e854a990 --- /dev/null +++ b/module/VuFind/CLI/Module.php @@ -0,0 +1,52 @@ +<?php + +namespace VuFind\CLI; +use Zend\ModuleManager\ModuleManager, + Zend\Mvc\MvcEvent, Zend\Mvc\Router\Http\RouteMatch; + +class Module +{ + public function getConfig() + { + return include __DIR__ . '/config/module.config.php'; + } + + public function getAutoloaderConfig() + { + // No extra configuration necessary; since this module uses a subset of the + // VuFind namespace, its library code is in the main src area of the VuFind + // module. + return array(); + } + + public function init(ModuleManager $m) + { + } + + public function onBootstrap(MvcEvent $e) + { + $callback = function ($e) { + // Get command line arguments and present working directory from + // server superglobal: + $server = $e->getApplication()->getRequest()->getServer(); + $args = $server->get('argv'); + $filename = $args[0]; + $pwd = $server->get('PWD', CLI_DIR); + + // Convert base filename (minus .php extension) and containing directory + // name into action and controller, respectively: + $baseFilename = basename($filename); + $baseFilename = substr($baseFilename, 0, strlen($baseFilename) - 4); + $baseDirname = basename(dirname(realpath($pwd . '/' . $filename))); + $routeMatch = new RouteMatch( + array('controller' => $baseDirname, 'action' => $baseFilename), 1 + ); + + // Override standard routing: + $routeMatch->setMatchedRouteName('default'); + $e->setRouteMatch($routeMatch); + }; + $events = $e->getApplication()->getEventManager(); + $events->attach('route', $callback); + } +} diff --git a/module/VuFind/CLI/config/module.config.php b/module/VuFind/CLI/config/module.config.php new file mode 100644 index 0000000000000000000000000000000000000000..92e27c0d7250e30c700746ac737148eddd871b0b --- /dev/null +++ b/module/VuFind/CLI/config/module.config.php @@ -0,0 +1,12 @@ +<?php +namespace VuFind\CLI\Module\Configuration; + +$config = array( + 'controllers' => array( + 'invokables' => array( + 'import' => 'VuFind\CLI\Controller\ImportController', + ), + ), +); + +return $config; \ No newline at end of file diff --git a/module/VuFind/src/VuFind/Bootstrap.php b/module/VuFind/src/VuFind/Bootstrap.php index 689785f933ed80e3b82c886cf6abde5b7a802b31..9480117a43984969136fe3b80c649af514772908 100644 --- a/module/VuFind/src/VuFind/Bootstrap.php +++ b/module/VuFind/src/VuFind/Bootstrap.php @@ -153,11 +153,13 @@ class Bootstrap // Grab the template name from the first child -- we can use this to // figure out the current template context. $children = $viewModel->getChildren(); - $parts = explode('/', $children[0]->getTemplate()); - $viewModel->setVariable('templateDir', $parts[0]); - $viewModel->setVariable( - 'templateName', isset($parts[1]) ? $parts[1] : null - ); + if (!empty($children)) { + $parts = explode('/', $children[0]->getTemplate()); + $viewModel->setVariable('templateDir', $parts[0]); + $viewModel->setVariable( + 'templateName', isset($parts[1]) ? $parts[1] : null + ); + } }; $this->events->attach('dispatch', $callback); } diff --git a/module/VuFind/src/VuFind/CLI/Controller/AbstractBase.php b/module/VuFind/src/VuFind/CLI/Controller/AbstractBase.php new file mode 100644 index 0000000000000000000000000000000000000000..4aa60042d4659cd0eac74a07fd35c34af696481b --- /dev/null +++ b/module/VuFind/src/VuFind/CLI/Controller/AbstractBase.php @@ -0,0 +1,74 @@ +<?php +/** + * VuFind controller base class (defines some methods that can be shared by other + * controllers). + * + * PHP version 5 + * + * Copyright (C) Villanova University 2010. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package Controller + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/building_a_recommendations_module Wiki + */ +namespace VuFind\CLI\Controller; +use Zend\Console\Getopt, Zend\Mvc\Controller\AbstractActionController; + +/** + * VuFind controller base class (defines some methods that can be shared by other + * controllers). + * + * @category VuFind2 + * @package Controller + * @author Chris Hallberg <challber@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/building_a_recommendations_module Wiki + */ +class AbstractBase extends AbstractActionController +{ + protected $consoleOpts; + + /** + * Constructor + */ + public function __construct() + { + // This controller should only be accessed from the command line! + if (PHP_SAPI != 'cli') { + throw new \Exception('Access denied to command line tools.'); + } + + // Get access to information about the CLI request. + $this->consoleOpts = new Getopt(array()); + } + + /** + * Warn the user if VUFIND_LOCAL_DIR is not set. + * + * @return void + */ + protected function checkLocalSetting() + { + if (!getenv('VUFIND_LOCAL_DIR')) { + echo "WARNING: The VUFIND_LOCAL_DIR environment variable is not set.\n"; + echo "This should point to your local configuration directory (i.e. \n"; + echo realpath(APPLICATION_PATH . '/../local') . ").\n"; + echo "Without it, inappropriate default settings may be loaded.\n\n"; + } + } +} \ No newline at end of file diff --git a/module/VuFind/src/VuFind/CLI/Controller/ImportController.php b/module/VuFind/src/VuFind/CLI/Controller/ImportController.php new file mode 100644 index 0000000000000000000000000000000000000000..9dc622c71c45c728c817ffe6b1e70c72c450bed9 --- /dev/null +++ b/module/VuFind/src/VuFind/CLI/Controller/ImportController.php @@ -0,0 +1,90 @@ +<?php +/** + * CLI Controller Module + * + * PHP version 5 + * + * Copyright (C) Villanova University 2010. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package Controller + * @author Chris Hallberg <challber@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/building_a_recommendations_module Wiki + */ +namespace VuFind\CLI\Controller; +use VuFind\XSLT\Importer; + +/** + * This controller handles various command-line tools + * + * @category VuFind2 + * @package Controller + * @author Chris Hallberg <challber@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/building_a_recommendations_module Wiki + */ +class ImportController extends AbstractBase +{ + /** + * XSLT Import Tool + * + * @return void + */ + public function importXslAction() + { + // Parse switches: + $this->consoleOpts->addRules( + array('test-only' => 'Use test mode', 'index-s' => 'Solr index to use') + ); + $testMode = $this->consoleOpts->getOption('test-only') ? true : false; + $index = $this->consoleOpts->getOption('index'); + if (empty($index)) { + $index = 'Solr'; + } + + // Display help message if parameters missing: + $argv = $this->consoleOpts->getRemainingArgs(); + if (!isset($argv[1])) { + echo "Usage: import-xsl.php [--test-only] [--index <type>] XML_file " + . "properties_file\n" + . "\tXML_file - source file to index\n" + . "\tproperties_file - import configuration file\n" + . "If the optional --test-only flag is set, transformed XML will " + . "be displayed\non screen for debugging purposes, but it will " + . "not be indexed into VuFind.\n\n" + . "If the optional --index parameter is set, it must be followed by " + . "the name of\na class for accessing Solr; it defaults to the " + . "standard Solr class, but could\nbe overridden with, for example, " + . "SolrAuth to load authority records.\n\n" + . "Note: See vudl.properties and ojs.properties for configuration " + . "examples.\n"; + exit(1); + } + + // Try to import the document if successful: + try { + Importer::save($argv[0], $argv[1], $index, $testMode); + } catch (\Exception $e) { + echo "Fatal error: " . $e->getMessage() . "\n"; + exit(1); + } + if (!$testMode) { + echo "Successfully imported {$argv[0]}...\n"; + } + exit(0); + } +} diff --git a/module/VuFind/src/VuFind/XSLT/Import/VuFind.php b/module/VuFind/src/VuFind/XSLT/Import/VuFind.php index 9a9784175f6304f67ef84033f5b927bc8eaffeea..8b3becdf1176f8d7fd9eafc308a3157f226cceda 100644 --- a/module/VuFind/src/VuFind/XSLT/Import/VuFind.php +++ b/module/VuFind/src/VuFind/XSLT/Import/VuFind.php @@ -26,7 +26,7 @@ * @link http://vufind.org/wiki/importing_records Wiki */ namespace VuFind\XSLT\Import; -use VuFind\Config\Reader as ConfigReader; +use DOMDocument, VuFind\Config\Reader as ConfigReader; /** * XSLT support class -- all methods of this class must be public and static; diff --git a/module/VuFind/src/VuFind/XSLT/Importer.php b/module/VuFind/src/VuFind/XSLT/Importer.php new file mode 100644 index 0000000000000000000000000000000000000000..20ca5028943568d7e4c44b981e73a4b516ddef99 --- /dev/null +++ b/module/VuFind/src/VuFind/XSLT/Importer.php @@ -0,0 +1,169 @@ +<?php +/** + * VuFind XSLT importer + * + * PHP version 5 + * + * Copyright (C) Villanova University 2010. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package Support_Classes + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/ Wiki + */ +namespace VuFind\XSLT; +use DOMDocument, VuFind\Config\Reader as ConfigReader, + VuFind\Connection\Manager as ConnectionManager, + XSLTProcessor; + +/** + * VuFind XSLT importer + * + * @category VuFind2 + * @package Support_Classes + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/ Wiki + */ +class Importer +{ + /** + * Save an XML file to the Solr index using the specified configuration. + * + * @param string $xmlFile XML file to transform. + * @param string $properties Properties file. + * @param string $index Solr index to use. + * @param bool $testMode Are we in test-only mode? + * + * @throws Exception + * @return void + */ + public static function save($xmlFile, $properties, $index = 'Solr', + $testMode = false + ) { + // Process the file: + $xml = self::generateXML($xmlFile, $properties); + + // Save the results (or just display them, if in test mode): + if (!$testMode) { + $solr = ConnectionManager::connectToIndex($index); + $result = $solr->saveRecord($xml); + } else { + echo $xml . "\n"; + } + } + + /** + * Transform $xmlFile using the provided $properties configuration. + * + * @param string $xmlFile XML file to transform. + * @param string $properties Properties file. + * + * @throws Exception + * @return mixed Transformed XML. + */ + protected static function generateXML($xmlFile, $properties) + { + // Load properties file: + $properties = ConfigReader::getConfigPath($properties, 'import'); + if (!file_exists($properties)) { + throw new Exception("Cannot load properties file: {$properties}."); + } + $options = parse_ini_file($properties, true); + + // Make sure required parameter is set: + if (!isset($options['General']['xslt'])) { + throw new Exception( + "Properties file ({$properties}) is missing General/xslt setting." + ); + } + $xslFile = ConfigReader::getConfigPath( + $options['General']['xslt'], 'import/xsl' + ); + + // Initialize the XSL processor: + $xsl = self::initProcessor($options); + + // Load up the style sheet + $style = new DOMDocument; + if (!$style->load($xslFile)) { + throw new Exception("Problem loading XSL file: {$xslFile}."); + } + $xsl->importStyleSheet($style); + + // Load up the XML document + $xml = new DOMDocument; + if (!$xml->load($xmlFile)) { + throw new Exception("Problem loading XML file: {$xmlFile}."); + } + + // Process and return the XML through the style sheet + $result = $xsl->transformToXML($xml); + if (!$result) { + throw new Exception("Problem transforming XML."); + } + return $result; + } + + /** + * Initialize an XSLT processor using settings from the user-specified properties + * file. + * + * @param array $options Parsed contents of properties file. + * + * @throws Exception + * @return object XSLT processor. + */ + protected static function initProcessor($options) + { + // Prepare an XSLT processor and pass it some variables + $xsl = new XSLTProcessor(); + + // Register PHP functions, if specified: + if (isset($options['General']['php_function'])) { + $functions = is_array($options['General']['php_function']) + ? $options['General']['php_function'] + : array($options['General']['php_function']); + foreach ($functions as $function) { + $xsl->registerPHPFunctions($function); + } + } + + // Register custom classes, if specified: + if (isset($options['General']['custom_class'])) { + $classes = is_array($options['General']['custom_class']) + ? $options['General']['custom_class'] + : array($options['General']['custom_class']); + foreach ($classes as $class) { + // Dynamically generate the requested class: + $class = preg_replace('/[^A-Za-z0-9_]/', '', $class); + eval("class $class extends \\VuFind\\XSLT\\Import\\$class { }"); + $methods = get_class_methods($class); + foreach ($methods as $method) { + $xsl->registerPHPFunctions($class . '::' . $method); + } + } + } + + // Load parameters, if provided: + if (isset($options['Parameters'])) { + $xsl->setParameter('', $options['Parameters']); + } + + return $xsl; + } +}