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 # ---- PUBLIC INTERFACE --------------------------------------------------
16
24 function __construct($AppFramework, $PluginDirectories)
25 {
26 # save framework and directory list for later use
27 $this->AF = $AppFramework;
28 $this->DirsToSearch = $PluginDirectories;
29
30 # get our own database handle
31 $this->DB = new Database();
32
33 # hook into events to load plugin PHP and HTML files
34 $this->AF->HookEvent("EVENT_PHP_FILE_LOAD", array($this, "FindPluginPhpFile"),
35 ApplicationFramework::ORDER_LAST);
36 $this->AF->HookEvent("EVENT_HTML_FILE_LOAD", array($this, "FindPluginHtmlFile"),
37 ApplicationFramework::ORDER_LAST);
38
39 # tell PluginCaller helper object how to get to us
40 PluginCaller::$Manager = $this;
41 }
42
53 function LoadPlugins($ForcePluginConfigOptLoad = FALSE)
54 {
55 # clear any existing errors/status
56 $this->ErrMsgs = array();
57 $this->StatusMsgs = array();
58
59 # load plugin info from database
60 $this->DB->Query("SELECT * FROM PluginInfo");
61 while ($Row = $this->DB->FetchRow())
62 {
63 $this->PluginInfo[$Row["BaseName"]] = $Row;
64 $this->PluginEnabled[$Row["BaseName"]] =
65 $Row["Enabled"] ? TRUE : FALSE;
66 }
67
68 # load list of all base plugin files
69 $this->FindPlugins($this->DirsToSearch);
70
71 # for each plugin found
72 foreach ($this->PluginNames as $PluginName)
73 {
74 # bring in plugin class file
75 include_once($this->PluginFiles[$PluginName]);
76
77 # if plugin class was defined by file
78 if (class_exists($PluginName))
79 {
80 # if plugin class is a valid descendant of base plugin class
81 $Plugin = new $PluginName;
82 if (is_subclass_of($Plugin, "Plugin"))
83 {
84 # set hooks needed for plugin to access plugin manager services
85 $Plugin->SetCfgSaveCallback(array(__CLASS__, "CfgSaveCallback"));
86
87 # register the plugin
88 $this->Plugins[$PluginName] = $Plugin;
89 $this->Plugins[$PluginName]->Register();
90 if (!isset($this->PluginEnabled[$PluginName]))
91 {
92 $this->PluginEnabled[$PluginName] = FALSE;
93 }
94
95 # add plugin to database if not already present
96 $Attribs[$PluginName] = $this->Plugins[$PluginName]->GetAttributes();
97 if (!isset($this->PluginInfo[$PluginName]))
98 {
99 $this->DB->Query("INSERT INTO PluginInfo"
100 ." (BaseName, Version, Installed, Enabled)"
101 ." VALUES ('".addslashes($PluginName)."', "
102 ." '".addslashes(
103 $Attribs[$PluginName]["Version"])."', "
104 ."0, "
105 ." ".($Attribs[$PluginName]["EnabledByDefault"]
106 ? 1 : 0).")");
107 $this->DB->Query("SELECT * FROM PluginInfo"
108 ." WHERE BaseName = '".addslashes($PluginName)."'");
109 $this->PluginInfo[$PluginName] = $this->DB->FetchRow();
110 }
111
112 # check required plugin attributes
113 if (!strlen($Attribs[$PluginName]["Name"]))
114 {
115 $this->ErrMsgs[$PluginName][] = "Plugin <b>".$PluginName."</b>"
116 ." could not be loaded because it"
117 ." did not have a <i>Name</i> attribute set.";
118 unset($this->PluginEnabled[$PluginName]);
119 unset($this->Plugins[$PluginName]);
120 }
121 if (!strlen($Attribs[$PluginName]["Version"]))
122 {
123 $this->ErrMsgs[$PluginName][] = "Plugin <b>".$PluginName."</b>"
124 ." could not be loaded because it"
125 ." did not have a <i>Version</i> attribute set.";
126 unset($this->PluginEnabled[$PluginName]);
127 unset($this->Plugins[$PluginName]);
128 }
129 }
130 else
131 {
132 $this->ErrMsgs[$PluginName][] = "Plugin <b>".$PluginName."</b>"
133 ." could not be loaded because <i>".$PluginName."</i> was"
134 ." not a subclass of base <i>Plugin</i> class";
135 }
136 }
137 else
138 {
139 $this->ErrMsgs[$PluginName][] = "Expected class <i>".$PluginName
140 ."</i> not found in plugin file <i>"
141 .$this->PluginFiles[$PluginName]."</i>";
142 }
143 }
144
145 # check plugin dependencies
146 $this->CheckDependencies();
147
148 # load plugin configurations
149 $this->DB->Query("SELECT BaseName,Cfg FROM PluginInfo");
150 $Cfgs = $this->DB->FetchColumn("Cfg", "BaseName");
151
152 # sort plugins according to any loading order requests
153 $SortedPlugins = $this->SortPluginsByInitializationPrecedence($this->Plugins);
154
155 # for each plugin
156 foreach ($SortedPlugins as $PluginName => $Plugin)
157 {
158 # if plugin is enabled
159 if ($this->PluginEnabled[$PluginName])
160 {
161 # if plugin has its own subdirectory
162 if ($this->PluginHasDir[$PluginName])
163 {
164 # if plugin has its own object directory
165 $Dir = dirname($this->PluginFiles[$PluginName]);
166 if (is_dir($Dir."/objects"))
167 {
168 # add object directory to class autoloading list
169 ApplicationFramework::AddObjectDirectory($Dir."/objects");
170 }
171 else
172 {
173 # add plugin directory to class autoloading list
174 ApplicationFramework::AddObjectDirectory($Dir);
175 }
176 }
177
178 # set configuration values if available
179 if (isset($Cfgs[$PluginName]))
180 {
181 $Plugin->SetAllCfg(unserialize($Cfgs[$PluginName]));
182 }
183
184 # install or upgrade plugin if needed
185 $this->InstallPlugin($Plugin);
186
187 # recheck dependencies if enabled status has changed
188 if (!$this->PluginEnabled[$PluginName])
189 {
190 $this->CheckDependencies();
191 }
192 }
193
194 # if plugin is enabled or we are loading all configuration options
195 if ($ForcePluginConfigOptLoad || $this->PluginEnabled[$PluginName])
196 {
197 # set up plugin configuration options
198 $ErrMsgs = $Plugin->SetUpConfigOptions();
199 if ($ErrMsgs !== NULL)
200 {
201 if (!is_array($ErrMsgs)) { $ErrMsgs = array($ErrMsgs); }
202 foreach ($ErrMsgs as $ErrMsg)
203 {
204 $this->ErrMsgs[$PluginName][] = "Configuration option setup"
205 ." failed for plugin <b>".$PluginName."</b>: <i>"
206 .$ErrMsg."</i>";
207 }
208 $this->PluginEnabled[$PluginName] = FALSE;
209 }
210 }
211
212 # if plugin is enabled
213 if ($this->PluginEnabled[$PluginName])
214 {
215 # initialize the plugin
216 $ErrMsgs = $Plugin->Initialize();
217
218 # if initialization failed
219 if ($ErrMsgs !== NULL)
220 {
221 if (!is_array($ErrMsgs)) { $ErrMsgs = array($ErrMsgs); }
222 foreach ($ErrMsgs as $ErrMsg)
223 {
224 $this->ErrMsgs[$PluginName][] = "Initialization failed for"
225 ." plugin <b>".$PluginName."</b>: <i>".$ErrMsg."</i>";
226 }
227 $this->PluginEnabled[$PluginName] = FALSE;
228 }
229 else
230 {
231 # register any events declared by plugin
232 $Events = $Plugin->DeclareEvents();
233 if (count($Events)) { $this->AF->RegisterEvent($Events); }
234
235 # set up any event hooks requested by plugin
236 $EventsToHook = $Plugin->HookEvents();
237 if (count($EventsToHook))
238 {
239 foreach ($EventsToHook as $EventName => $PluginMethods)
240 {
241 if (!is_array($PluginMethods))
242 { $PluginMethods = array($PluginMethods); }
243
244 foreach ($PluginMethods as $PluginMethod)
245 {
246 if ($this->AF->IsStaticOnlyEvent($EventName))
247 {
248 $Caller = new PluginCaller(
249 $PluginName, $PluginMethod);
250 $Result = $this->AF->HookEvent(
251 $EventName,
252 array($Caller, "CallPluginMethod"));
253 }
254 else
255 {
256 $Result = $this->AF->HookEvent(
257 $EventName, array($Plugin, $PluginMethod));
258 }
259 if ($Result === FALSE)
260 {
261 $this->ErrMsgs[$PluginName][] =
262 "Unable to hook requested event <i>"
263 .$EventName."</i> for plugin <b>"
264 .$PluginName."</b>";
265 $this->PluginEnabled[$PluginName] = FALSE;
266 }
267 }
268 }
269 }
270
271 # mark plugin initialization as complete
272 if ($this->PluginEnabled[$PluginName])
273 {
274 $this->PluginReady[$PluginName] = TRUE;
275 }
276 }
277
278 # recheck dependencies if enabled status has changed
279 if (!$this->PluginEnabled[$PluginName])
280 {
281 $this->CheckDependencies();
282 }
283 }
284 }
285
286 # check plugin dependencies again in case an install or upgrade failed
287 $this->CheckDependencies();
288
289 # report to caller whether any problems were encountered
290 return count($this->ErrMsgs) ? FALSE : TRUE;
291 }
292
298 function GetErrorMessages()
299 {
300 return $this->ErrMsgs;
301 }
302
309 function GetStatusMessages()
310 {
311 return $this->StatusMsgs;
312 }
313
319 function GetPlugin($PluginName)
320 {
321 # pages where we need to access plugins even if they aren't
322 # yet ready
323 $AllowUnreadyPages = array("PluginConfig", "PluginUninstall");
324
325 if (!array_key_exists($PluginName, $this->PluginReady) &&
326 !in_array($GLOBALS["AF"]->GetPageName(), $AllowUnreadyPages) &&
327 !(basename($_SERVER["SCRIPT_NAME"]) == "installcwis.php") )
328 {
329 throw new Exception("Attempt to access plugin ".$PluginName
330 ." that has not been initialized.");
331 }
332 return isset($this->Plugins[$PluginName])
333 ? $this->Plugins[$PluginName] : NULL;
334 }
335
343 function GetPluginForCurrentPage()
344 {
345 return $this->GetPlugin($this->PageFilePlugin);
346 }
347
352 function GetPluginAttributes()
353 {
354 $Info = array();
355 foreach ($this->Plugins as $PluginName => $Plugin)
356 {
357 $Info[$PluginName] = $Plugin->GetAttributes();
358 $Info[$PluginName]["Enabled"] =
359 isset($this->PluginInfo[$PluginName]["Enabled"])
360 ? $this->PluginInfo[$PluginName]["Enabled"] : FALSE;
361 $Info[$PluginName]["Installed"] =
362 isset($this->PluginInfo[$PluginName]["Installed"])
363 ? $this->PluginInfo[$PluginName]["Installed"] : FALSE;
364 $Info[$PluginName]["ClassFile"] = $this->PluginFiles[$PluginName];
365 }
366 return $Info;
367 }
368
374 function GetDependents($PluginName)
375 {
376 $Dependents = array();
377 $AllAttribs = $this->GetPluginAttributes();
378 foreach ($AllAttribs as $Name => $Attribs)
379 {
380 if (array_key_exists($PluginName, $Attribs["Requires"]))
381 {
382 $Dependents[] = $Name;
383 $SubDependents = $this->GetDependents($Name);
384 $Dependents = array_merge($Dependents, $SubDependents);
385 }
386 }
387 return $Dependents;
388 }
389
394 function GetActivePluginList()
395 {
396 return array_keys($this->PluginEnabled, 1);
397 }
398
405 function PluginEnabled($PluginName, $NewValue = NULL)
406 {
407 # if an enabled/disabled value was supplied
408 if ($NewValue !== NULL)
409 {
410 # if enabled/disabled status has changed for plugin
411 if (($NewValue && !$this->PluginInfo[$PluginName]["Enabled"])
412 || (!$NewValue && $this->PluginInfo[$PluginName]["Enabled"]))
413 {
414 $Info = $this->Plugins[$PluginName]->GetAttributes();
415 $this->DB->Query("UPDATE PluginInfo"
416 ." SET Enabled = ".($NewValue ? "1" : "0")
417 ." WHERE BaseName = '".addslashes($PluginName)."'");
418 $this->PluginEnabled[$PluginName] = $NewValue;
419 $this->PluginInfo[$PluginName]["Enabled"] = $NewValue;
420 $this->StatusMsgs[$PluginName][] = "Plugin <i>"
421 .$Info["Name"]."</i> "
422 .($NewValue ? "enabled" : "disabled").".";
423 }
424 }
425 return array_key_exists($PluginName, $this->PluginEnabled)
426 && $this->PluginEnabled[$PluginName];
427 }
428
434 function UninstallPlugin($PluginName)
435 {
436 # assume success
437 $Result = NULL;
438
439 # if plugin is installed
440 if ($this->PluginInfo[$PluginName]["Installed"])
441 {
442 # call uninstall method for plugin
443 $Result = $this->Plugins[$PluginName]->Uninstall();
444
445 # if plugin uninstall method succeeded
446 if ($Result === NULL)
447 {
448 # remove plugin info from database
449 $this->DB->Query("DELETE FROM PluginInfo"
450 ." WHERE BaseName = '".addslashes($PluginName)."'");
451
452 # drop our data for the plugin
453 unset($this->Plugins[$PluginName]);
454 unset($this->PluginInfo[$PluginName]);
455 unset($this->PluginEnabled[$PluginName]);
456 unset($this->PluginNames[$PluginName]);
457 unset($this->PluginFiles[$PluginName]);
458 }
459 }
460
461 # report results (if any) to caller
462 return $Result;
463 }
464
465
466 # ---- PRIVATE INTERFACE -------------------------------------------------
467
468 private $AF;
469 private $DB;
470 private $DirsToSearch;
471 private $ErrMsgs = array();
472 private $PageFilePlugin = NULL;
473 private $Plugins = array();
474 private $PluginEnabled = array();
475 private $PluginFiles = array();
476 private $PluginHasDir = array();
477 private $PluginInfo = array();
478 private $PluginNames = array();
479 private $PluginReady = array();
480 private $StatusMsgs = array();
481
487 private function FindPlugins($DirsToSearch)
488 {
489 # for each directory
490 foreach ($DirsToSearch as $Dir)
491 {
492 # if directory exists
493 if (is_dir($Dir))
494 {
495 # for each file in directory
496 $FileNames = scandir($Dir);
497 foreach ($FileNames as $FileName)
498 {
499 # if file looks like base plugin file
500 if (preg_match("/^[a-zA-Z_][a-zA-Z0-9_]*\.php$/", $FileName))
501 {
502 # add file to list
503 $PluginName = preg_replace("/\.php$/", "", $FileName);
504 $this->PluginNames[$PluginName] = $PluginName;
505 $this->PluginFiles[$PluginName] = $Dir."/".$FileName;
506 $this->PluginHasDir[$PluginName] = FALSE;
507 }
508 # else if file looks like plugin directory
509 elseif (is_dir($Dir."/".$FileName)
510 && preg_match("/^[a-zA-Z_][a-zA-Z0-9_]*/", $FileName))
511 {
512 # if there is a base plugin file in the directory
513 $PluginName = $FileName;
514 $PluginFile = $Dir."/".$PluginName."/".$PluginName.".php";
515 if (file_exists($PluginFile))
516 {
517 # add plugin and directory to lists
518 $this->PluginNames[$PluginName] = $PluginName;
519 $this->PluginFiles[$PluginName] = $PluginFile;
520 $this->PluginHasDir[$PluginName] = TRUE;
521 }
522 # else if we don't already have a base plugin file
523 elseif (!array_key_exists($PluginName, $this->PluginFiles))
524 {
525 $this->ErrMsgs[$PluginName][] =
526 "Expected plugin file <i>".$PluginName.".php</i> not"
527 ." found in plugin subdirectory <i>"
528 .$Dir."/".$PluginName."</i>";
529 }
530 }
531 }
532 }
533 }
534 }
535
541 private function InstallPlugin($Plugin)
542 {
543 # if plugin is enabled
544 $PluginName = get_class($Plugin);
545 if ($this->PluginEnabled[$PluginName])
546 {
547 # if plugin has not been installed
548 $Attribs = $Plugin->GetAttributes();
549 if (!$this->PluginInfo[$PluginName]["Installed"])
550 {
551 # set default values if present
552 if (isset($Attribs["CfgSetup"]))
553 {
554 foreach ($Attribs["CfgSetup"] as $CfgValName => $CfgSetup)
555 {
556 if (isset($CfgSetup["Default"]))
557 {
558 $Plugin->ConfigSetting($CfgValName,
559 $CfgSetup["Default"]);
560 }
561 }
562 }
563
564 # install plugin
565 $ErrMsg = $Plugin->Install();
566
567 # if install succeeded
568 if ($ErrMsg == NULL)
569 {
570 # mark plugin as installed
571 $this->DB->Query("UPDATE PluginInfo SET Installed = 1"
572 ." WHERE BaseName = '".addslashes($PluginName)."'");
573 $this->PluginInfo[$PluginName]["Installed"] = 1;
574 }
575 else
576 {
577 # disable plugin
578 $this->PluginEnabled[$PluginName] = FALSE;
579
580 # record error message about installation failure
581 $this->ErrMsgs[$PluginName][] = "Installation of plugin <b>"
582 .$PluginName."</b> failed: <i>".$ErrMsg."</i>";;
583 }
584 }
585 else
586 {
587 # if plugin version is newer than version in database
588 if (version_compare($Attribs["Version"],
589 $this->PluginInfo[$PluginName]["Version"]) == 1)
590 {
591 # set default values for any new configuration settings
592 if (isset($Attribs["CfgSetup"]))
593 {
594 foreach ($Attribs["CfgSetup"] as $CfgValName => $CfgSetup)
595 {
596 if (isset($CfgSetup["Default"])
597 && ($Plugin->ConfigSetting(
598 $CfgValName) === NULL))
599 {
600 $Plugin->ConfigSetting($CfgValName,
601 $CfgSetup["Default"]);
602 }
603 }
604 }
605
606 # upgrade plugin
607 $ErrMsg = $Plugin->Upgrade(
608 $this->PluginInfo[$PluginName]["Version"]);
609
610 # if upgrade succeeded
611 if ($ErrMsg == NULL)
612 {
613 # update plugin version in database
614 $this->DB->Query("UPDATE PluginInfo"
615 ." SET Version = '".addslashes($Attribs["Version"])."'"
616 ." WHERE BaseName = '".addslashes($PluginName)."'");
617 $this->PluginInfo[$PluginName]["Version"] = $Attribs["Version"];
618 }
619 else
620 {
621 # disable plugin
622 $this->PluginEnabled[$PluginName] = FALSE;
623
624 # record error message about upgrade failure
625 $this->ErrMsgs[$PluginName][] = "Upgrade of plugin <b>"
626 .$PluginName."</b> from version <i>"
627 .addslashes($this->PluginInfo[$PluginName]["Version"])
628 ."</i> to version <i>"
629 .addslashes($Attribs["Version"])."</i> failed: <i>"
630 .$ErrMsg."</i>";
631 }
632 }
633 # else if plugin version is older than version in database
634 elseif (version_compare($Attribs["Version"],
635 $this->PluginInfo[$PluginName]["Version"]) == -1)
636 {
637 # disable plugin
638 $this->PluginEnabled[$PluginName] = FALSE;
639
640 # record error message about version conflict
641 $this->ErrMsgs[$PluginName][] = "Plugin <b>"
642 .$PluginName."</b> is older (<i>"
643 .addslashes($Attribs["Version"])
644 ."</i>) than previously-installed version (<i>"
645 .addslashes(
646 $this->PluginInfo[$PluginName]["Version"]
647 )."</i>).";
648 }
649 }
650 }
651 }
652
657 private function CheckDependencies()
658 {
659 # look until all enabled plugins check out okay
660 do
661 {
662 # start out assuming all plugins are okay
663 $AllOkay = TRUE;
664
665 # for each plugin
666 foreach ($this->Plugins as $PluginName => $Plugin)
667 {
668 # if plugin is currently enabled
669 if ($this->PluginEnabled[$PluginName])
670 {
671 # load plugin attributes
672 if (!isset($Attribs[$PluginName]))
673 {
674 $Attribs[$PluginName] =
675 $this->Plugins[$PluginName]->GetAttributes();
676 }
677
678 # for each dependency for this plugin
679 foreach ($Attribs[$PluginName]["Requires"]
680 as $ReqName => $ReqVersion)
681 {
682 # handle PHP version requirements
683 if ($ReqName == "PHP")
684 {
685 if (version_compare($ReqVersion, phpversion(), ">"))
686 {
687 $this->ErrMsgs[$PluginName][] = "PHP version "
688 ."<i>".$ReqVersion."</i>"
689 ." required by <b>".$PluginName."</b>"
690 ." was not available. (Current PHP version"
691 ." is <i>".phpversion()."</i>.)";
692 }
693 }
694 # handle PHP extension requirements
695 elseif (preg_match("/^PHPX_/", $ReqName))
696 {
697 list($Dummy, $ExtensionName) = explode("_", $ReqName, 2);
698 if (!extension_loaded($ExtensionName))
699 {
700 $this->ErrMsgs[$PluginName][] = "PHP extension "
701 ."<i>".$ExtensionName."</i>"
702 ." required by <b>".$PluginName."</b>"
703 ." was not available.";
704 }
705 }
706 # handle dependencies on other plugins
707 else
708 {
709 # load plugin attributes if not already loaded
710 if (isset($this->Plugins[$ReqName])
711 && !isset($Attribs[$ReqName]))
712 {
713 $Attribs[$ReqName] =
714 $this->Plugins[$ReqName]->GetAttributes();
715 }
716
717 # if target plugin is not present or is disabled or is too old
718 if (!isset($this->PluginEnabled[$ReqName])
719 || !$this->PluginEnabled[$ReqName]
720 || (version_compare($ReqVersion,
721 $Attribs[$ReqName]["Version"], ">")))
722 {
723 # add error message and disable plugin
724 $this->ErrMsgs[$PluginName][] = "Plugin <i>"
725 .$ReqName." ".$ReqVersion."</i>"
726 ." required by <b>".$PluginName."</b>"
727 ." was not available.";
728 $this->PluginEnabled[$PluginName] = FALSE;
729 $this->PluginReady[$PluginName] = FALSE;
730
731 # set flag indicating plugin did not check out
732 $AllOkay = FALSE;
733 }
734 }
735 }
736 }
737 }
738 } while ($AllOkay == FALSE);
739 }
740
749 private function SortPluginsByInitializationPrecedence($Plugins)
750 {
751 # determine initialization order
752 $PluginAttribs = $this->GetPluginAttributes();
753 $PluginsAfterUs = array();
754 foreach ($PluginAttribs as $PluginName => $Attribs)
755 {
756 foreach ($Attribs["InitializeBefore"] as $OtherPluginName)
757 {
758 $PluginsAfterUs[$PluginName][] = $OtherPluginName;
759 }
760 foreach ($Attribs["InitializeAfter"] as $OtherPluginName)
761 {
762 $PluginsAfterUs[$OtherPluginName][] = $PluginName;
763 }
764 }
765
766 # infer other initialization order cues from lists of required plugins
767 foreach ($PluginAttribs as $PluginName => $Attribs)
768 {
769 # for each required plugin
770 foreach ($Attribs["Requires"]
771 as $RequiredPluginName => $RequiredPluginVersion)
772 {
773 # if there is not a requirement in the opposite direction
774 if (!array_key_exists($PluginName,
775 $PluginAttribs[$RequiredPluginName]["Requires"]))
776 {
777 # if the required plugin is not scheduled to be after us
778 if (!array_key_exists($PluginName, $PluginsAfterUs)
779 || !in_array($RequiredPluginName,
780 $PluginsAfterUs[$PluginName]))
781 {
782 # if we are not already scheduled to be after the required plugin
783 if (!array_key_exists($PluginName, $PluginsAfterUs)
784 || !in_array($RequiredPluginName,
785 $PluginsAfterUs[$PluginName]))
786 {
787 # schedule us to be after the required plugin
788 $PluginsAfterUs[$RequiredPluginName][] =
789 $PluginName;
790 }
791 }
792 }
793 }
794 }
795
796 # keep track of those plugins we have yet to do and those that are done
797 $UnsortedPlugins = array_keys($Plugins);
798 $PluginsProcessed = array();
799
800 # limit the number of iterations of the plugin ordering loop
801 # to 10 times the number of plugins we have
802 $MaxIterations = 10 * count($UnsortedPlugins);
803 $IterationCount = 0;
804
805 # iterate through all the plugins that need processing
806 while (($NextPlugin = array_shift($UnsortedPlugins)) !== NULL)
807 {
808 # check to be sure that we're not looping forever
809 $IterationCount++;
810 if ($IterationCount > $MaxIterations)
811 {
812 throw new Exception(
813 "Max iteration count exceeded trying to determine plugin"
814 ." loading order. Is there a dependency loop?");
815 }
816
817 # if no plugins require this one, it can go last
818 if (!isset($PluginsAfterUs[$NextPlugin]))
819 {
820 $PluginsProcessed[$NextPlugin] = $MaxIterations;
821 }
822 else
823 {
824 # for plugins that are required by others
825 $Index = $MaxIterations;
826 foreach ($PluginsAfterUs[$NextPlugin] as $GoBefore)
827 {
828 if (!isset($PluginsProcessed[$GoBefore]))
829 {
830 # if there is something that requires us which hasn't
831 # yet been assigned an order, then we can't determine
832 # our own place on this iteration
833 array_push($UnsortedPlugins, $NextPlugin);
834 continue 2;
835 }
836 else
837 {
838 # otherwise, make sure that we're loaded
839 # before the earliest of the things that require us
840 $Index = min($Index, $PluginsProcessed[$GoBefore] - 1);
841 }
842 }
843 $PluginsProcessed[$NextPlugin] = $Index;
844 }
845 }
846
847 # arrange plugins according to our ordering
848 asort($PluginsProcessed, SORT_NUMERIC);
849 $SortedPlugins = array();
850 foreach ($PluginsProcessed as $PluginName => $SortOrder)
851 {
852 $SortedPlugins[$PluginName] = $Plugins[$PluginName];
853 }
854
855 # return sorted list to caller
856 return $SortedPlugins;
857 }
858
867 function FindPluginPhpFile($PageName)
868 {
869 # build list of possible locations for file
870 $Locations = array(
871 "local/plugins/%PLUGIN%/pages/%PAGE%.php",
872 "plugins/%PLUGIN%/pages/%PAGE%.php",
873 "local/plugins/%PLUGIN%/%PAGE%.php",
874 "plugins/%PLUGIN%/%PAGE%.php",
875 );
876
877 # look for file and return (possibly) updated page to caller
878 return $this->FindPluginPageFile($PageName, $Locations);
879 }
890 function FindPluginHtmlFile($PageName)
891 {
892 # build list of possible locations for file
893 $Locations = array(
894 "local/plugins/%PLUGIN%/interface/%ACTIVEUI%/%PAGE%.html",
895 "plugins/%PLUGIN%/interface/%ACTIVEUI%/%PAGE%.html",
896 "local/plugins/%PLUGIN%/interface/default/%PAGE%.html",
897 "plugins/%PLUGIN%/interface/default/%PAGE%.html",
898 "local/plugins/%PLUGIN%/%PAGE%.html",
899 "plugins/%PLUGIN%/%PAGE%.html",
900 );
901
902 # find HTML file
903 $Params = $this->FindPluginPageFile($PageName, $Locations);
904
905 # if plugin HTML file was found
906 if ($Params["PageName"] != $PageName)
907 {
908 # add subdirectories for plugin to search paths
909 $Dir = preg_replace("%^local/%", "", dirname($Params["PageName"]));
910 $GLOBALS["AF"]->AddImageDirectories(array(
911 "local/".$Dir."/images",
912 $Dir."/images",
913 ));
914 $GLOBALS["AF"]->AddIncludeDirectories(array(
915 "local/".$Dir."/include",
916 $Dir."/include",
917 ));
918 $GLOBALS["AF"]->AddFunctionDirectories(array(
919 "local/".$Dir."/include",
920 $Dir."/include",
921 ));
922 }
923
924 # return possibly revised HTML file name to caller
925 return $Params;
926 }
937 private function FindPluginPageFile($PageName, $Locations)
938 {
939 # set up return value assuming we will not find plugin page file
940 $ReturnValue["PageName"] = $PageName;
941
942 # look for plugin name and plugin page name in base page name
943 preg_match("/P_([A-Za-z].[A-Za-z0-9]*)_([A-Za-z0-9_-]+)/", $PageName, $Matches);
944
945 # if base page name contained name of enabled plugin with its own subdirectory
946 if ((count($Matches) == 3)
947 && $this->PluginEnabled[$Matches[1]]
948 && $this->PluginHasDir[$Matches[1]])
949 {
950 # for each possible location
951 $ActiveUI = $GLOBALS["AF"]->ActiveUserInterface();
952 $PluginName = $Matches[1];
953 $PageName = $Matches[2];
954 foreach ($Locations as $Loc)
955 {
956 # make any needed substitutions into path
957 $FileName = str_replace(array("%ACTIVEUI%", "%PLUGIN%", "%PAGE%"),
958 array($ActiveUI, $PluginName, $PageName), $Loc);
959
960 # if file exists in this location
961 if (file_exists($FileName))
962 {
963 # set return value to contain full plugin page file name
964 $ReturnValue["PageName"] = $FileName;
965
966 # save plugin name as home of current page
967 $this->PageFilePlugin = $PluginName;
968
969 # set G_Plugin to plugin associated with current page
970 $GLOBALS["G_Plugin"] = $this->GetPluginForCurrentPage();
971
972 # stop looking
973 break;
974 }
975 }
976 }
977
978 # return array containing page name or page file name to caller
979 return $ReturnValue;
980 }
981
989 static function CfgSaveCallback($BaseName, $Cfg)
990 {
991 $DB = new Database();
992 $DB->Query("UPDATE PluginInfo SET Cfg = '".addslashes(serialize($Cfg))
993 ."' WHERE BaseName = '".addslashes($BaseName)."'");
994 }
996 }
997
1009 class PluginCaller {
1010
1017 function __construct($PluginName, $MethodName)
1018 {
1019 $this->PluginName = $PluginName;
1020 $this->MethodName = $MethodName;
1021 }
1022
1028 function CallPluginMethod()
1029 {
1030 $Args = func_get_args();
1031 $Plugin = self::$Manager->GetPlugin($this->PluginName);
1032 return call_user_func_array(array($Plugin, $this->MethodName), $Args);
1033 }
1034
1039 function GetCallbackAsText()
1040 {
1041 return $this->PluginName."::".$this->MethodName;
1042 }
1043
1049 function __sleep()
1050 {
1051 return array("PluginName", "MethodName");
1052 }
1053
1055 static public $Manager;
1056
1057 private $PluginName;
1058 private $MethodName;
1059 }
1062 ?>
PluginManager\LoadPlugins
LoadPlugins($ForcePluginConfigOptLoad=FALSE)
Load and initialize plugins.
Definition: PluginManager.php:53
PluginManager\GetErrorMessages
GetErrorMessages()
Retrieve any error messages generated during plugin loading.
Definition: PluginManager.php:298
PluginManager
Manager to load and invoke plugins.
Definition: PluginManager.php:13
Database
SQL database abstraction object with smart query caching.
Definition: Axis--Database.php:22
PluginManager\UninstallPlugin
UninstallPlugin($PluginName)
Uninstall plugin and (optionally) delete any associated data.
Definition: PluginManager.php:434
PluginManager\GetDependents
GetDependents($PluginName)
Returns a list of plugins dependent on the specified plugin.
Definition: PluginManager.php:374
PluginManager\GetActivePluginList
GetActivePluginList()
Get list of active (i.e.
Definition: PluginManager.php:394
PluginManager\GetPluginAttributes
GetPluginAttributes()
Retrieve info about currently loaded plugins.
Definition: PluginManager.php:352
PluginManager\GetPlugin
GetPlugin($PluginName)
Retrieve specified plugin.
Definition: PluginManager.php:319
PluginManager\PluginEnabled
PluginEnabled($PluginName, $NewValue=NULL)
Get/set whether specified plugin is enabled.
Definition: PluginManager.php:405
PHP
PHP
Definition: RSSClient--Test.php:12
PluginManager\__construct
__construct($AppFramework, $PluginDirectories)
PluginManager class constructor.
Definition: PluginManager.php:24
PluginManager\GetPluginForCurrentPage
GetPluginForCurrentPage()
Retrieve plugin for current page (if any).
Definition: PluginManager.php:343
PluginManager\GetStatusMessages
GetStatusMessages()
Get/set any status messages generated during plugin loading or via plugin method calls.
Definition: PluginManager.php:309