aliroExtensionInstaller.php

Go to the documentation of this file.
00001 <?php
00002 
00003 /*******************************************************************************
00004  * Aliro - the modern, accessible content management system
00005  *
00006  * Aliro is open source software, free to use, and licensed under GPL.
00007  * You can find the full licence at http://www.gnu.org/copyleft/gpl.html GNU/GPL
00008  *
00009  * The author freely draws attention to the fact that Aliro derives from Mambo,
00010  * software that is controlled by the Mambo Foundation.  However, this section
00011  * of code is totally new.  If it should contain any fragments that are similar
00012  * to Mambo, please bear in mind (1) there are only so many ways to do things
00013  * and (2) the author of Aliro is also the author and copyright owner for large
00014  * parts of Mambo 4.6.
00015  *
00016  * Tribute should be paid to all the developers who took Mambo to the stage
00017  * it had reached at the time Aliro was created.  It is a feature rich system
00018  * that contains a good deal of innovation.
00019  *
00020  * Your attention is also drawn to the fact that Aliro relies on other items of
00021  * open source software, which is very much in the spirit of open source.  Aliro
00022  * wishes to give credit to those items of code.  Please refer to
00023  * http://aliro.org/credits for details.  The credits are not included within
00024  * the Aliro package simply to avoid providing a marker that allows hackers to
00025  * identify the system.
00026  *
00027  * Copyright in this code is strictly reserved by its author, Martin Brampton.
00028  * If it seems appropriate, the copyright will be vested in the Aliro Organisation
00029  * at a suitable time.
00030  *
00031  * Copyright (c) 2007 Martin Brampton
00032  *
00033  * http://aliro.org
00034  *
00035  * counterpoint@aliro.org
00036  *
00037  * aliroInstaller is the abstract base class for the following:
00038  *
00039  * aliroExtensionInstaller is the class that handles the installation of extensions
00040  *
00041  * aliroExtensionUninstaller is the class that handles removal of any extension.
00042  *
00043  */
00044 
00045 class installerException extends Exception {
00046     public $extension = null;
00047 
00048     public function __construct ($message, $extension) {
00049         $basemessage = T_('Installer error: XML file %s for extension %s - ');
00050         $this->extension = $extension;
00051         parent::__construct($basemessage.$message, 0);
00052     }
00053 
00054 }
00055 
00056 class aliroLanguageHandler extends aliroCommonExtHandler  {
00057     protected static $instance = __CLASS__;
00058     protected $extensiondir = '/language/';
00059 
00060     // Singleton accessor with cache
00061     public static function getInstance () {
00062         return is_object(self::$instance) ? self::$instance : (self::$instance = parent::getCachedSingleton(self::$instance));
00063     }
00064 }
00065 
00066 class aliroPatchHandler extends aliroCommonExtHandler {
00067     protected static $instance = __CLASS__;
00068     protected $extensiondir = '/';
00069 
00070     // Singleton accessor with cache
00071     public static function getInstance () {
00072         return is_object(self::$instance) ? self::$instance : (self::$instance = parent::getCachedSingleton(self::$instance));
00073     }
00074 }
00075 
00076 class aliroIncludeHandler extends aliroCommonExtHandler  {
00077     protected static $instance = __CLASS__;
00078     protected $extensiondir = '/includes/';
00079 
00080     // Singleton accessor with cache
00081     public static function getInstance () {
00082         return is_object(self::$instance) ? self::$instance : (self::$instance = parent::getCachedSingleton(self::$instance));
00083     }
00084 }
00085 
00086 class aliroParameterHandler extends aliroCommonExtHandler  {
00087     protected static $instance = __CLASS__;
00088     protected $extensiondir = '/parameters/';
00089 
00090     // Singleton accessor with cache
00091     public static function getInstance () {
00092         return is_object(self::$instance) ? self::$instance : (self::$instance = parent::getCachedSingleton(self::$instance));
00093     }
00094 }
00095 
00096 class aliroExtensionInstaller extends aliroFriendlyBase  {
00097     //protected $legalTypes = array('component', 'module', 'mambot', 'plugin', 'template', 'language', 'patch');
00098 
00099     // These are given real values by the constructor
00100     protected $xmlfile = '';
00101     protected $xmlobject = null;
00102     protected $here = '';
00103     protected $purifier = null;
00104 
00105     // This is given a real value by the install method
00106     protected $handler = null;
00107 
00108     public function __construct ($xmlfile) {
00109         $this->xmlfile = $xmlfile;
00110         $this->xmlobject = new aliroXML;
00111         try {
00112             $this->xmlobject->loadFile($xmlfile);
00113         } catch (aliroXMLException $exception) {
00114                 aliroRequest::getInstance()->setErrorMessage ($exception->getMessage(), _ALIRO_ERROR_FATAL);
00115                 $this->xmlobject = null;
00116         }
00117         if ($this->xmlobject) {
00118             $this->here = dirname($xmlfile).'/';
00119             $this->purifier = new HTMLPurifier;
00120         }
00121     }
00122 
00123     public function install () {
00124         if (!$this->xmlobject) return;
00125         try {
00126             $extension = $this->createExtension();
00127             $isUpgrade = $this->getParam($_POST, 'upgrade') ? true : false;
00128             if ($this->extensionExists($extension)) {
00129                 if (!$isUpgrade) throw new installerException (T_('cannot be installed - already present'), $extension);
00130                 // Must be an upgrade, and $isUpgrade must be true
00131                 aliroExtensionHandler::getInstance()->removeExtensions($extension->formalname, $isUpgrade);
00132             }
00133             elseif ($isUpgrade) throw new installerException (T_('cannot be upgraded - not presently installed'), $extension);
00134             $this->handler = aliroExtensionHandler::getExtensionTypeHandler($extension->type);
00135             if (!$this->handler) throw new installerException (sprintf(T_('cannot be installed - no handler for type %s'), $extension->type), $extension);
00136             if ('component' == $extension->type) $this->admin = 1;
00137             else $this->admin = $extension->admin;
00138             $extension->store();
00139             $method = 'install_'.$extension->type;
00140             $this->putFilesInPlace($extension);
00141             $this->handleClasses($extension);
00142             if (!$isUpgrade) {
00143                 $queries = $this->getXML('install->queries->query');
00144                 if ($queries) $this->doQueries($queries);
00145                 $jqueries = $this->getXML('install->sql->file');
00146                 if ($jqueries) $this->doJQueries($jqueries);
00147             }
00148             if (method_exists($this, $method)) $this->$method($isUpgrade, $extension);
00149             if ($isUpgrade) {
00150                 if ($upgradefile = $this->getXML('upgradefile')) {
00151                     $this->doPackageCode ($extension, (string) $upgradefile, (string) $upgradefile['class'], 'com_upgrade');
00152                 }
00153             }
00154             else {
00155                 if ($installfile = $this->getXML('installfile')) {
00156                     $this->doPackageCode ($extension, (string) $installfile, (string) $installfile['class'], 'com_install');
00157                 }
00158             }
00159             $this->handler->clearCache(('component' != $extension->type));
00160             aliroRequest::getInstance()->setErrorMessage (T_('Installation completed: ').$extension->description, _ALIRO_ERROR_INFORM);
00161             return true;
00162         }
00163         catch (installerException $exception) {
00164             aliroRequest::getInstance()->setErrorMessage (sprintf($exception->getMessage(), basename($this->xmlfile), $exception->extension->formalname), _ALIRO_ERROR_FATAL);
00165         }
00166         catch (databaseException $exception) {
00167             $message = sprintf(T_('Extension XML %s database error on %s at %s'), $this->xmlfile, date('Y-M-d'), date('H:i:s'));
00168             $errorkey = "SQL/{$exception->getCode()}/installer/$exception->dbname/{$exception->getMessage()}/$exception->sql";
00169             aliroErrorRecorder::getInstance()->recordError($message, $errorkey, $message, $exception);
00170             aliroRequest::getInstance()->setErrorMessage ($message, _ALIRO_ERROR_SEVERE);
00171         }
00172         catch (xmlException $exception) {
00173             $message = $exception->getMessage();
00174             aliroErrorRecorder::getInstance()->recordError($message, $message, $message, $exception);
00175             aliroRequest::getInstance()->setErrorMessage ($message, _ALIRO_ERROR_FATAL);
00176         }
00177         return false;
00178     }
00179 
00180     protected function extensionExists ($extension) {
00181         return (aliroExtensionHandler::getInstance()->getExtensionByName($extension->formalname)) ? true : false;
00182     }
00183 
00184     protected function doPackageCode ($extension, $file, $class, $retro) {
00185         if (false !== strpos($file, '..')) throw new installerException (sprintf(T_('file %s contains illegal ".."'), $file), $extension);
00186         $dofile = $this->handler->getClassPath($extension->formalname, 2).'/'.$file;
00187         if (is_file($dofile)) {
00188             try {
00189                 if ($class) {
00190                     require_once($dofile);
00191                     new $class();
00192                 }
00193                 else aliroRequest::getInstance()->invokeRetroCode($dofile, $retro);
00194             }
00195             catch (databaseException $exception) {
00196                 $message = sprintf(T_('Extension XML %s database error on %s at %s'), $this->xmlfile, date('Y-M-d'), date('H:i:s'));
00197                 $errorkey = "SQL/{$exception->getCode()}/installer/$exception->dbname/{$exception->getMessage()}/$exception->sql";
00198                 aliroErrorRecorder::getInstance()->recordError($message, $errorkey, $message, $exception);
00199             }
00200         }
00201     }
00202 
00203     protected function createExtension () {
00204         $extension = new aliroExtension;
00205         $message = $extension->populateFromXML($this->xmlobject);
00206         if ($message) throw new installerException ($message, $extension);
00207         /*
00208         $extension->name = $this->purifier->purify((string) $this->getXML('name'));
00209         $extension->type = $this->xmlobject->baseAttribute('type');
00210         if (!in_array($extension->type, $this->legalTypes)) throw new installerException (T_('has no valid type'), $extension);
00211         if ('plugin' == $extension->type) $extension->type = 'mambot';
00212         $extension->formalname = $this->purifier->purify((string) $this->getXML('formalname'));;
00213         if (!$extension->formalname AND 'component' == strtolower($extension->type)) $extension->formalname = 'com_'.str_replace(' ', '', strtolower($extension->name));
00214         if (!$extension->formalname) throw new installerException (T_('has no formal name'), $extension);
00215         // validate formalname
00216         $extension->admin = ('administrator' == $this->xmlobject->baseAttribute('client')) ? 2 : 1;
00217         if ('template' == $extension->type) {
00218             $currentDefault = aliroTemplateHandler::getInstance()->getDefaultTemplateProperty('formalname', $extension->admin);
00219             if (!$currentDefault OR $currentDefault == $extension->formalname) {
00220                 $extension->default_template = '1';
00221             }
00222         }
00223         $extension->author = $this->purifier->purify((string) $this->getXML('author'));
00224         $extension->version = $this->purifier->purify((string) $this->getXML('version'));
00225         $extension->date = $this->purifier->purify((string) $this->getXML('creationdate'));
00226         $extension->authoremail = $this->purifier->purify((string) $this->getXML('authoremail'));
00227         $extension->authorurl = $this->purifier->purify((string) $this->getXML('authorurl'));
00228         $extension->description = $this->purifier->purify((string) $this->getXML('description'));
00229         $extension->class = $this->xmlobject->baseAttribute('userclass');
00230         $extension->adminclass = $this->xmlobject->baseAttribute('adminclass');
00231         $extension->menuclass = $this->xmlobject->baseAttribute('menuclass');
00232         $extension->timestamp = date('Y-m-d');
00233         */
00234         return $extension;
00235     }
00236 
00237     protected function getXML ($properties) {
00238         return is_null($this->xmlobject) ? null : $this->xmlobject->getXML($properties);
00239     }
00240 
00241     protected function handleClasses ($extension) {
00242         $side = $extension->admin & 2 ? 'admin' : 'user';
00243         $classfiles = $this->getXML('files->filename');
00244         $this->handleClassFiles ($classfiles, $extension, $side);
00245         $classfiles = $this->getXML('classfiles->filename');
00246         $this->handleClassFiles ($classfiles, $extension, $side, true);
00247         if ('component' == $extension->type) {
00248             $classfiles = $this->getXML('administration->files->filename');
00249             $this->handleClassFiles ($classfiles, $extension, 'admin');
00250             $classfiles = $this->getXML('administration->classfiles->filename');
00251             $this->handleClassFiles ($classfiles, $extension, 'admin', true);
00252         }
00253         smartAdminClassMapper::getInstance()->clearCache();
00254         smartClassMapper::getInstance()->clearCache();
00255     }
00256 
00257     protected function handleClassFiles ($classfiles, $extension, $side, $classRequired=false) {
00258         if ($classfiles) {
00259             $database = aliroCoreDatabase::getInstance();
00260             foreach ($classfiles as $classfile) {
00261                 $classes = explode(',', (string) $classfile['classes']);
00262                 $filename = (string) $classfile;
00263                 $filedir = dirname($filename);
00264                 $filemap = ('.' == $filedir ? '' : $filedir.'/').basename($filename, '.php');
00265                 foreach ($classes as $class) {
00266                     $class = trim($class);
00267                     if (!$class) {
00268                         if ($classRequired) aliroRequest::getInstance()->setErrorMessage (sprintf(T_('Installer error: XML file %s for extension %s - filename %s has no valid classes attribute'), basename($this->xmlfile), $extension->formalname, $filename), _ALIRO_ERROR_SEVERE);
00269                         continue;
00270                     }
00271                     $database->doSQL("INSERT INTO #__classmap VALUES (0, '$extension->type', '$extension->formalname', '$side', '$filemap', '$class')");
00272                 }
00273             }
00274         }
00275     }
00276 
00277     protected function putFilesInPlace ($extension) {
00278         $xmlfilename = basename($this->xmlfile);
00279         if (!$this->handler->createDirectory($extension->formalname, $extension->admin)) {
00280             throw new installerException (sprintf(T_('unable to create user directory for %s %s'), $extension->type, $extension->formalname), $extension);
00281         }
00282         $side = ($extension->admin & 2) ? 'admin' : 'user';
00283         $dirpath = $this->handler->getPath($extension->formalname, $extension->admin);
00284         $classpath = $this->handler->getClassPath($extension->formalname, $extension->admin);
00285         $this->moveFiles($this->getXML('classfiles->filename'), $classpath);
00286         $this->moveFiles($this->getXML('files->filename'), $dirpath, $this->getXML('files->[folder]'));
00287         $this->moveFiles($this->getXML('images->filename'), $dirpath);
00288         $this->moveFiles($this->getXML('css->filename'), $dirpath);
00289         $this->moveFiles($this->getXML('media->filename'), $this->absolute_path.'/images/stories/');
00290         if ('component' == $extension->type) {
00291             if (!$this->handler->createDirectory($extension->formalname, 2)) {
00292                 throw new installerException (sprintf(T_('unable to create user directory for %s %s'), $extension->type, $extension->formalname), $extension);
00293             }
00294             $dirpath = $this->handler->getPath($extension->formalname, 2);
00295             $classpath = $this->handler->getClassPath($extension->formalname, 2);
00296             $this->moveFiles($this->getXML('administration->files->filename'), $dirpath, $this->getXML('administration->files->[folder]'));
00297             $this->moveFiles($this->getXML('administration->classfiles->filename'), $classpath);
00298             $this->moveFiles($this->getXML('administration->images->filename'), $dirpath);
00299             $this->moveFiles(array($this->getXML('installfile')), $classpath);
00300             $this->moveFiles(array($this->getXML('uninstallfile')), $classpath);
00301             $this->moveFiles(array($this->getXML('upgradefile')), $classpath);
00302             $this->moveFiles(array($xmlfilename), $this->handler->getXMLPath($extension->formalname, 2));
00303         }
00304         $this->moveFiles(array($xmlfilename), $this->handler->getXMLPath($extension->formalname, $extension->admin));
00305         aliroCoreDatabase::getInstance()->doSQL("UPDATE #__extensions SET xmlfile = '{$this->handler->getXMLRelativePath($extension->formalname, $extension->admin)}/{$xmlfilename}' WHERE formalname = '$extension->formalname'");
00306     }
00307 
00308     protected function moveFiles ($files, $path, $subdir='') {
00309         $fromdir = $subdir ? $this->here.$subdir.'/' : $this->here;
00310         if ($files) {
00311             $fmanager = aliroFileManager::getInstance();
00312             foreach ($files as $file) {
00313                 $filename = trim((string) $file);
00314                 if ($filename) {
00315                     if (false !== strpos($filename, '..')) {
00316                         throw new installerException (sprintf(T_('file %s contains illegal ".."'), $filename), $extension);
00317                     }
00318                     else $fmanager->forceCopy($fromdir.$filename, $path.'/'.$filename, true);
00319                 }
00320             }
00321         }
00322     }
00323     
00324     protected function doQueries ($queries) {
00325         $database = aliroDatabase::getInstance();
00326         foreach ($queries as $query) if ($query = trim((string)$query)) $database->doSQL($query);
00327     }
00328     
00329     protected function doJQueries ($jqueries) {
00330         $database = aliroDatabase::getInstance();
00331         $subdir = $this->getXML('administration->files->[folder]').'/';
00332         foreach ($jqueries as $query) {
00333             if ($query = trim((string)$query)) {
00334                 $realquery = @file_get_contents($this->here.$subdir.$query);
00335                 if ($realquery) {
00336                     $database->setQuery($realquery);
00337                     $database->query_batch();
00338                 }
00339             }
00340         }
00341     }
00342 
00343     protected function createComponentMenu ($item, $name, $parent, $toplevel=false) {
00344         $menuitem = new aliroAdminMenu();
00345         $menuitem->name = $this->purifier->purify((string) $item);
00346         $menuitem->parent = $toplevel;
00347         $link = "index.php?option=$name";
00348         if ($toplevel AND ($linkdetail = (string) $item['link'])) {
00349             if ('&' == $linkdetail[0]) $link .= $linkdetail;
00350             else $link = 'index.php?'.$linkdetail;
00351         }
00352         elseif ($toplevel AND ($task = (string) $item['task'])) $link .= '&task='.$task;
00353         $menuitem->link = $link;
00354         $menuitem->type = 'components';
00355         $menuitem->published = 1;
00356         $menuitem->parent = $parent;
00357         $menuitem->component = $name;
00358         $menuitem->ordering = $toplevel ? $this->submenuordering++ : 0;
00359         $menuitem->store();
00360         return $menuitem->id;
00361     }
00362 
00363     protected function install_component ($isUpgrade, $extension) {
00364         $mainmenu = $this->getXML('administration->menu');
00365         $component = new aliroComponent();
00366         $component->name = ($mainmenu ? $this->purifier->purify((string) $mainmenu) : $extension->name);
00367         $component->option = $component->extformalname = $extension->formalname;
00368         $component->ordering = 0;
00369         $component->class = $this->xmlobject->baseAttribute('userclass');
00370         $component->adminclass = $this->xmlobject->baseAttribute('adminclass');
00371         $component->menuclass = $this->xmlobject->baseAttribute('menuclass');
00372         $component->params = '';
00373         $component->store();
00374         if ($mainmenu) {
00375             $topid = $this->createComponentMenu ($mainmenu, $extension->formalname, aliroAdminMenuHandler::getInstance()->baseComponentMenu());
00376             $submenus = $this->getXML('administration->submenu->menu');
00377             $this->submenuordering = 1;
00378             if ($submenus) foreach ($submenus as $submenu) {
00379                 $this->createComponentMenu ($submenu, $extension->formalname, $topid, true);
00380             }
00381         }
00382         aliroAdminMenuHandler::getInstance()->clearCache(true);
00383     }
00384 
00385     public function removeComponent ($handler, $extension) {
00386         try {
00387             $classpath = $handler->getClassPath($extension->formalname, 2);
00388             if ($uninstallfile = $this->getXML('uninstallfile')) {
00389                 $this->handler = $handler;
00390                 $this->doPackageCode ($extension, (string) $uninstallfile, (string) $uninstallfile['class'], 'com_uninstall');
00391             }
00392             $queries = $this->getXML('uninstall->queries->query');
00393             if ($queries) foreach ($queries as $query) {
00394                 aliroDatabase::getInstance()->doSQL((string) $query);
00395             }
00396         }
00397         catch (databaseException $exception) {
00398 
00399         }
00400     }
00401 
00402     protected function install_module ($isUpgrade, $extension) {
00403         $module = new aliroModule();
00404         $module->title = $extension->name;
00405         $module->ordering = 99;
00406         $module->position = call_user_func(array(aliroTemplateHandler::getInstance()->getDefaultUserTemplateClass(), 'defaultModulePosition'));
00407         $module->showtitle = 1;
00408         $module->admin = $extension->admin;
00409         $module->module = $extension->formalname;
00410         $module->class = $this->xmlobject->baseAttribute('userclass');
00411         $module->adminclass = $this->xmlobject->baseAttribute('adminclass');
00412         $module->published = ('yes' == $this->xmlobject->baseAttribute('published')) ? 1 : 0;
00413         $module->store();
00414         aliroCoreDatabase::getInstance()->doSQL("INSERT INTO #__modules_menu VALUES ('$module->id', 0)");
00415     }
00416 
00417     protected function install_mambot ($isUpgrade, $extension) {
00418         $mambot = new aliroMambot();
00419         $mambot->name = $extension->name;
00420         $mambot->ordering = 99;
00421         $mambot->element = $extension->formalname;
00422         $mambot->class = $this->xmlobject->baseAttribute('userclass');
00423         $mambot->published = ('yes' == $this->xmlobject->baseAttribute('published')) ? 1 : 0;
00424         if ($triggers = $this->xmlobject->baseAttribute('triggers')) {
00425             $mambot->triggers = $triggers;
00426             $mambot->store();
00427         }
00428         else throw new installerException (T_('is plugin but has no triggers'), $extension);
00429     }
00430 
00431 }

Generated on Wed May 14 13:01:56 2008 for ALIRO by  doxygen 1.5.5