CWIS Developer Documentation

- Home
- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

PluginManager.php
Go to the documentation of this file.
1 <?PHP
2 #
3 # FILE: PluginManager.php
4 #
5 # Part of the ScoutLib application support library
6 # Copyright 2009-2013 Edward Almasy and Internet Scout Research Group
7 # http://scout.wisc.edu
8 #
9
13 class PluginManager
14 {
15
16 # ---- PUBLIC INTERFACE --------------------------------------------------
17
25 public function __construct($AppFramework, $PluginDirectories)
26 {
27 # save framework and directory list for later use
28 $this->AF = $AppFramework;
29 Plugin::SetApplicationFramework($AppFramework);
30 $this->DirsToSearch = $PluginDirectories;
31
32 # get our own database handle
33 $this->DB = new Database();
34
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);
40
41 # tell PluginCaller helper object how to get to us
42 PluginCaller::$Manager = $this;
43 }
44
49 public function LoadPlugins()
50 {
51 $ErrMsgs = array();
52
53 # look for plugin files
54 $PluginFiles = $this->FindPlugins($this->DirsToSearch);
55
56 # for each plugin found
57 foreach ($PluginFiles as $PluginName => $PluginFileName)
58 {
59 # attempt to load plugin
60 $Result = $this->LoadPlugin($PluginName, $PluginFileName);
61
62 # if errors were encountered during loading
63 if (is_array($Result))
64 {
65 # save errors
66 $ErrMsgs[$PluginName][] = $Result;
67 }
68 else
69 {
70 # add plugin to list of loaded plugins
71 $this->Plugins[$PluginName] = $Result;
72 }
73 }
74
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)
79 {
80 $DisabledPlugins[] = $PluginName;
81 foreach ($Msgs as $Msg)
82 {
83 $ErrMsgs[$PluginName][] = $Msg;
84 }
85 }
86
87 # sort plugins according to any loading order requests
88 $this->Plugins = $this->SortPluginsByInitializationPrecedence(
89 $this->Plugins);
90
91 # for each plugin
92 foreach ($this->Plugins as $PluginName => $Plugin)
93 {
94 # if plugin is loaded and enabled
95 if (!in_array($PluginName, $DisabledPlugins)
96 && $Plugin->IsEnabled())
97 {
98 # attempt to make plugin ready
99 $Result = $this->ReadyPlugin($Plugin);
100
101 # if making plugin ready failed
102 if ($Result !== NULL)
103 {
104 # save error messages
105 foreach ($Result as $Msg)
106 {
107 $ErrMsgs[$PluginName][] = $Msg;
108 }
109 }
110 else
111 {
112 # mark plugin as ready
113 $Plugin->IsReady(TRUE);
114 }
115 }
116 }
117
118 # check plugin dependencies again in case an install or upgrade failed
119 $DepErrMsgs = $this->CheckDependencies($this->Plugins, TRUE);
120 foreach ($DepErrMsgs as $PluginName => $Msgs)
121 {
122 $this->Plugins[$PluginName]->IsReady(FALSE);
123 foreach ($Msgs as $Msg)
124 {
125 $ErrMsgs[$PluginName][] = $Msg;
126 }
127 }
128
129 # save plugin files names and any error messages for later use
130 $this->PluginFiles = $PluginFiles;
131 $this->ErrMsgs = $ErrMsgs;
132
133 # report to caller whether any problems were encountered
134 return count($ErrMsgs) ? FALSE : TRUE;
135 }
136
142 public function GetErrorMessages()
143 {
144 return $this->ErrMsgs;
145 }
146
155 public function GetPlugin($PluginName, $EvenIfNotReady = FALSE)
156 {
157 if (!$EvenIfNotReady && array_key_exists($PluginName, $this->Plugins)
158 && !$this->Plugins[$PluginName]->IsReady())
159 {
160 $Trace = debug_backtrace();
161 $Caller = basename($Trace[0]["file"]).":".$Trace[0]["line"];
162 throw new Exception("Attempt to access uninitialized plugin "
163 .$PluginName." from ".$Caller);
164 }
165 return isset($this->Plugins[$PluginName])
166 ? $this->Plugins[$PluginName] : NULL;
167 }
168
173 public function GetPlugins()
174 {
175 return $this->Plugins;
176 }
177
185 public function GetPluginForCurrentPage()
186 {
187 return $this->GetPlugin($this->PageFilePlugin);
188 }
189
195 public function GetPluginAttributes()
196 {
197 # for each loaded plugin
198 $Info = array();
199 foreach ($this->Plugins as $PluginName => $Plugin)
200 {
201 # retrieve plugin attributes
202 $Info[$PluginName] = $Plugin->GetAttributes();
203
204 # add in other values to attributes
205 $Info[$PluginName]["Enabled"] = $Plugin->IsEnabled();
206 $Info[$PluginName]["Installed"] = $Plugin->IsInstalled();
207 $Info[$PluginName]["ClassFile"] = $this->PluginFiles[$PluginName];
208 }
209
210 # sort plugins by name
211 uasort($Info, function ($A, $B) {
212 $AName = strtoupper($A["Name"]);
213 $BName = strtoupper($B["Name"]);
214 return ($AName == $BName) ? 0
215 : ($AName < $BName) ? -1 : 1;
216 });
217
218 # return plugin info to caller
219 return $Info;
220 }
221
227 public function GetDependents($PluginName)
228 {
229 $Dependents = array();
230 $AllAttribs = $this->GetPluginAttributes();
231 foreach ($AllAttribs as $Name => $Attribs)
232 {
233 if (array_key_exists($PluginName, $Attribs["Requires"]))
234 {
235 $Dependents[] = $Name;
236 $SubDependents = $this->GetDependents($Name);
237 $Dependents = array_merge($Dependents, $SubDependents);
238 }
239 }
240 return $Dependents;
241 }
242
247 public function GetActivePluginList()
248 {
249 $ActivePluginNames = array();
250 foreach ($this->Plugins as $PluginName => $Plugin)
251 {
252 if ($Plugin->IsReady())
253 {
254 $ActivePluginNames[] = $PluginName;
255 }
256 }
257 return $ActivePluginNames;
258 }
259
266 public function PluginEnabled($PluginName, $NewValue = NULL)
267 {
268 return !isset($this->Plugins[$PluginName]) ? FALSE
269 : $this->Plugins[$PluginName]->IsEnabled($NewValue);
270 }
271
277 public function UninstallPlugin($PluginName)
278 {
279 # assume success
280 $Result = NULL;
281
282 # if plugin is installed
283 if ($this->Plugins[$PluginName]->IsInstalled())
284 {
285 # call uninstall method for plugin
286 $Result = $this->Plugins[$PluginName]->Uninstall();
287
288 # if plugin uninstall method succeeded
289 if ($Result === NULL)
290 {
291 # remove plugin info from database
292 $this->DB->Query("DELETE FROM PluginInfo"
293 ." WHERE BaseName = '".addslashes($PluginName)."'");
294
295 # drop our data for the plugin
296 unset($this->Plugins[$PluginName]);
297 unset($this->PluginFiles[$PluginName]);
298 }
299 }
300
301 # report results (if any) to caller
302 return $Result;
303 }
304
305
306 # ---- PRIVATE INTERFACE -------------------------------------------------
307
308 private $AF;
309 private $DB;
310 private $DirsToSearch;
311 private $ErrMsgs = array();
312 private $PageFilePlugin = NULL;
313 private $Plugins = array();
314 private $PluginFiles = array();
315 private $PluginHasDir = array();
316
324 private function FindPlugins($DirsToSearch)
325 {
326 # for each directory
327 $PluginFiles = array();
328 foreach ($DirsToSearch as $Dir)
329 {
330 # if directory exists
331 if (is_dir($Dir))
332 {
333 # for each file in directory
334 $FileNames = scandir($Dir);
335 foreach ($FileNames as $FileName)
336 {
337 # if file looks like base plugin file
338 if (preg_match("/^[a-zA-Z_][a-zA-Z0-9_]*\.php$/", $FileName))
339 {
340 # add file to list
341 $PluginName = preg_replace("/\.php$/", "", $FileName);
342 $PluginFiles[$PluginName] = $Dir."/".$FileName;
343 }
344 # else if file looks like plugin directory
345 elseif (is_dir($Dir."/".$FileName)
346 && preg_match("/^[a-zA-Z_][a-zA-Z0-9_]*/", $FileName))
347 {
348 # if there is a base plugin file in the directory
349 $PluginName = $FileName;
350 $PluginFile = $Dir."/".$PluginName."/".$PluginName.".php";
351 if (file_exists($PluginFile))
352 {
353 # add file to list
354 $PluginFiles[$PluginName] = $PluginFile;
355 }
356 else
357 {
358 # record error
359 $this->ErrMsgs[$PluginName][] =
360 "Expected plugin file <i>".$PluginName.".php</i> not"
361 ." found in plugin subdirectory <i>"
362 .$Dir."/".$PluginName."</i>";
363 }
364 }
365 }
366 }
367 }
368
369 # return info about found plugins to caller
370 return $PluginFiles;
371 }
372
380 private function LoadPlugin($PluginName, $PluginFileName)
381 {
382 # bring in plugin class file
383 include_once($PluginFileName);
384
385 # if plugin class was not defined by file
386 if (!class_exists($PluginName))
387 {
388 $ErrMsgs[] = "Expected class <i>".$PluginName
389 ."</i> not found in plugin file <i>"
390 .$PluginFileName."</i>";
391 }
392 else
393 {
394 # if plugin class is not a valid descendant of base plugin class
395 if (!is_subclass_of($PluginName, "Plugin"))
396 {
397 $ErrMsgs[] = "Plugin <b>".$PluginName."</b>"
398 ." could not be loaded because <i>".$PluginName."</i> class"
399 ." was not a subclass of base <i>Plugin</i> class";
400 }
401 else
402 {
403 # instantiate and register the plugin
404 $Plugin = new $PluginName($PluginName);
405
406 # check required plugin attributes
407 $RequiredAttribs = array("Name", "Version");
408 $Attribs = $Plugin->GetAttributes();
409 foreach ($RequiredAttribs as $AttribName)
410 {
411 if (!strlen($Attribs[$AttribName]))
412 {
413 $ErrMsgs[] = "Plugin <b>".$PluginName."</b>"
414 ." could not be loaded because it"
415 ." did not have a <i>"
416 .$AttribName."</i> attribute set.";
417 }
418 }
419
420 # if all required attributes were found
421 if (!isset($ErrMsgs))
422 {
423 # if plugin has its own subdirectory
424 $this->PluginHasDir[$PluginName] = preg_match(
425 "%/".$PluginName."/".$PluginName.".php\$%",
426 $PluginFileName) ? TRUE : FALSE;
427 if ($this->PluginHasDir[$PluginName])
428 {
429 # if plugin has its own object directory
430 $Dir = dirname($PluginFileName);
431 if (is_dir($Dir."/objects"))
432 {
433 # add object directory to class autoloading list
434 ApplicationFramework::AddObjectDirectory($Dir."/objects");
435 }
436 else
437 {
438 # add plugin directory to class autoloading list
439 ApplicationFramework::AddObjectDirectory($Dir);
440 }
441
442 # if plugin has its own interface directory
443 $InterfaceDir = $Dir."/interface";
444 if (is_dir($InterfaceDir))
445 {
446 # scan contents of interface directory for
447 # entries other than the default default
448 $InterfaceSubdirsFound = FALSE;
449 foreach (scandir($InterfaceDir) as $Entry)
450 {
451 if (($Entry != "default") && ($Entry[0] != "."))
452 {
453 $InterfaceSubdirsFound = TRUE;
454 break;
455 }
456 }
457
458 # if entries found other than the default default
459 if ($InterfaceSubdirsFound)
460 {
461 # add directory to those scanned for interfaces
462 $this->AF->AddInterfaceDirectories(
463 array($InterfaceDir."/%ACTIVEUI%/",
464 $InterfaceDir."/%DEFAULTUI%/"));
465 }
466
467 # add plugin interface object directories if present
468 $ActiveUI = $this->AF->ActiveUserInterface();
469 $DefaultUI = $this->AF->DefaultUserInterface();
470 if (is_dir($InterfaceDir."/".$ActiveUI."/objects"))
471 {
472 ApplicationFramework::AddObjectDirectory(
473 $InterfaceDir."/%ACTIVEUI%/objects");
474 }
475 if (is_dir($InterfaceDir."/".$DefaultUI."/objects"))
476 {
477 ApplicationFramework::AddObjectDirectory(
478 $InterfaceDir."/%DEFAULTUI%/objects");
479 }
480
481 # add plugin interface include directories if present
482 if (is_dir($InterfaceDir."/".$DefaultUI."/include"))
483 {
484 $this->AF->AddIncludeDirectories(
485 $InterfaceDir."/%DEFAULTUI%/include");
486 }
487 if (is_dir($InterfaceDir."/".$ActiveUI."/include"))
488 {
489 $this->AF->AddIncludeDirectories(
490 $InterfaceDir."/%ACTIVEUI%/include");
491 }
492 }
493 }
494 }
495 }
496 }
497
498 # return loaded plugin or error messages, as appropriate
499 return isset($ErrMsgs) ? $ErrMsgs : $Plugin;
500 }
501
507 private function ReadyPlugin(&$Plugin)
508 {
509 # install or upgrade plugin if needed
510 $PluginInstalled = $this->InstallPlugin($Plugin);
511
512 # if install/upgrade failed
513 if (is_string($PluginInstalled))
514 {
515 # report errors to caller
516 return array($PluginInstalled);
517 }
518
519 # set up plugin configuration options
520 $ErrMsgs = $Plugin->SetUpConfigOptions();
521
522 # if plugin configuration setup failed
523 if ($ErrMsgs !== NULL)
524 {
525 # report errors to caller
526 return is_array($ErrMsgs) ? $ErrMsgs : array($ErrMsgs);
527 }
528
529 # set default configuration values if necessary
530 if ($PluginInstalled)
531 {
532 $this->SetPluginDefaultConfigValues($Plugin);
533 }
534
535 # initialize the plugin
536 $ErrMsgs = $Plugin->Initialize();
537
538 # if initialization failed
539 if ($ErrMsgs !== NULL)
540 {
541 # report errors to caller
542 return is_array($ErrMsgs) ? $ErrMsgs : array($ErrMsgs);
543 }
544
545 # register any events declared by plugin
546 $Events = $Plugin->DeclareEvents();
547 if (count($Events)) { $this->AF->RegisterEvent($Events); }
548
549 # if plugin has events that need to be hooked
550 $EventsToHook = $Plugin->HookEvents();
551 if (count($EventsToHook))
552 {
553 # for each event
554 $ErrMsgs = array();
555 foreach ($EventsToHook as $EventName => $PluginMethods)
556 {
557 # for each method to hook for the event
558 if (!is_array($PluginMethods))
559 { $PluginMethods = array($PluginMethods); }
560 foreach ($PluginMethods as $PluginMethod)
561 {
562 # if the event only allows static callbacks
563 if ($this->AF->IsStaticOnlyEvent($EventName))
564 {
565 # hook event with shell for static callback
566 $Caller = new PluginCaller(
567 $Plugin->GetBaseName(), $PluginMethod);
568 $Result = $this->AF->HookEvent(
569 $EventName,
570 array($Caller, "CallPluginMethod"));
571 }
572 else
573 {
574 # hook event
575 $Result = $this->AF->HookEvent(
576 $EventName, array($Plugin, $PluginMethod));
577 }
578
579 # record any errors
580 if ($Result === FALSE)
581 {
582 $ErrMsgs[] = "Unable to hook requested event <i>"
583 .$EventName."</i> for plugin <b>"
584 .$Plugin->GetBaseName()."</b>";
585 }
586 }
587 }
588
589 # if event hook setup failed
590 if (count($ErrMsgs))
591 {
592 # report errors to caller
593 return $ErrMsgs;
594 }
595 }
596
597 # report success to caller
598 return NULL;
599 }
600
608 private function InstallPlugin(&$Plugin)
609 {
610 # if plugin has not been installed
611 $InstallOrUpgradePerformed = FALSE;
612 $PluginName = $Plugin->GetBaseName();
613 $Attribs = $Plugin->GetAttributes();
614 if (!$Plugin->IsInstalled())
615 {
616 # set default values if present
617 $this->SetPluginDefaultConfigValues($Plugin, TRUE);
618
619 # install plugin
620 $ErrMsg = $Plugin->Install();
621 $InstallOrUpgradePerformed = TRUE;
622
623 # if install succeeded
624 if ($ErrMsg == NULL)
625 {
626 # mark plugin as installed
627 $Plugin->IsInstalled(TRUE);
628 }
629 else
630 {
631 # return error message about installation failure
632 return "Installation of plugin <b>"
633 .$PluginName."</b> failed: <i>".$ErrMsg."</i>";
634 }
635 }
636 else
637 {
638 # if plugin version is newer than version in database
639 if (version_compare($Attribs["Version"],
640 $Plugin->InstalledVersion()) == 1)
641 {
642 # set default values for any new configuration settings
643 $this->SetPluginDefaultConfigValues($Plugin);
644
645 # upgrade plugin
646 $ErrMsg = $Plugin->Upgrade($Plugin->InstalledVersion());
647 $InstallOrUpgradePerformed = TRUE;
648
649 # if upgrade succeeded
650 if ($ErrMsg === NULL)
651 {
652 # update plugin version in database
653 $Plugin->InstalledVersion($Attribs["Version"]);
654 }
655 else
656 {
657 # report error message about upgrade failure
658 return "Upgrade of plugin <b>"
659 .$PluginName."</b> from version <i>"
660 .addslashes($Plugin->InstalledVersion())
661 ."</i> to version <i>"
662 .addslashes($Attribs["Version"])."</i> failed: <i>"
663 .$ErrMsg."</i>";
664 }
665 }
666 # else if plugin version is older than version in database
667 elseif (version_compare($Attribs["Version"],
668 $Plugin->InstalledVersion()) == -1)
669 {
670 # return error message about version conflict
671 return "Plugin <b>"
672 .$PluginName."</b> is older (<i>"
673 .addslashes($Attribs["Version"])
674 ."</i>) than previously-installed version (<i>"
675 .addslashes($Plugin->InstalledVersion())
676 ."</i>).";
677 }
678 }
679
680 # report result to caller
681 return $InstallOrUpgradePerformed;
682 }
683
691 private function SetPluginDefaultConfigValues($Plugin, $Overwrite = FALSE)
692 {
693 # if plugin has configuration info
694 $Attribs = $Plugin->GetAttributes();
695 if (isset($Attribs["CfgSetup"]))
696 {
697 foreach ($Attribs["CfgSetup"] as $CfgValName => $CfgSetup)
698 {
699 if (isset($CfgSetup["Default"]) && ($Overwrite
700 || ($Plugin->ConfigSetting($CfgValName) === NULL)))
701 {
702 $Plugin->ConfigSetting($CfgValName, $CfgSetup["Default"]);
703 }
704 }
705 }
706 }
707
715 private function CheckDependencies($Plugins, $CheckReady = FALSE)
716 {
717 # look until all enabled plugins check out okay
718 $ErrMsgs = array();
719 do
720 {
721 # start out assuming all plugins are okay
722 $AllOkay = TRUE;
723
724 # for each plugin
725 foreach ($Plugins as $PluginName => $Plugin)
726 {
727 # if plugin is enabled and not checking for ready
728 # or plugin is ready
729 if ($Plugin->IsEnabled() && (!$CheckReady || $Plugin->IsReady()))
730 {
731 # load plugin attributes
732 if (!isset($Attribs[$PluginName]))
733 {
734 $Attribs[$PluginName] = $Plugin->GetAttributes();
735 }
736
737 # for each dependency for this plugin
738 foreach ($Attribs[$PluginName]["Requires"]
739 as $ReqName => $ReqVersion)
740 {
741 # handle PHP version requirements
742 if ($ReqName == "PHP")
743 {
744 if (version_compare($ReqVersion, phpversion(), ">"))
745 {
746 $ErrMsgs[$PluginName][] = "PHP version "
747 ."<i>".$ReqVersion."</i>"
748 ." required by <b>".$PluginName."</b>"
749 ." was not available. (Current PHP version"
750 ." is <i>".phpversion()."</i>.)";
751 }
752 }
753 # handle PHP extension requirements
754 elseif (preg_match("/^PHPX_/", $ReqName))
755 {
756 list($Dummy, $ExtensionName) = explode("_", $ReqName, 2);
757 if (!extension_loaded($ExtensionName))
758 {
759 $ErrMsgs[$PluginName][] = "PHP extension "
760 ."<i>".$ExtensionName."</i>"
761 ." required by <b>".$PluginName."</b>"
762 ." was not available.";
763 }
764 }
765 # handle dependencies on other plugins
766 else
767 {
768 # load plugin attributes if not already loaded
769 if (isset($Plugins[$ReqName])
770 && !isset($Attribs[$ReqName]))
771 {
772 $Attribs[$ReqName] =
773 $Plugins[$ReqName]->GetAttributes();
774 }
775
776 # if target plugin is not present or is too old
777 # or is not enabled
778 # or (if appropriate) is not ready
779 if (!isset($Plugins[$ReqName])
780 || version_compare($ReqVersion,
781 $Attribs[$ReqName]["Version"], ">")
782 || !$Plugins[$ReqName]->IsEnabled()
783 || ($CheckReady
784 && !$Plugins[$ReqName]->IsReady()))
785 {
786 # add error message
787 $ErrMsgs[$PluginName][] = "Plugin <i>"
788 .$ReqName." ".$ReqVersion."</i>"
789 ." required by <b>".$PluginName."</b>"
790 ." was not available.";
791 }
792 }
793
794 # if problem was found with plugin
795 if (isset($ErrMsgs[$PluginName]))
796 {
797 # remove plugin from our list
798 unset($Plugins[$PluginName]);
799
800 # set flag to indicate a plugin had to be dropped
801 $AllOkay = FALSE;
802 }
803 }
804 }
805 }
806 } while ($AllOkay == FALSE);
807
808 # return messages about any dropped plugins back to caller
809 return $ErrMsgs;
810 }
811
820 private function SortPluginsByInitializationPrecedence($Plugins)
821 {
822 # load plugin attributes
823 foreach ($Plugins as $PluginName => $Plugin)
824 {
825 $PluginAttribs[$PluginName] = $Plugin->GetAttributes();
826 }
827
828 # determine initialization order
829 $PluginsAfterUs = array();
830 foreach ($PluginAttribs as $PluginName => $Attribs)
831 {
832 foreach ($Attribs["InitializeBefore"] as $OtherPluginName)
833 {
834 $PluginsAfterUs[$PluginName][] = $OtherPluginName;
835 }
836 foreach ($Attribs["InitializeAfter"] as $OtherPluginName)
837 {
838 $PluginsAfterUs[$OtherPluginName][] = $PluginName;
839 }
840 }
841
842 # infer other initialization order cues from lists of required plugins
843 foreach ($PluginAttribs as $PluginName => $Attribs)
844 {
845 # for each required plugin
846 foreach ($Attribs["Requires"]
847 as $RequiredPluginName => $RequiredPluginVersion)
848 {
849 # if there is not a requirement in the opposite direction
850 if (!array_key_exists($PluginName,
851 $PluginAttribs[$RequiredPluginName]["Requires"]))
852 {
853 # if the required plugin is not scheduled to be after us
854 if (!array_key_exists($PluginName, $PluginsAfterUs)
855 || !in_array($RequiredPluginName,
856 $PluginsAfterUs[$PluginName]))
857 {
858 # if we are not already scheduled to be after the required plugin
859 if (!array_key_exists($PluginName, $PluginsAfterUs)
860 || !in_array($RequiredPluginName,
861 $PluginsAfterUs[$PluginName]))
862 {
863 # schedule us to be after the required plugin
864 $PluginsAfterUs[$RequiredPluginName][] =
865 $PluginName;
866 }
867 }
868 }
869 }
870 }
871
872 # keep track of those plugins we have yet to do and those that are done
873 $UnsortedPlugins = array_keys($Plugins);
874 $PluginsProcessed = array();
875
876 # limit the number of iterations of the plugin ordering loop
877 # to 10 times the number of plugins we have
878 $MaxIterations = 10 * count($UnsortedPlugins);
879 $IterationCount = 0;
880
881 # iterate through all the plugins that need processing
882 while (($NextPlugin = array_shift($UnsortedPlugins)) !== NULL)
883 {
884 # check to be sure that we're not looping forever
885 $IterationCount++;
886 if ($IterationCount > $MaxIterations)
887 {
888 throw new Exception(
889 "Max iteration count exceeded trying to determine plugin"
890 ." loading order. Is there a dependency loop?");
891 }
892
893 # if no plugins require this one, it can go last
894 if (!isset($PluginsAfterUs[$NextPlugin]))
895 {
896 $PluginsProcessed[$NextPlugin] = $MaxIterations;
897 }
898 else
899 {
900 # for plugins that are required by others
901 $Index = $MaxIterations;
902 foreach ($PluginsAfterUs[$NextPlugin] as $GoBefore)
903 {
904 if (!isset($PluginsProcessed[$GoBefore]))
905 {
906 # if there is something that requires us which hasn't
907 # yet been assigned an order, then we can't determine
908 # our own place on this iteration
909 array_push($UnsortedPlugins, $NextPlugin);
910 continue 2;
911 }
912 else
913 {
914 # otherwise, make sure that we're loaded
915 # before the earliest of the things that require us
916 $Index = min($Index, $PluginsProcessed[$GoBefore] - 1);
917 }
918 }
919 $PluginsProcessed[$NextPlugin] = $Index;
920 }
921 }
922
923 # arrange plugins according to our ordering
924 asort($PluginsProcessed, SORT_NUMERIC);
925 $SortedPlugins = array();
926 foreach ($PluginsProcessed as $PluginName => $SortOrder)
927 {
928 $SortedPlugins[$PluginName] = $Plugins[$PluginName];
929 }
930
931 # return sorted list to caller
932 return $SortedPlugins;
933 }
934
943 public function FindPluginPhpFile($PageName)
944 {
945 # build list of possible locations for file
946 $Locations = array(
947 "local/plugins/%PLUGIN%/pages/%PAGE%.php",
948 "plugins/%PLUGIN%/pages/%PAGE%.php",
949 "local/plugins/%PLUGIN%/%PAGE%.php",
950 "plugins/%PLUGIN%/%PAGE%.php",
951 );
952
953 # look for file and return (possibly) updated page to caller
954 return $this->FindPluginPageFile($PageName, $Locations);
955 }
966 public function FindPluginHtmlFile($PageName)
967 {
968 # build list of possible locations for file
969 $Locations = array(
970 "local/plugins/%PLUGIN%/interface/%ACTIVEUI%/%PAGE%.html",
971 "plugins/%PLUGIN%/interface/%ACTIVEUI%/%PAGE%.html",
972 "local/plugins/%PLUGIN%/interface/%DEFAULTUI%/%PAGE%.html",
973 "plugins/%PLUGIN%/interface/%DEFAULTUI%/%PAGE%.html",
974 "local/plugins/%PLUGIN%/%PAGE%.html",
975 "plugins/%PLUGIN%/%PAGE%.html",
976 );
977
978 # find HTML file
979 $Params = $this->FindPluginPageFile($PageName, $Locations);
980
981 # if plugin HTML file was found
982 if ($Params["PageName"] != $PageName)
983 {
984 # add subdirectories for plugin to search paths
985 $Dir = preg_replace("%^local/%", "", dirname($Params["PageName"]));
986 $this->AF->AddImageDirectories(array(
987 "local/".$Dir."/images",
988 $Dir."/images",
989 ));
990 $this->AF->AddIncludeDirectories(array(
991 "local/".$Dir."/include",
992 $Dir."/include",
993 ));
994 $this->AF->AddFunctionDirectories(array(
995 "local/".$Dir."/include",
996 $Dir."/include",
997 ));
998 }
999
1000 # return possibly revised HTML file name to caller
1001 return $Params;
1002 }
1015 private function FindPluginPageFile($PageName, $Locations)
1016 {
1017 # set up return value assuming we will not find plugin page file
1018 $ReturnValue["PageName"] = $PageName;
1019
1020 # look for plugin name and plugin page name in base page name
1021 preg_match("/P_([A-Za-z].[A-Za-z0-9]*)_([A-Za-z0-9_-]+)/", $PageName, $Matches);
1022
1023 # if plugin name and plugin page name were found and plugin name is valid
1024 if (count($Matches) == 3)
1025 {
1026 # if plugin is valid and enabled and has its own subdirectory
1027 $PluginName = $Matches[1];
1028 if (isset($this->Plugins[$PluginName])
1029 && $this->PluginHasDir[$PluginName]
1030 && $this->Plugins[$PluginName]->IsEnabled())
1031 {
1032 # for each possible location
1033 $PageName = $Matches[2];
1034 $ActiveUI = $this->AF->ActiveUserInterface();
1035 $DefaultUI = $this->AF->DefaultUserInterface();
1036 foreach ($Locations as $Loc)
1037 {
1038 # make any needed substitutions into path
1039 $FileName = str_replace(
1040 array("%DEFAULTUI%", "%ACTIVEUI%", "%PLUGIN%", "%PAGE%"),
1041 array($DefaultUI, $ActiveUI, $PluginName, $PageName),
1042 $Loc);
1043
1044 # if file exists in this location
1045 if (file_exists($FileName))
1046 {
1047 # set return value to contain full plugin page file name
1048 $ReturnValue["PageName"] = $FileName;
1049
1050 # save plugin name as home of current page
1051 $this->PageFilePlugin = $PluginName;
1052
1053 # set G_Plugin to plugin associated with current page
1054 $GLOBALS["G_Plugin"] = $this->GetPluginForCurrentPage();
1055
1056 # stop looking
1057 break;
1058 }
1059 }
1060 }
1061 }
1062
1063 # return array containing page name or page file name to caller
1064 return $ReturnValue;
1065 }
1066 }
1067
1079 class PluginCaller
1080 {
1081
1088 public function __construct($PluginName, $MethodName)
1089 {
1090 $this->PluginName = $PluginName;
1091 $this->MethodName = $MethodName;
1092 }
1093
1099 public function CallPluginMethod()
1100 {
1101 $Args = func_get_args();
1102 $Plugin = self::$Manager->GetPlugin($this->PluginName);
1103 return call_user_func_array(array($Plugin, $this->MethodName), $Args);
1104 }
1105
1110 public function GetCallbackAsText()
1111 {
1112 return $this->PluginName."::".$this->MethodName;
1113 }
1114
1120 public function __sleep()
1121 {
1122 return array("PluginName", "MethodName");
1123 }
1124
1126 static public $Manager;
1127
1128 private $PluginName;
1129 private $MethodName;
1130 }
1133 ?>
PluginManager\GetErrorMessages
GetErrorMessages()
Retrieve any error messages generated during plugin loading.
Definition: PluginManager.php:142
PluginManager
Manager to load and invoke plugins.
Definition: PluginManager.php:13
PluginManager\GetPlugin
GetPlugin($PluginName, $EvenIfNotReady=FALSE)
Retrieve specified plugin.
Definition: PluginManager.php:155
Database
SQL database abstraction object with smart query caching.
Definition: Database.php:22
PluginManager\UninstallPlugin
UninstallPlugin($PluginName)
Uninstall plugin and (optionally) delete any associated data.
Definition: PluginManager.php:277
PluginManager\GetDependents
GetDependents($PluginName)
Returns a list of plugins dependent on the specified plugin.
Definition: PluginManager.php:227
PluginManager\GetActivePluginList
GetActivePluginList()
Get list of active (i.e.
Definition: PluginManager.php:247
PluginManager\GetPluginAttributes
GetPluginAttributes()
Retrieve info about currently loaded plugins.
Definition: PluginManager.php:195
PluginManager\PluginEnabled
PluginEnabled($PluginName, $NewValue=NULL)
Get/set whether specified plugin is enabled.
Definition: PluginManager.php:266
PHP
PHP
Definition: RSSClient--Test.php:12
PluginManager\__construct
__construct($AppFramework, $PluginDirectories)
PluginManager class constructor.
Definition: PluginManager.php:25
PluginManager\GetPluginForCurrentPage
GetPluginForCurrentPage()
Retrieve plugin for current page (if any).
Definition: PluginManager.php:185
PluginManager\GetPlugins
GetPlugins()
Retrieve all loaded plugins.
Definition: PluginManager.php:173
Plugin\SetApplicationFramework
static SetApplicationFramework($AF)
Set the application framework to be referenced within plugins.
Definition: Plugin.php:378
PluginManager\LoadPlugins
LoadPlugins()
Load and initialize plugins.
Definition: PluginManager.php:49