3 # FILE: PluginManager.php 5 # Part of the ScoutLib application support library 6 # Copyright 2009-2013 Edward Almasy and Internet Scout Research Group 7 # http://scout.wisc.edu 16 # ---- PUBLIC INTERFACE -------------------------------------------------- 25 public function __construct($AppFramework, $PluginDirectories)
27 # save framework and directory list for later use 28 $this->AF = $AppFramework;
30 $this->DirsToSearch = $PluginDirectories;
32 # get our own database handle 35 # hook into events to load plugin PHP and HTML files 36 $this->AF->HookEvent(
"EVENT_PHP_FILE_LOAD", array($this,
"FindPluginPhpFile"),
37 ApplicationFramework::ORDER_LAST);
38 $this->AF->HookEvent(
"EVENT_HTML_FILE_LOAD", array($this,
"FindPluginHtmlFile"),
39 ApplicationFramework::ORDER_LAST);
41 # tell PluginCaller helper object how to get to us 42 PluginCaller::$Manager = $this;
53 # look for plugin files 54 $PluginFiles = $this->FindPlugins($this->DirsToSearch);
56 # for each plugin found 57 foreach ($PluginFiles as $PluginName => $PluginFileName)
59 # attempt to load plugin 60 $Result = $this->LoadPlugin($PluginName, $PluginFileName);
62 # if errors were encountered during loading 63 if (is_array($Result))
66 $ErrMsgs[$PluginName][] = $Result;
70 # add plugin to list of loaded plugins 71 $this->Plugins[$PluginName] = $Result;
75 # check dependencies and drop any plugins with failed dependencies 76 $DepErrMsgs = $this->CheckDependencies($this->Plugins);
77 $DisabledPlugins = array();
78 foreach ($DepErrMsgs as $PluginName => $Msgs)
80 $DisabledPlugins[] = $PluginName;
81 foreach ($Msgs as $Msg)
83 $ErrMsgs[$PluginName][] = $Msg;
87 # sort plugins according to any loading order requests 88 $this->Plugins = $this->SortPluginsByInitializationPrecedence(
92 foreach ($this->Plugins as $PluginName => $Plugin)
94 # if plugin is loaded and enabled 95 if (!in_array($PluginName, $DisabledPlugins)
96 && $Plugin->IsEnabled())
98 # attempt to make plugin ready 101 $Result = $this->ReadyPlugin($Plugin);
103 catch (Exception $Except)
105 $Result = array(
"Uncaught Exception: ".$Except->getMessage());
108 # if making plugin ready failed 109 if ($Result !== NULL)
111 # save error messages 112 foreach ($Result as $Msg)
114 $ErrMsgs[$PluginName][] = $Msg;
119 # mark plugin as ready 120 $Plugin->IsReady(TRUE);
125 # check plugin dependencies again in case an install or upgrade failed 126 $DepErrMsgs = $this->CheckDependencies($this->Plugins, TRUE);
128 # for any plugins that were disabled because of dependencies 129 foreach ($DepErrMsgs as $PluginName => $Msgs)
131 # make sure all plugin hooks are undone 132 $this->UnhookPlugin($this->Plugins[$PluginName]);
134 # mark the plugin as unready 135 $this->Plugins[$PluginName]->IsReady(FALSE);
137 # record any errors that were reported 138 foreach ($Msgs as $Msg)
140 $ErrMsgs[$PluginName][] = $Msg;
144 # save plugin files names and any error messages for later use 145 $this->PluginFiles = $PluginFiles;
146 $this->ErrMsgs = $ErrMsgs;
148 # report to caller whether any problems were encountered 149 return count($ErrMsgs) ? FALSE : TRUE;
159 return $this->ErrMsgs;
170 public function GetPlugin($PluginName, $EvenIfNotReady = FALSE)
172 if (!$EvenIfNotReady && array_key_exists($PluginName, $this->Plugins)
173 && !$this->Plugins[$PluginName]->IsReady())
175 $Trace = debug_backtrace();
176 $Caller = basename($Trace[0][
"file"]).
":".$Trace[0][
"line"];
177 throw new Exception(
"Attempt to access uninitialized plugin " 178 .$PluginName.
" from ".$Caller);
180 return isset($this->Plugins[$PluginName])
181 ? $this->Plugins[$PluginName] : NULL;
190 return $this->Plugins;
202 return $this->
GetPlugin($this->PageFilePlugin);
212 # for each loaded plugin 214 foreach ($this->Plugins as $PluginName => $Plugin)
216 # retrieve plugin attributes 217 $Info[$PluginName] = $Plugin->GetAttributes();
219 # add in other values to attributes 220 $Info[$PluginName][
"Enabled"] = $Plugin->IsEnabled();
221 $Info[$PluginName][
"Installed"] = $Plugin->IsInstalled();
222 $Info[$PluginName][
"ClassFile"] = $this->PluginFiles[$PluginName];
225 # sort plugins by name 226 uasort($Info,
function ($A, $B) {
227 $AName = strtoupper($A[
"Name"]);
228 $BName = strtoupper($B[
"Name"]);
229 return ($AName == $BName) ? 0
230 : ($AName < $BName) ? -1 : 1;
233 # return plugin info to caller 244 $Dependents = array();
246 foreach ($AllAttribs as $Name => $Attribs)
248 if (array_key_exists($PluginName, $Attribs[
"Requires"]))
250 $Dependents[] = $Name;
252 $Dependents = array_merge($Dependents, $SubDependents);
264 $ActivePluginNames = array();
265 foreach ($this->Plugins as $PluginName => $Plugin)
267 if ($Plugin->IsReady())
269 $ActivePluginNames[] = $PluginName;
272 return $ActivePluginNames;
283 return !isset($this->Plugins[$PluginName]) ? FALSE
284 : $this->Plugins[$PluginName]->IsEnabled($NewValue);
297 # if plugin is installed 298 if ($this->Plugins[$PluginName]->IsInstalled())
300 # call uninstall method for plugin 301 $Result = $this->Plugins[$PluginName]->Uninstall();
303 # if plugin uninstall method succeeded 304 if ($Result === NULL)
306 # remove plugin info from database 307 $this->DB->Query(
"DELETE FROM PluginInfo" 308 .
" WHERE BaseName = '".addslashes($PluginName).
"'");
310 # drop our data for the plugin 311 unset($this->Plugins[$PluginName]);
312 unset($this->PluginFiles[$PluginName]);
316 # report results (if any) to caller 327 if (!is_callable($Func))
329 throw new InvalidArgumentException(
330 "Invalid configuration value loading function supplied.");
332 self::$CfgValueLoader = $Func;
336 # ---- PRIVATE INTERFACE ------------------------------------------------- 340 private $DirsToSearch;
341 private $ErrMsgs = array();
342 private $PageFilePlugin = NULL;
343 private $Plugins = array();
344 private $PluginFiles = array();
345 private $PluginHasDir = array();
347 static private $CfgValueLoader;
358 private function FindPlugins($DirsToSearch)
361 $PluginFiles = array();
362 foreach ($DirsToSearch as $Dir)
364 # if directory exists 367 # for each file in directory 368 $FileNames = scandir($Dir);
369 foreach ($FileNames as $FileName)
371 # if file looks like base plugin file 372 if (preg_match(
"/^[a-zA-Z_][a-zA-Z0-9_]*\.php$/", $FileName))
374 # if we do not already have a plugin with that name 375 $PluginName = substr($FileName, 0, -4);
376 if (!isset($PluginFiles[$PluginName]))
379 $PluginFiles[$PluginName] = $Dir.
"/".$FileName;
382 # else if file looks like plugin directory 383 elseif (is_dir($Dir.
"/".$FileName)
384 && preg_match(
"/^[a-zA-Z_][a-zA-Z0-9_]*/", $FileName))
386 # if there is a base plugin file in the directory 387 $PluginName = $FileName;
388 $PluginFile = $Dir.
"/".$PluginName.
"/".$PluginName.
".php";
389 if (file_exists($PluginFile))
392 $PluginFiles[$PluginName] = $PluginFile;
397 $this->ErrMsgs[$PluginName][] =
398 "Expected plugin file <i>".$PluginName.
".php</i> not" 399 .
" found in plugin subdirectory <i>" 400 .$Dir.
"/".$PluginName.
"</i>";
407 # return info about found plugins to caller 418 private function LoadPlugin($PluginName, $PluginFileName)
420 # bring in plugin class file 421 include_once($PluginFileName);
423 # if plugin class was not defined by file 424 if (!class_exists($PluginName))
426 $ErrMsgs[] =
"Expected class <i>".$PluginName
427 .
"</i> not found in plugin file <i>" 428 .$PluginFileName.
"</i>";
432 # if plugin class is not a valid descendant of base plugin class 433 if (!is_subclass_of($PluginName,
"Plugin"))
435 $ErrMsgs[] =
"Plugin <b>".$PluginName.
"</b>" 436 .
" could not be loaded because <i>".$PluginName.
"</i> class" 437 .
" was not a subclass of base <i>Plugin</i> class";
441 # instantiate and register the plugin 442 $Plugin =
new $PluginName($PluginName);
444 # check required plugin attributes 445 $RequiredAttribs = array(
"Name",
"Version");
446 $Attribs = $Plugin->GetAttributes();
447 foreach ($RequiredAttribs as $AttribName)
449 if (!strlen($Attribs[$AttribName]))
451 $ErrMsgs[] =
"Plugin <b>".$PluginName.
"</b>" 452 .
" could not be loaded because it" 453 .
" did not have a <i>" 454 .$AttribName.
"</i> attribute set.";
458 # if all required attributes were found 459 if (!isset($ErrMsgs))
461 # if plugin has its own subdirectory 462 $this->PluginHasDir[$PluginName] = preg_match(
463 "%/".$PluginName.
"/".$PluginName.
".php\$%",
464 $PluginFileName) ? TRUE : FALSE;
465 if ($this->PluginHasDir[$PluginName])
467 # if plugin has its own object directory 468 $Dir = dirname($PluginFileName);
469 if (is_dir($Dir.
"/objects"))
471 # add object directory to class autoloading list 472 ApplicationFramework::AddObjectDirectory($Dir.
"/objects");
476 # add plugin directory to class autoloading list 477 ApplicationFramework::AddObjectDirectory($Dir);
480 # if plugin has its own interface directory 481 $InterfaceDir = $Dir.
"/interface";
482 if (is_dir($InterfaceDir))
484 # scan contents of interface directory for 485 # entries other than the default default 486 $InterfaceSubdirsFound = FALSE;
487 foreach (scandir($InterfaceDir) as $Entry)
489 if (($Entry !=
"default") && ($Entry[0] !=
"."))
491 $InterfaceSubdirsFound = TRUE;
496 # if entries found other than the default default 497 if ($InterfaceSubdirsFound)
499 # add directory to those scanned for interfaces 500 $this->AF->AddInterfaceDirectories(
501 array($InterfaceDir.
"/%ACTIVEUI%/",
502 $InterfaceDir.
"/%DEFAULTUI%/"));
505 # add plugin interface object directories if present 506 $ActiveUI = $this->AF->ActiveUserInterface();
507 $DefaultUI = $this->AF->DefaultUserInterface();
508 if (is_dir($InterfaceDir.
"/".$ActiveUI.
"/objects"))
510 ApplicationFramework::AddObjectDirectory(
511 $InterfaceDir.
"/%ACTIVEUI%/objects");
513 if (is_dir($InterfaceDir.
"/".$DefaultUI.
"/objects"))
515 ApplicationFramework::AddObjectDirectory(
516 $InterfaceDir.
"/%DEFAULTUI%/objects");
519 # add plugin interface include directories if present 520 if (is_dir($InterfaceDir.
"/".$DefaultUI.
"/include"))
522 $this->AF->AddIncludeDirectories(
523 $InterfaceDir.
"/%DEFAULTUI%/include");
525 if (is_dir($InterfaceDir.
"/".$ActiveUI.
"/include"))
527 $this->AF->AddIncludeDirectories(
528 $InterfaceDir.
"/%ACTIVEUI%/include");
536 # return loaded plugin or error messages, as appropriate 537 return isset($ErrMsgs) ? $ErrMsgs : $Plugin;
545 private function ReadyPlugin(&$Plugin)
547 # install or upgrade plugin if needed 548 $PluginInstalled = $this->InstallPlugin($Plugin);
550 # if install/upgrade failed 551 if (is_string($PluginInstalled))
553 # report errors to caller 554 return array($PluginInstalled);
557 # set up plugin configuration options 558 $ErrMsgs = $Plugin->SetUpConfigOptions();
560 # if plugin configuration setup failed 561 if ($ErrMsgs !== NULL)
563 # report errors to caller 564 return is_array($ErrMsgs) ? $ErrMsgs : array($ErrMsgs);
567 # set default configuration values if necessary 568 if ($PluginInstalled)
570 $this->SetPluginDefaultConfigValues($Plugin);
573 # initialize the plugin 574 $ErrMsgs = $Plugin->Initialize();
576 # if initialization failed 577 if ($ErrMsgs !== NULL)
579 # report errors to caller 580 return is_array($ErrMsgs) ? $ErrMsgs : array($ErrMsgs);
583 # register and hook any events for plugin 584 $ErrMsgs = $this->HookPlugin($Plugin);
586 # make sure all hooks are undone if hooking failed 587 if ($ErrMsgs !== NULL)
589 $this->UnhookPlugin($Plugin);
592 # report result to caller 601 private function HookPlugin(&$Plugin)
603 # register any events declared by plugin 604 $Events = $Plugin->DeclareEvents();
605 if (count($Events)) { $this->AF->RegisterEvent($Events); }
607 # if plugin has events that need to be hooked 608 $EventsToHook = $Plugin->HookEvents();
609 if (count($EventsToHook))
613 foreach ($EventsToHook as $EventName => $PluginMethods)
615 # for each method to hook for the event 616 if (!is_array($PluginMethods))
617 { $PluginMethods = array($PluginMethods); }
618 foreach ($PluginMethods as $PluginMethod)
620 # if the event only allows static callbacks 621 if ($this->AF->IsStaticOnlyEvent($EventName))
623 # hook event with shell for static callback 624 $Caller =
new PluginCaller(
625 $Plugin->GetBaseName(), $PluginMethod);
626 $Result = $this->AF->HookEvent(
628 array($Caller,
"CallPluginMethod"));
633 $Result = $this->AF->HookEvent(
634 $EventName, array($Plugin, $PluginMethod));
638 if ($Result === FALSE)
640 $ErrMsgs[] =
"Unable to hook requested event <i>" 641 .$EventName.
"</i> for plugin <b>" 642 .$Plugin->GetBaseName().
"</b>";
647 # if event hook setup failed 650 # report errors to caller 655 # report success to caller 663 private function UnhookPlugin(&$Plugin)
665 # if plugin had events to hook 666 $EventsToHook = $Plugin->HookEvents();
667 if (count($EventsToHook))
671 foreach ($EventsToHook as $EventName => $PluginMethods)
673 # for each method to hook for the event 674 if (!is_array($PluginMethods))
675 { $PluginMethods = array($PluginMethods); }
676 foreach ($PluginMethods as $PluginMethod)
678 # if the event only allows static callbacks 679 if ($this->AF->IsStaticOnlyEvent($EventName))
681 # unhook event with shell for static callback 682 $Caller =
new PluginCaller(
683 $Plugin->GetBaseName(), $PluginMethod);
684 $this->AF->UnhookEvent($EventName,
685 array($Caller,
"CallPluginMethod"));
690 $this->AF->UnhookEvent(
691 $EventName, array($Plugin, $PluginMethod));
705 private function InstallPlugin(&$Plugin)
707 # if plugin has not been installed 708 $InstallOrUpgradePerformed = FALSE;
709 $PluginName = $Plugin->GetBaseName();
710 $Attribs = $Plugin->GetAttributes();
711 $LockName = __CLASS__.
":Install:".$PluginName;
712 if (!$Plugin->IsInstalled())
714 # set default values if present 715 $this->SetPluginDefaultConfigValues($Plugin, TRUE);
717 # try to get lock to prevent anyone else from trying to run 718 # install or upgrade at the same time 719 $GotLock = $this->AF->GetLock($LockName, FALSE);
721 # if could not get lock 725 return "Installation of plugin <b>" 726 .$PluginName.
"</b> in progress.";
730 $ErrMsg = $Plugin->Install();
731 $InstallOrUpgradePerformed = TRUE;
733 # if install succeeded 736 # mark plugin as installed 737 $Plugin->IsInstalled(TRUE);
740 $this->AF->ReleaseLock($LockName);
745 $this->AF->ReleaseLock($LockName);
747 # return error message about installation failure 748 return "Installation of plugin <b>" 749 .$PluginName.
"</b> failed: <i>".$ErrMsg.
"</i>";
754 # if plugin version is newer than version in database 755 if (version_compare($Attribs[
"Version"],
756 $Plugin->InstalledVersion()) == 1)
758 # set default values for any new configuration settings 759 $this->SetPluginDefaultConfigValues($Plugin);
761 # try to get lock to prevent anyone else from trying to run 762 # upgrade or install at the same time 763 $GotLock = $this->AF->GetLock($LockName, FALSE);
765 # if could not get lock 769 return "Upgrade of plugin <b>" 770 .$PluginName.
"</b> in progress.";
774 $ErrMsg = $Plugin->Upgrade($Plugin->InstalledVersion());
775 $InstallOrUpgradePerformed = TRUE;
777 # if upgrade succeeded 778 if ($ErrMsg === NULL)
780 # update plugin version in database 781 $Plugin->InstalledVersion($Attribs[
"Version"]);
784 $this->AF->ReleaseLock($LockName);
789 $this->AF->ReleaseLock($LockName);
791 # report error message about upgrade failure 792 return "Upgrade of plugin <b>" 793 .$PluginName.
"</b> from version <i>" 794 .addslashes($Plugin->InstalledVersion())
795 .
"</i> to version <i>" 796 .addslashes($Attribs[
"Version"]).
"</i> failed: <i>" 800 # else if plugin version is older than version in database 801 elseif (version_compare($Attribs[
"Version"],
802 $Plugin->InstalledVersion()) == -1)
804 # return error message about version conflict 806 .$PluginName.
"</b> is older (<i>" 807 .addslashes($Attribs[
"Version"])
808 .
"</i>) than previously-installed version (<i>" 809 .addslashes($Plugin->InstalledVersion())
814 # report result to caller 815 return $InstallOrUpgradePerformed;
825 private function SetPluginDefaultConfigValues($Plugin, $Overwrite = FALSE)
827 # if plugin has configuration info 828 $Attribs = $Plugin->GetAttributes();
829 if (isset($Attribs[
"CfgSetup"]))
831 foreach ($Attribs[
"CfgSetup"] as $CfgValName => $CfgSetup)
833 if (isset($CfgSetup[
"Default"]) && ($Overwrite
834 || ($Plugin->ConfigSetting($CfgValName) === NULL)))
836 if (isset(self::$CfgValueLoader))
838 $Plugin->ConfigSetting($CfgValName,
839 call_user_func(self::$CfgValueLoader,
840 $CfgSetup[
"Type"], $CfgSetup[
"Default"]));
844 $Plugin->ConfigSetting($CfgValName, $CfgSetup[
"Default"]);
858 private function CheckDependencies($Plugins, $CheckReady = FALSE)
860 # look until all enabled plugins check out okay 864 # start out assuming all plugins are okay 868 foreach ($Plugins as $PluginName => $Plugin)
870 # if plugin is enabled and not checking for ready 872 if ($Plugin->IsEnabled() && (!$CheckReady || $Plugin->IsReady()))
874 # load plugin attributes 875 if (!isset($Attribs[$PluginName]))
877 $Attribs[$PluginName] = $Plugin->GetAttributes();
880 # for each dependency for this plugin 881 foreach ($Attribs[$PluginName][
"Requires"]
882 as $ReqName => $ReqVersion)
884 # handle PHP version requirements 885 if ($ReqName ==
"PHP")
887 if (version_compare($ReqVersion, phpversion(),
">"))
889 $ErrMsgs[$PluginName][] =
"PHP version " 890 .
"<i>".$ReqVersion.
"</i>" 891 .
" required by <b>".$PluginName.
"</b>" 892 .
" was not available. (Current PHP version" 893 .
" is <i>".phpversion().
"</i>.)";
896 # handle PHP extension requirements 897 elseif (preg_match(
"/^PHPX_/", $ReqName))
899 list($Dummy, $ExtensionName) = explode(
"_", $ReqName, 2);
900 if (!extension_loaded($ExtensionName))
902 $ErrMsgs[$PluginName][] =
"PHP extension " 903 .
"<i>".$ExtensionName.
"</i>" 904 .
" required by <b>".$PluginName.
"</b>" 905 .
" was not available.";
907 elseif (($ReqVersion !== TRUE)
908 && (phpversion($ExtensionName) !== FALSE)
909 && version_compare($ReqVersion,
910 phpversion($ExtensionName),
">"))
912 $ErrMsgs[$PluginName][] =
"PHP extension " 913 .
"<i>".$ExtensionName.
"</i>" 914 .
" version <i>".$ReqVersion.
"</i>" 915 .
" required by <b>".$PluginName.
"</b>" 916 .
" was not available. (Current version" 917 .
" of extension <i>".$ExtensionName.
"</i>" 918 .
" is <i>".phpversion($ExtensionName).
"</i>.)";
921 # handle dependencies on other plugins 924 # load plugin attributes if not already loaded 925 if (isset($Plugins[$ReqName])
926 && !isset($Attribs[$ReqName]))
929 $Plugins[$ReqName]->GetAttributes();
932 # if target plugin is not present or is too old 934 # or (if appropriate) is not ready 935 if (!isset($Plugins[$ReqName])
936 || version_compare($ReqVersion,
937 $Attribs[$ReqName][
"Version"],
">")
938 || !$Plugins[$ReqName]->IsEnabled()
940 && !$Plugins[$ReqName]->IsReady()))
943 $ErrMsgs[$PluginName][] =
"Plugin <i>" 944 .$ReqName.
" ".$ReqVersion.
"</i>" 945 .
" required by <b>".$PluginName.
"</b>" 946 .
" was not available.";
950 # if problem was found with plugin 951 if (isset($ErrMsgs[$PluginName]))
953 # remove plugin from our list 954 unset($Plugins[$PluginName]);
956 # set flag to indicate a plugin had to be dropped 962 }
while ($AllOkay == FALSE);
964 # return messages about any dropped plugins back to caller 976 private function SortPluginsByInitializationPrecedence($Plugins)
978 # load plugin attributes 979 foreach ($Plugins as $PluginName => $Plugin)
981 $PluginAttribs[$PluginName] = $Plugin->GetAttributes();
984 # determine initialization order 985 $PluginsAfterUs = array();
986 foreach ($PluginAttribs as $PluginName => $Attribs)
988 foreach ($Attribs[
"InitializeBefore"] as $OtherPluginName)
990 $PluginsAfterUs[$PluginName][] = $OtherPluginName;
992 foreach ($Attribs[
"InitializeAfter"] as $OtherPluginName)
994 $PluginsAfterUs[$OtherPluginName][] = $PluginName;
998 # infer other initialization order cues from lists of required plugins 999 foreach ($PluginAttribs as $PluginName => $Attribs)
1001 # for each required plugin 1002 foreach ($Attribs[
"Requires"]
1003 as $RequiredPluginName => $RequiredPluginVersion)
1005 # skip the requirement if it it not for another known plugin 1006 if (!isset($PluginAttribs[$RequiredPluginName]))
1011 # if there is not a requirement in the opposite direction 1012 if (!array_key_exists($PluginName,
1013 $PluginAttribs[$RequiredPluginName][
"Requires"]))
1015 # if the required plugin is not scheduled to be after us 1016 if (!array_key_exists($PluginName, $PluginsAfterUs)
1017 || !in_array($RequiredPluginName,
1018 $PluginsAfterUs[$PluginName]))
1020 # if we are not already scheduled to be after the required plugin 1021 if (!array_key_exists($PluginName, $PluginsAfterUs)
1022 || !in_array($RequiredPluginName,
1023 $PluginsAfterUs[$PluginName]))
1025 # schedule us to be after the required plugin 1026 $PluginsAfterUs[$RequiredPluginName][] =
1034 # keep track of those plugins we have yet to do and those that are done 1035 $UnsortedPlugins = array_keys($Plugins);
1036 $PluginsProcessed = array();
1038 # limit the number of iterations of the plugin ordering loop 1039 # to 10 times the number of plugins we have 1040 $MaxIterations = 10 * count($UnsortedPlugins);
1041 $IterationCount = 0;
1043 # iterate through all the plugins that need processing 1044 while (($NextPlugin = array_shift($UnsortedPlugins)) !== NULL)
1046 # check to be sure that we're not looping forever 1048 if ($IterationCount > $MaxIterations)
1050 throw new Exception(
1051 "Max iteration count exceeded trying to determine plugin" 1052 .
" loading order. Is there a dependency loop?");
1055 # if no plugins require this one, it can go last 1056 if (!isset($PluginsAfterUs[$NextPlugin]))
1058 $PluginsProcessed[$NextPlugin] = $MaxIterations;
1062 # for plugins that are required by others 1063 $Index = $MaxIterations;
1064 foreach ($PluginsAfterUs[$NextPlugin] as $GoBefore)
1066 if (!isset($PluginsProcessed[$GoBefore]))
1068 # if there is something that requires us which hasn't 1069 # yet been assigned an order, then we can't determine 1070 # our own place on this iteration 1071 array_push($UnsortedPlugins, $NextPlugin);
1076 # otherwise, make sure that we're loaded 1077 # before the earliest of the things that require us 1078 $Index = min($Index, $PluginsProcessed[$GoBefore] - 1);
1081 $PluginsProcessed[$NextPlugin] = $Index;
1085 # arrange plugins according to our ordering 1086 asort($PluginsProcessed, SORT_NUMERIC);
1087 $SortedPlugins = array();
1088 foreach ($PluginsProcessed as $PluginName => $SortOrder)
1090 $SortedPlugins[$PluginName] = $Plugins[$PluginName];
1093 # return sorted list to caller 1094 return $SortedPlugins;
1105 public function FindPluginPhpFile($PageName)
1107 # build list of possible locations for file 1109 "local/plugins/%PLUGIN%/pages/%PAGE%.php",
1110 "plugins/%PLUGIN%/pages/%PAGE%.php",
1111 "local/plugins/%PLUGIN%/%PAGE%.php",
1112 "plugins/%PLUGIN%/%PAGE%.php",
1115 # look for file and return (possibly) updated page to caller 1116 return $this->FindPluginPageFile($PageName, $Locations);
1128 public function FindPluginHtmlFile($PageName)
1130 # build list of possible locations for file 1132 "local/plugins/%PLUGIN%/interface/%ACTIVEUI%/%PAGE%.html",
1133 "plugins/%PLUGIN%/interface/%ACTIVEUI%/%PAGE%.html",
1134 "local/plugins/%PLUGIN%/interface/%DEFAULTUI%/%PAGE%.html",
1135 "plugins/%PLUGIN%/interface/%DEFAULTUI%/%PAGE%.html",
1136 "local/plugins/%PLUGIN%/%PAGE%.html",
1137 "plugins/%PLUGIN%/%PAGE%.html",
1141 $Params = $this->FindPluginPageFile($PageName, $Locations);
1143 # if plugin HTML file was found 1144 if ($Params[
"PageName"] != $PageName)
1146 # add subdirectories for plugin to search paths 1147 $Dir = preg_replace(
"%^local/%",
"", dirname($Params[
"PageName"]));
1148 $this->AF->AddImageDirectories(array(
1149 "local/".$Dir.
"/images",
1152 $this->AF->AddIncludeDirectories(array(
1153 "local/".$Dir.
"/include",
1156 $this->AF->AddFunctionDirectories(array(
1157 "local/".$Dir.
"/include",
1162 # return possibly revised HTML file name to caller 1177 private function FindPluginPageFile($PageName, $Locations)
1179 # set up return value assuming we will not find plugin page file 1180 $ReturnValue[
"PageName"] = $PageName;
1182 # look for plugin name and plugin page name in base page name 1183 preg_match(
"/P_([A-Za-z].[A-Za-z0-9]*)_([A-Za-z0-9_-]+)/", $PageName, $Matches);
1185 # if plugin name and plugin page name were found and plugin name is valid 1186 if (count($Matches) == 3)
1188 # if plugin is valid and enabled and has its own subdirectory 1189 $PluginName = $Matches[1];
1190 if (isset($this->Plugins[$PluginName])
1191 && $this->PluginHasDir[$PluginName]
1192 && $this->Plugins[$PluginName]->IsEnabled())
1194 # for each possible location 1195 $PageName = $Matches[2];
1196 $ActiveUI = $this->AF->ActiveUserInterface();
1197 $DefaultUI = $this->AF->DefaultUserInterface();
1198 foreach ($Locations as $Loc)
1200 # make any needed substitutions into path 1201 $FileName = str_replace(
1202 array(
"%DEFAULTUI%",
"%ACTIVEUI%",
"%PLUGIN%",
"%PAGE%"),
1203 array($DefaultUI, $ActiveUI, $PluginName, $PageName),
1206 # if file exists in this location 1207 if (file_exists($FileName))
1209 # set return value to contain full plugin page file name 1210 $ReturnValue[
"PageName"] = $FileName;
1212 # save plugin name as home of current page 1213 $this->PageFilePlugin = $PluginName;
1215 # set G_Plugin to plugin associated with current page 1225 # return array containing page name or page file name to caller 1226 return $ReturnValue;
1250 public function __construct($PluginName, $MethodName)
1252 $this->PluginName = $PluginName;
1253 $this->MethodName = $MethodName;
1261 public function CallPluginMethod()
1263 $Args = func_get_args();
1264 $Plugin = self::$Manager->GetPlugin($this->PluginName);
1265 return call_user_func_array(array($Plugin, $this->MethodName), $Args);
1272 public function GetCallbackAsText()
1274 return $this->PluginName.
"::".$this->MethodName;
1282 public function __sleep()
1284 return array(
"PluginName",
"MethodName");
1288 static public $Manager;
1290 private $PluginName;
1291 private $MethodName;
GetErrorMessages()
Retrieve any error messages generated during plugin loading.
Manager to load and invoke plugins.
GetPlugin($PluginName, $EvenIfNotReady=FALSE)
Retrieve specified plugin.
SQL database abstraction object with smart query caching.
UninstallPlugin($PluginName)
Uninstall plugin and (optionally) delete any associated data.
GetDependents($PluginName)
Returns a list of plugins dependent on the specified plugin.
GetActivePluginList()
Get list of active (i.e.
GetPluginAttributes()
Retrieve info about currently loaded plugins.
PluginEnabled($PluginName, $NewValue=NULL)
Get/set whether specified plugin is enabled.
__construct($AppFramework, $PluginDirectories)
PluginManager class constructor.
GetPluginForCurrentPage()
Retrieve plugin for current page (if any).
GetPlugins()
Retrieve all loaded plugins.
static SetApplicationFramework($AF)
Set the application framework to be referenced within plugins.
static SetConfigValueLoader($Func)
Set function to load plugin configuration values from data.
LoadPlugins()
Load and initialize plugins.