Search:

# CWIS Developers Documentation

- Main Page
- Classes
- Files

- Search for |

- File List
- File Members

# PluginManager.php

Go to the documentation of this file.

    00001 <?PHP
    00002
    00006 class PluginManager {
    00007
    00008     # ---- PUBLIC INTERFACE --------------------------------------------------
    00009
    00015     function __construct($AppFramework, $PluginDirectories)
    00016     {
    00017         # save framework and directory list for later use
    00018         $this->AF = $AppFramework;
    00019         $this->DirsToSearch = $PluginDirectories;
    00020
    00021         # get our own database handle
    00022         $this->DB = new Database();
    00023
    00024         # hook into events to load plugin PHP and HTML files
    00025         $this->AF->HookEvent("EVENT_PHP_FILE_LOAD", array($this, "FindPluginPhpFile"));
    00026         $this->AF->HookEvent("EVENT_HTML_FILE_LOAD", array($this, "FindPluginHtmlFile"));
    00027     }
    00028
    00033     function LoadPlugins()
    00034     {
    00035         # clear any existing errors
    00036         $this->ErrMsgs = array();
    00037
    00038         # load list of all base plugin files
    00039         $this->FindPlugins($this->DirsToSearch);
    00040
    00041         # for each plugin found
    00042         foreach ($this->PluginNames as $PluginName)
    00043         {
    00044             # bring in plugin class file
    00045             include_once($this->PluginFiles[$PluginName]);
    00046
    00047             # if plugin class was defined by file
    00048             if (class_exists($PluginName))
    00049             {
    00050                 # if plugin class is a valid descendant of base plugin class
    00051                 $Plugin = new $PluginName;
    00052                 if (is_subclass_of($Plugin, "Plugin"))
    00053                 {
    00054                     # register the plugin
    00055                     $this->Plugins[$PluginName] = $Plugin;
    00056                     $this->PluginEnabled[$PluginName] = TRUE;
    00057                     $this->Plugins[$PluginName]->Register();
    00058
    00059                     # check required plugin attributes
    00060                     $Attribs[$PluginName] = $this->Plugins[$PluginName]->GetAttributes();
    00061                     if (!strlen($Attribs[$PluginName]["Name"]))
    00062                     {
    00063                         $this->ErrMsgs[$PluginName][] = "Plugin <b>".$PluginName."</b>"
    00064                                 ." could not be loaded because it"
    00065                                 ." did not have a <i>Name</i> attribute set.";
    00066                         unset($this->PluginEnabled[$PluginName]);
    00067                         unset($this->Plugins[$PluginName]);
    00068                     }
    00069                     if (!strlen($Attribs[$PluginName]["Version"]))
    00070                     {
    00071                         $this->ErrMsgs[$PluginName][] = "Plugin <b>".$PluginName."</b>"
    00072                                 ." could not be loaded because it"
    00073                                 ." did not have a <i>Version</i> attribute set.";
    00074                         unset($this->PluginEnabled[$PluginName]);
    00075                         unset($this->Plugins[$PluginName]);
    00076                     }
    00077                 }
    00078                 else
    00079                 {
    00080                     $this->ErrMsgs[$PluginName][] = "Plugin <b>".$PluginName."</b>"
    00081                             ." could not be loaded because <i>".$PluginName."</i> was"
    00082                             ." not a subclass of base <i>Plugin</i> class";
    00083                 }
    00084             }
    00085             else
    00086             {
    00087                 $this->ErrMsgs[$PluginName][] = "Expected class <i>".$PluginName
    00088                         ."</i> not found in plugin file <i>"
    00089                         .$this->PluginFiles[$PluginName]."</i>";
    00090             }
    00091         }
    00092
    00093         # install or upgrade plugins if needed
    00094         foreach ($this->Plugins as $PluginName => $Plugin)
    00095         {
    00096             if ($this->PluginEnabled[$PluginName])
    00097             {
    00098                 $this->InstallPlugin($Plugin);
    00099             }
    00100         }
    00101
    00102         # check plugin dependencies
    00103         $this->CheckDependencies();
    00104
    00105         # initialize each plugin
    00106         foreach ($this->Plugins as $PluginName => $Plugin)
    00107         {
    00108             if ($this->PluginEnabled[$PluginName])
    00109             {
    00110                 $ErrMsg = $Plugin->Initialize();
    00111                 if ($ErrMsg !== NULL)
    00112                 {
    00113                     $this->ErrMsgs[$PluginName][] = "Initialization failed for"
    00114                             ." plugin <b>".$PluginName."</b>: <i>".$ErrMsg."</i>";
    00115                     $this->PluginEnabled[$PluginName] = FALSE;
    00116                 }
    00117             }
    00118         }
    00119
    00120         # register any events declared by each plugin
    00121         foreach ($this->Plugins as $PluginName => $Plugin)
    00122         {
    00123             if ($this->PluginEnabled[$PluginName])
    00124             {
    00125                 $Events = $Plugin->DeclareEvents();
    00126                 if (count($Events)) {  $this->AF->RegisterEvent($Events);  }
    00127             }
    00128         }
    00129
    00130         # hook plugins to events
    00131         foreach ($this->Plugins as $PluginName => $Plugin)
    00132         {
    00133             if ($this->PluginEnabled[$PluginName])
    00134             {
    00135                 $EventsToHook = $Plugin->HookEvents();
    00136                 foreach ($EventsToHook as $EventName => $PluginMethod)
    00137                 {
    00138                     $Result = $this->AF->HookEvent(
    00139                             $EventName, array($Plugin, $PluginMethod));
    00140                     if ($Result === FALSE)
    00141                     {
    00142                         $this->ErrMsgs[$PluginName][] =
    00143                                 "Unable to hook requested event <i>"
    00144                                 .$EventName."</i> for plugin <b>".$PluginName."</b>";
    00145                     }
    00146                 }
    00147             }
    00148         }
    00149
    00150         # limit plugin directory list to only active plugins
    00151         foreach ($this->PluginEnabled as $PluginName => $Enabled)
    00152         {
    00153             if (isset($this->PluginDirs[$PluginName]) && !$Enabled)
    00154             {
    00155                 unset($this->PluginDirs[$PluginName]);
    00156             }
    00157         }
    00158
    00159         # add plugin directories to list to be searched for object files
    00160         $ObjDirs = array();
    00161         foreach ($this->PluginDirs as $Dir)
    00162         {
    00163             $ObjDirs[$Dir] = "";
    00164         }
    00165         $this->AF->AddObjectDirectories($ObjDirs);
    00166
    00167         # report to caller whether any problems were encountered
    00168         return count($this->ErrMsgs) ? FALSE : TRUE;
    00169     }
    00170
    00175     function GetErrorMessages()
    00176     {
    00177         return $this->ErrMsgs;
    00178     }
    00179
    00184     function GetPluginAttributes()
    00185     {
    00186         $Info = array();
    00187         foreach ($this->Plugins as $PluginName => $Plugin)
    00188         {
    00189             $Info[$PluginName] = $Plugin->GetAttributes();
    00190             $Info[$PluginName]["Enabled"] =
    00191                     $this->PluginInfo[$PluginName]["Enabled"];
    00192         }
    00193         return $Info;
    00194     }
    00195
    00202     function PluginEnabled($PluginName, $NewValue = NULL)
    00203     {
    00204         if ($NewValue !== NULL)
    00205         {
    00206             $this->DB->Query("UPDATE PluginInfo"
    00207                     ." SET Enabled = ".($NewValue ? "1" : "0")
    00208                     ." WHERE BaseName = '".addslashes($PluginName)."'");
    00209             $this->PluginEnabled[$PluginName] = $NewValue;
    00210             $this->PluginInfo[$PluginName]["Enabled"] = $NewValue;
    00211         }
    00212         return $this->PluginEnabled[$PluginName];
    00213     }
    00214
    00215
    00216     # ---- PRIVATE INTERFACE -------------------------------------------------
    00217
    00218     private $Plugins = array();
    00219     private $PluginFiles = array();
    00220     private $PluginNames = array();
    00221     private $PluginDirs = array();
    00222     private $PluginInfo = array();
    00223     private $PluginEnabled = array();
    00224     private $AF;
    00225     private $DirsToSearch;
    00226     private $ErrMsgs = array();
    00227     private $DB;
    00228
    00229     private function FindPlugins($DirsToSearch)
    00230     {
    00231         # for each directory
    00232         $PluginFiles = array();
    00233         foreach ($DirsToSearch as $Dir)
    00234         {
    00235             # if directory exists
    00236             if (is_dir($Dir))
    00237             {
    00238                 # for each file in directory
    00239                 $FileNames = scandir($Dir);
    00240                 foreach ($FileNames as $FileName)
    00241                 {
    00242                     # if file looks like base plugin file
    00243                     if (preg_match("/^[a-zA-Z_][a-zA-Z0-9_]*\.php$/", $FileName))
    00244                     {
    00245                         # add file to list
    00246                         $PluginName = preg_replace("/\.php$/", "", $FileName);
    00247                         $this->PluginNames[$PluginName] = $PluginName;
    00248                         $this->PluginFiles[$PluginName] = $Dir."/".$FileName;
    00249                     }
    00250                     # else if file looks like plugin directory
    00251                     elseif (is_dir($Dir."/".$FileName)
    00252                             && preg_match("/^[a-zA-Z_][a-zA-Z0-9_]*/", $FileName))
    00253                     {
    00254                         # if there is a base plugin file in the directory
    00255                         $PossibleFile = $Dir."/".$FileName."/".$FileName.".php";
    00256                         if (file_exists($PossibleFile))
    00257                         {
    00258                             # add plugin and directory to lists
    00259                             $this->PluginNames[$FileName] = $FileName;
    00260                             $this->PluginFiles[$FileName] = $PossibleFile;
    00261                             $this->PluginDirs[$FileName] = $Dir."/".$FileName;
    00262                         }
    00263                         else
    00264                         {
    00265                             $this->ErrMsgs[$FileName][] =
    00266                                     "Expected plugin file <i>".$FileName.".php</i> not"
    00267                                     ." found in plugin subdirectory <i>"
    00268                                     .$Dir."/".$FileName."</i>";
    00269                         }
    00270                     }
    00271                 }
    00272             }
    00273         }
    00274
    00275         # return list of base plugin files to caller
    00276         return $PluginFiles;
    00277     }
    00278
    00279     private function InstallPlugin($Plugin)
    00280     {
    00281         # retrieve info about plugin from database
    00282         $PluginName = get_class($Plugin);
    00283         $this->DB->Query("SELECT * FROM PluginInfo"
    00284                 ." WHERE BaseName = '".addslashes($PluginName)."'");
    00285
    00286         # if plugin was not found in database
    00287         $Attribs = $Plugin->GetAttributes();
    00288         if ($this->DB->NumRowsSelected() == 0)
    00289         {
    00290             # add plugin to database
    00291             $this->DB->Query("INSERT INTO PluginInfo"
    00292                     ." (BaseName, Version, Installed, Enabled)"
    00293                     ." VALUES ('".addslashes($PluginName)."', "
    00294                     ." '".addslashes($Attribs["Version"])."', "
    00295                     ."0, "
    00296                     ." ".($Attribs["EnabledByDefault"] ? 1 : 0).")");
    00297
    00298             # read plugin settings back in
    00299             $this->DB->Query("SELECT * FROM PluginInfo"
    00300                  ." WHERE BaseName = '".addslashes($PluginName)."'");
    00301         }
    00302
    00303         # store plugin settings for later use
    00304         $this->PluginInfo[$PluginName] = $this->DB->FetchRow();
    00305         $this->PluginEnabled[$PluginName] = $this->PluginInfo[$PluginName]["Enabled"];
    00306
    00307         # if plugin is enabled
    00308         if ($this->PluginEnabled[$PluginName])
    00309         {
    00310             # if plugin has not been installed
    00311             if (!$this->PluginInfo[$PluginName]["Installed"])
    00312             {
    00313                 # install plugin
    00314                 $ErrMsg = $Plugin->Install();
    00315
    00316                 # if install succeeded
    00317                 if ($ErrMsg == NULL)
    00318                 {
    00319                     # mark plugin as installed
    00320                     $this->DB->Query("UPDATE PluginInfo SET Installed = 1"
    00321                             ." WHERE BaseName = '".addslashes($PluginName)."'");
    00322                 }
    00323                 else
    00324                 {
    00325                     # disable plugin
    00326                     $this->PluginEnabled[$PluginName] = FALSE;
    00327
    00328                     # record error message about installation failure
    00329                     $this->ErrMsgs[$PluginName][] = "Installation of plugin <b>"
    00330                             .$PluginName."</b> failed: <i>".$ErrMsg."</i>";;
    00331                 }
    00332             }
    00333             else
    00334             {
    00335                 # if plugin version is newer than version in database
    00336                 if (version_compare($Attribs["Version"],
    00337                         $this->PluginInfo[$PluginName]["Version"]) == 1)
    00338                 {
    00339                     # upgrade plugin
    00340                     $ErrMsg = $Plugin->Upgrade($this->PluginInfo[$PluginName]["Version"]);
    00341
    00342                     # if upgrade succeeded
    00343                     if ($ErrMsg == NULL)
    00344                     {
    00345                         # update plugin version in database
    00346                         $Attribs = $Plugin->GetAttributes();
    00347                         $this->DB->Query("UPDATE PluginInfo"
    00348                                 ." SET Version = '".addslashes($Attribs["Version"])."'"
    00349                                 ." WHERE BaseName = '".addslashes($PluginName)."'");
    00350                         $this->PluginInfo[$PluginName]["Version"] = $Attribs["Version"];
    00351                     }
    00352                     else
    00353                     {
    00354                         # disable plugin
    00355                         $this->PluginEnabled[$PluginName] = FALSE;
    00356
    00357                         # record error message about upgrade failure
    00358                         $this->ErrMsgs[$PluginName][] = "Upgrade of plugin <b>"
    00359                                 .$PluginName."</b> from version <i>"
    00360                                 .addslashes($this->PluginInfo[$PluginName]["Version"])
    00361                                 ."</i> to version <i>"
    00362                                 .addslashes($Attribs["Version"])."</i> failed: <i>"
    00363                                 .$ErrMsg."</i>";
    00364                     }
    00365                 }
    00366                 # else if plugin version is older than version in database
    00367                 elseif (version_compare($Attribs["Version"],
    00368                         $this->PluginInfo[$PluginName]["Version"]) == -1)
    00369                 {
    00370                     # disable plugin
    00371                     $this->PluginEnabled[$PluginName] = FALSE;
    00372
    00373                     # record error message about version conflict
    00374                     $this->ErrMsgs[$PluginName][] = "Plugin <b>"
    00375                             .$PluginName."</b> is older (<i>"
    00376                             .addslashes($Attribs["Version"])
    00377                             ."</i>) than previously-installed version (<i>"
    00378                             .addslashes($this->PluginInfo[$PluginName]["Version"])."</i>).";
    00379                 }
    00380             }
    00381         }
    00382     }
    00383
    00384     private function CheckDependencies()
    00385     {
    00386         # look until all enabled plugins check out okay
    00387         do
    00388         {
    00389             # start out assuming all plugins are okay
    00390             $AllOkay = TRUE;
    00391
    00392             # for each plugin
    00393             foreach ($this->Plugins as $PluginName => $Plugin)
    00394             {
    00395                 # if plugin is currently enabled
    00396                 if ($this->PluginEnabled[$PluginName])
    00397                 {
    00398                     # load plugin attributes
    00399                     if (!isset($Attribs[$PluginName]))
    00400                     {
    00401                         $Attribs[$PluginName] =
    00402                                 $this->Plugins[$PluginName]->GetAttributes();
    00403                     }
    00404
    00405                     # for each dependency for this plugin
    00406                     foreach ($Attribs[$PluginName]["Requires"]
    00407                             as $ReqName => $ReqVersion)
    00408                     {
    00409                         # if target plugin is not present or is disabled or is too old
    00410                         if (isset($this->Plugins[$ReqName])
    00411                                 && !isset($Attribs[$ReqName]))
    00412                         {
    00413                             $Attribs[$ReqName] =
    00414                                     $this->Plugins[$ReqName]->GetAttributes();
    00415                         }
    00416                         if (!isset($this->PluginEnabled[$ReqName])
    00417                                 || !$this->PluginEnabled[$ReqName]
    00418                                 || (version_compare($ReqVersion,
    00419                                         $Attribs[$ReqName]["Version"], ">")))
    00420                         {
    00421                             # add error message and disable plugin
    00422                             $this->ErrMsgs[$PluginName][] = "Plugin <i>"
    00423                                     .$ReqName." ".$ReqVersion."</i>"
    00424                                     ." required by <b>".$PluginName."</b>"
    00425                                     ." was not available.";
    00426                             $this->PluginEnabled[$PluginName] = FALSE;
    00427
    00428                             # set flag indicating plugin did not check out
    00429                             $AllOkay = FALSE;
    00430                         }
    00431                     }
    00432                 }
    00433             }
    00434         } while ($AllOkay == FALSE);
    00435     }
    00436
    00438     function FindPluginPhpFile($PageName)
    00439     {
    00440         return $this->FindPluginPageFile($PageName, "php");
    00441     }
    00445     function FindPluginHtmlFile($PageName)
    00446     {
    00447         return $this->FindPluginPageFile($PageName, "html");
    00448     }
    00451     private function FindPluginPageFile($PageName, $Suffix)
    00452     {
    00453         # set up return value assuming we will not find plugin page file
    00454         $ReturnValue["PageName"] = $PageName;
    00455
    00456         # look for plugin name and plugin page name in base page name
    00457         preg_match("/P_([A-Za-z].[A-Za-z0-9]*)_([A-Za-z0-9_-]+)/", $PageName, $Matches);
    00458
    00459         # if base page name contained name of existing plugin with its own subdirectory
    00460         if ((count($Matches) == 3) && isset($this->PluginDirs[$Matches[1]]))
    00461         {
    00462             # if PHP file with specified name exists in plugin subdirectory
    00463             $PageFile = $this->PluginDirs[$Matches[1]]."/".$Matches[2].".".$Suffix;
    00464             if (file_exists($PageFile))
    00465             {
    00466                 # set return value to contain full plugin PHP file name
    00467                 $ReturnValue["PageName"] = $PageFile;
    00468             }
    00469         }
    00470
    00471         # return array containing page name or page file name to caller
    00472         return $ReturnValue;
    00473     }
    00474 }
    00475
    00476 ?>