00001 <?PHP
00002
00003 #
00004 # FILE: ApplicationFramework.php
00005 #
00006 # Part of the ScoutLib application support library
00007 # Copyright 2009-2011 Edward Almasy and Internet Scout
00008 # http://scout.wisc.edu
00009 #
00010
00015 class ApplicationFramework {
00016
00017 # ---- PUBLIC INTERFACE --------------------------------------------------
00018
00020
00028 function __construct($ObjectDirectories = NULL)
00029 {
00030 # save execution start time
00031 $this->ExecutionStartTime = microtime(TRUE);
00032
00033 # save object directory search list
00034 if ($ObjectDirectories) { $this->AddObjectDirectories($ObjectDirectories); }
00035
00036 # set up object file autoloader
00037 $this->SetUpObjectAutoloading();
00038
00039 # set up function to output any buffered text in case of crash
00040 register_shutdown_function(array($this, "OnCrash"));
00041
00042 # set up our internal environment
00043 $this->DB = new Database();
00044
00045 # load our settings from database
00046 $this->DB->Query("SELECT * FROM ApplicationFrameworkSettings");
00047 $this->Settings = $this->DB->FetchRow();
00048 if (!$this->Settings)
00049 {
00050 $this->DB->Query("INSERT INTO ApplicationFrameworkSettings"
00051 ." (LastTaskRunAt) VALUES ('2000-01-02 03:04:05')");
00052 $this->DB->Query("SELECT * FROM ApplicationFrameworkSettings");
00053 $this->Settings = $this->DB->FetchRow();
00054 }
00055
00056 # set PHP maximum execution time
00057 $this->MaxExecutionTime($this->Settings["MaxExecTime"]);
00058
00059 # register events we handle internally
00060 $this->RegisterEvent($this->PeriodicEvents);
00061 $this->RegisterEvent($this->UIEvents);
00062 }
00070 function AddObjectDirectories($Dirs)
00071 {
00072 foreach ($Dirs as $Location => $Prefix)
00073 {
00074 $Location = $Location
00075 .((substr($Location, -1) != "/") ? "/" : "");
00076 self::$ObjectDirectories = array_merge(
00077 array($Location => $Prefix),
00078 self::$ObjectDirectories);
00079 }
00080 }
00081
00087 function SetBrowserDetectionFunc($DetectionFunc)
00088 {
00089 $this->BrowserDetectFunc = $DetectionFunc;
00090 }
00091
00096 function LoadPage($PageName)
00097 {
00098 # buffer any output from includes or PHP file
00099 ob_start();
00100
00101 # include any files needed to set up execution environment
00102 foreach ($this->EnvIncludes as $IncludeFile)
00103 {
00104 include($IncludeFile);
00105 }
00106
00107 # sanitize incoming page name
00108 $PageName = preg_replace("/[^a-zA-Z0-9_.-]/", "", $PageName);
00109
00110 # signal page load
00111 $this->SignalEvent("EVENT_PAGE_LOAD", array("PageName" => $PageName));
00112
00113 # signal PHP file load
00114 $SignalResult = $this->SignalEvent("EVENT_PHP_FILE_LOAD", array(
00115 "PageName" => $PageName));
00116
00117 # if signal handler returned new page name value
00118 $NewPageName = $PageName;
00119 if (($SignalResult["PageName"] != $PageName)
00120 && strlen($SignalResult["PageName"]))
00121 {
00122 # if new page name value is page file
00123 if (file_exists($SignalResult["PageName"]))
00124 {
00125 # use new value for PHP file name
00126 $PageFile = $SignalResult["PageName"];
00127 }
00128 else
00129 {
00130 # use new value for page name
00131 $NewPageName = $SignalResult["PageName"];
00132 }
00133 }
00134
00135 # if we do not already have a PHP file
00136 if (!isset($PageFile))
00137 {
00138 # look for PHP file for page
00139 $OurPageFile = "pages/".$NewPageName.".php";
00140 $LocalPageFile = "local/pages/".$NewPageName.".php";
00141 $PageFile = file_exists($LocalPageFile) ? $LocalPageFile
00142 : (file_exists($OurPageFile) ? $OurPageFile
00143 : "pages/".$this->DefaultPage.".php");
00144 }
00145
00146 # load PHP file
00147 include($PageFile);
00148
00149 # save buffered output to be displayed later after HTML file loads
00150 $PageOutput = ob_get_contents();
00151 ob_end_clean();
00152
00153 # set up for possible TSR (Terminate and Stay Resident :))
00154 $ShouldTSR = $this->PrepForTSR();
00155
00156 # if PHP file indicated we should autorefresh to somewhere else
00157 if ($this->JumpToPage)
00158 {
00159 if (!strlen(trim($PageOutput)))
00160 {
00161 ?><html>
00162 <head>
00163 <meta http-equiv="refresh" content="0; URL=<?PHP
00164 print($this->JumpToPage); ?>">
00165 </head>
00166 <body bgcolor="white">
00167 </body>
00168 </html><?PHP
00169 }
00170 }
00171 # else if HTML loading is not suppressed
00172 elseif (!$this->SuppressHTML)
00173 {
00174 # set content-type to get rid of diacritic errors
00175 header("Content-Type: text/html; charset="
00176 .$this->HtmlCharset, TRUE);
00177
00178 # load common HTML file (defines common functions) if available
00179 $CommonHtmlFile = $this->FindCommonTemplate("Common");
00180 if ($CommonHtmlFile) { include($CommonHtmlFile); }
00181
00182 # load UI functions
00183 $this->LoadUIFunctions();
00184
00185 # begin buffering content
00186 ob_start();
00187
00188 # signal HTML file load
00189 $SignalResult = $this->SignalEvent("EVENT_HTML_FILE_LOAD", array(
00190 "PageName" => $PageName));
00191
00192 # if signal handler returned new page name value
00193 $NewPageName = $PageName;
00194 $PageContentFile = NULL;
00195 if (($SignalResult["PageName"] != $PageName)
00196 && strlen($SignalResult["PageName"]))
00197 {
00198 # if new page name value is HTML file
00199 if (file_exists($SignalResult["PageName"]))
00200 {
00201 # use new value for HTML file name
00202 $PageContentFile = $SignalResult["PageName"];
00203 }
00204 else
00205 {
00206 # use new value for page name
00207 $NewPageName = $SignalResult["PageName"];
00208 }
00209 }
00210
00211 # load page content HTML file if available
00212 if ($PageContentFile === NULL)
00213 {
00214 $PageContentFile = $this->FindTemplate(
00215 $this->ContentTemplateList, $NewPageName);
00216 }
00217 if ($PageContentFile)
00218 {
00219 include($PageContentFile);
00220 }
00221 else
00222 {
00223 print "<h2>ERROR: No HTML/TPL template found"
00224 ." for this page.</h2>";
00225 }
00226
00227 # signal HTML file load complete
00228 $SignalResult = $this->SignalEvent("EVENT_HTML_FILE_LOAD_COMPLETE");
00229
00230 # stop buffering and save output
00231 $PageContentOutput = ob_get_contents();
00232 ob_end_clean();
00233
00234 # load page start HTML file if available
00235 ob_start();
00236 $PageStartFile = $this->FindCommonTemplate("Start");
00237 if ($PageStartFile) { include($PageStartFile); }
00238 $PageStartOutput = ob_get_contents();
00239 ob_end_clean();
00240
00241 # load page end HTML file if available
00242 ob_start();
00243 $PageEndFile = $this->FindCommonTemplate("End");
00244 if ($PageEndFile) { include($PageEndFile); }
00245 $PageEndOutput = ob_get_contents();
00246 ob_end_clean();
00247
00248 # get list of any required files not loaded
00249 $RequiredFiles = $this->GetRequiredFilesNotYetLoaded($PageContentFile);
00250
00251 # if a browser detection function has been made available
00252 if (is_callable($this->BrowserDetectFunc))
00253 {
00254 # call function to get browser list
00255 $Browsers = call_user_func($this->BrowserDetectFunc);
00256
00257 # for each required file
00258 $NewRequiredFiles = array();
00259 foreach ($RequiredFiles as $File)
00260 {
00261 # if file name includes browser keyword
00262 if (preg_match("/%BROWSER%/", $File))
00263 {
00264 # for each browser
00265 foreach ($Browsers as $Browser)
00266 {
00267 # substitute in browser name and add to new file list
00268 $NewRequiredFiles[] = preg_replace(
00269 "/%BROWSER%/", $Browser, $File);
00270 }
00271 }
00272 else
00273 {
00274 # add to new file list
00275 $NewRequiredFiles[] = $File;
00276 }
00277 }
00278 $RequiredFiles = $NewRequiredFiles;
00279 }
00280
00281 # for each required file
00282 foreach ($RequiredFiles as $File)
00283 {
00284 # locate specific file to use
00285 $FilePath = $this->GUIFile($File);
00286
00287 # if file was found
00288 if ($FilePath)
00289 {
00290 # determine file type
00291 $NamePieces = explode(".", $File);
00292 $FileSuffix = strtolower(array_pop($NamePieces));
00293
00294 # add file to HTML output based on file type
00295 $FilePath = htmlspecialchars($FilePath);
00296 switch ($FileSuffix)
00297 {
00298 case "js":
00299 $Tag = '<script type="text/javascript" src="'
00300 .$FilePath.'"></script>';
00301 $PageEndOutput = preg_replace(
00302 "#</body>#i", $Tag."\n</body>", $PageEndOutput, 1);
00303 break;
00304
00305 case "css":
00306 $Tag = '<link rel="stylesheet" type="text/css"'
00307 .' media="all" href="'.$FilePath.'">';
00308 $PageStartOutput = preg_replace(
00309 "#</head>#i", $Tag."\n</head>", $PageStartOutput, 1);
00310 break;
00311 }
00312 }
00313 }
00314
00315 # write out page
00316 print $PageStartOutput.$PageContentOutput.$PageEndOutput;
00317 }
00318
00319 # run any post-processing routines
00320 foreach ($this->PostProcessingFuncs as $Func)
00321 {
00322 call_user_func_array($Func["FunctionName"], $Func["Arguments"]);
00323 }
00324
00325 # write out any output buffered from page code execution
00326 if (strlen($PageOutput))
00327 {
00328 if (!$this->SuppressHTML)
00329 {
00330 ?><table width="100%" cellpadding="5"
00331 style="border: 2px solid #666666; background: #CCCCCC;
00332 font-family: Courier New, Courier, monospace;
00333 margin-top: 10px;"><tr><td><?PHP
00334 }
00335 if ($this->JumpToPage)
00336 {
00337 ?><div style="color: #666666;"><span style="font-size: 150%;">
00338 <b>Page Jump Aborted</b></span>
00339 (because of error or other unexpected output)<br />
00340 <b>Jump Target:</b>
00341 <i><?PHP print($this->JumpToPage); ?></i></div><?PHP
00342 }
00343 print($PageOutput);
00344 if (!$this->SuppressHTML)
00345 {
00346 ?></td></tr></table><?PHP
00347 }
00348 }
00349
00350 # terminate and stay resident (TSR!) if indicated and HTML has been output
00351 # (only TSR if HTML has been output because otherwise browsers will misbehave)
00352 if ($ShouldTSR) { $this->LaunchTSR(); }
00353 }
00354
00361 function SetJumpToPage($Page)
00362 {
00363 if ((strpos($Page, "?") === FALSE)
00364 && ((strpos($Page, "=") !== FALSE)
00365 || ((strpos($Page, ".php") === FALSE)
00366 && (strpos($Page, ".htm") === FALSE)
00367 && (strpos($Page, "/") === FALSE))))
00368 {
00369 $this->JumpToPage = "index.php?P=".$Page;
00370 }
00371 else
00372 {
00373 $this->JumpToPage = $Page;
00374 }
00375 }
00376
00381 function JumpToPageIsSet()
00382 {
00383 return ($this->JumpToPage === NULL) ? FALSE : TRUE;
00384 }
00385
00395 function HtmlCharset($NewSetting = NULL)
00396 {
00397 if ($NewSetting !== NULL) { $this->HtmlCharset = $NewSetting; }
00398 return $this->HtmlCharset;
00399 }
00400
00407 function SuppressHTMLOutput($NewSetting = TRUE)
00408 {
00409 $this->SuppressHTML = $NewSetting;
00410 }
00411
00418 function ActiveUserInterface($UIName = NULL)
00419 {
00420 if ($UIName !== NULL)
00421 {
00422 $this->ActiveUI = preg_replace("/^SPTUI--/", "", $UIName);
00423 }
00424 return $this->ActiveUI;
00425 }
00426
00442 function AddPostProcessingCall($FunctionName,
00443 &$Arg1 = self::NOVALUE, &$Arg2 = self::NOVALUE, &$Arg3 = self::NOVALUE,
00444 &$Arg4 = self::NOVALUE, &$Arg5 = self::NOVALUE, &$Arg6 = self::NOVALUE,
00445 &$Arg7 = self::NOVALUE, &$Arg8 = self::NOVALUE, &$Arg9 = self::NOVALUE)
00446 {
00447 $FuncIndex = count($this->PostProcessingFuncs);
00448 $this->PostProcessingFuncs[$FuncIndex]["FunctionName"] = $FunctionName;
00449 $this->PostProcessingFuncs[$FuncIndex]["Arguments"] = array();
00450 $Index = 1;
00451 while (isset(${"Arg".$Index}) && (${"Arg".$Index} !== self::NOVALUE))
00452 {
00453 $this->PostProcessingFuncs[$FuncIndex]["Arguments"][$Index]
00454 =& ${"Arg".$Index};
00455 $Index++;
00456 }
00457 }
00458
00464 function AddEnvInclude($FileName)
00465 {
00466 $this->EnvIncludes[] = $FileName;
00467 }
00468
00475 function GUIFile($FileName)
00476 {
00477 # pull off file name suffix
00478 $NamePieces = explode(".", $FileName);
00479 $Suffix = strtolower(array_pop($NamePieces));
00480
00481 # determine which location to search based on file suffix
00482 $ImageSuffixes = array("gif", "jpg", "png");
00483 $FileList = in_array($Suffix, $ImageSuffixes)
00484 ? $this->ImageFileList : $this->CommonTemplateList;
00485
00486 # search for file
00487 $FoundFileName = $this->FindTemplate($FileList, $FileName);
00488
00489 # add non-image files to list of found files
00490 if (!in_array($Suffix, $ImageSuffixes))
00491 { $this->FoundUIFiles[] = basename($FoundFileName); }
00492
00493 # return file name to caller
00494 return $FoundFileName;
00495 }
00496
00506 function PUIFile($FileName)
00507 {
00508 $FullFileName = $this->GUIFile($FileName);
00509 if ($FullFileName) { print($FullFileName); }
00510 }
00511
00516 function FindCommonTemplate($PageName)
00517 {
00518 return $this->FindTemplate(
00519 array_merge($this->CommonTemplateList, $this->ContentTemplateList),
00520 $PageName);
00521 }
00522
00531 function LoadFunction($Callback)
00532 {
00533 if (!is_callable($Callback) && is_string($Callback))
00534 {
00535 $Locations = $this->FunctionFileList;
00536 foreach (self::$ObjectDirectories as $Location => $Prefix)
00537 {
00538 $Locations[] = $Location."%PAGENAME%.php";
00539 $Locations[] = $Location."%PAGENAME%.html";
00540 }
00541 $FunctionFileName = $this->FindTemplate($Locations, "F-".$Callback);
00542 if ($FunctionFileName)
00543 {
00544 include_once($FunctionFileName);
00545 }
00546 }
00547 return is_callable($Callback);
00548 }
00549
00554 function GetElapsedExecutionTime()
00555 {
00556 return microtime(TRUE) - $this->ExecutionStartTime;
00557 }
00558
00563 function GetSecondsBeforeTimeout()
00564 {
00565 return ini_get("max_execution_time") - $this->GetElapsedExecutionTime();
00566 }
00567
00568
00569
00570 # ---- Event Handling ----------------------------------------------------
00571
00573
00577 const EVENTTYPE_DEFAULT = 1;
00583 const EVENTTYPE_CHAIN = 2;
00589 const EVENTTYPE_FIRST = 3;
00597 const EVENTTYPE_NAMED = 4;
00598
00600 const ORDER_FIRST = 1;
00602 const ORDER_MIDDLE = 2;
00604 const ORDER_LAST = 3;
00605
00614 function RegisterEvent($EventsOrEventName, $EventType = NULL)
00615 {
00616 # convert parameters to array if not already in that form
00617 $Events = is_array($EventsOrEventName) ? $EventsOrEventName
00618 : array($EventsOrEventName => $Type);
00619
00620 # for each event
00621 foreach ($Events as $Name => $Type)
00622 {
00623 # store event information
00624 $this->RegisteredEvents[$Name]["Type"] = $Type;
00625 $this->RegisteredEvents[$Name]["Hooks"] = array();
00626 }
00627 }
00628
00642 function HookEvent($EventsOrEventName, $Callback = NULL, $Order = self::ORDER_MIDDLE)
00643 {
00644 # convert parameters to array if not already in that form
00645 $Events = is_array($EventsOrEventName) ? $EventsOrEventName
00646 : array($EventsOrEventName => $Callback);
00647
00648 # for each event
00649 $Success = TRUE;
00650 foreach ($Events as $EventName => $EventCallback)
00651 {
00652 # if callback is valid
00653 if (is_callable($EventCallback))
00654 {
00655 # if this is a periodic event we process internally
00656 if (isset($this->PeriodicEvents[$EventName]))
00657 {
00658 # process event now
00659 $this->ProcessPeriodicEvent($EventName, $EventCallback);
00660 }
00661 # if specified event has been registered
00662 elseif (isset($this->RegisteredEvents[$EventName]))
00663 {
00664 # add callback for event
00665 $this->RegisteredEvents[$EventName]["Hooks"][]
00666 = array("Callback" => $EventCallback, "Order" => $Order);
00667
00668 # sort callbacks by order
00669 if (count($this->RegisteredEvents[$EventName]["Hooks"]) > 1)
00670 {
00671 usort($this->RegisteredEvents[$EventName]["Hooks"],
00672 array("ApplicationFramework", "HookEvent_OrderCompare"));
00673 }
00674 }
00675 else
00676 {
00677 $Success = FALSE;
00678 }
00679 }
00680 else
00681 {
00682 $Success = FALSE;
00683 }
00684 }
00685
00686 # report to caller whether all callbacks were hooked
00687 return $Success;
00688 }
00689 private static function HookEvent_OrderCompare($A, $B)
00690 {
00691 if ($A["Order"] == $B["Order"]) { return 0; }
00692 return ($A["Order"] < $B["Order"]) ? -1 : 1;
00693 }
00694
00703 function SignalEvent($EventName, $Parameters = NULL)
00704 {
00705 $ReturnValue = NULL;
00706
00707 # if event has been registered
00708 if (isset($this->RegisteredEvents[$EventName]))
00709 {
00710 # set up default return value (if not NULL)
00711 switch ($this->RegisteredEvents[$EventName]["Type"])
00712 {
00713 case self::EVENTTYPE_CHAIN:
00714 $ReturnValue = $Parameters;
00715 break;
00716
00717 case self::EVENTTYPE_NAMED:
00718 $ReturnValue = array();
00719 break;
00720 }
00721
00722 # for each callback for this event
00723 foreach ($this->RegisteredEvents[$EventName]["Hooks"] as $Hook)
00724 {
00725 # invoke callback
00726 $Callback = $Hook["Callback"];
00727 $Result = ($Parameters !== NULL)
00728 ? call_user_func_array($Callback, $Parameters)
00729 : call_user_func($Callback);
00730
00731 # process return value based on event type
00732 switch ($this->RegisteredEvents[$EventName]["Type"])
00733 {
00734 case self::EVENTTYPE_CHAIN:
00735 $ReturnValue = $Result;
00736 $Parameters = $Result;
00737 break;
00738
00739 case self::EVENTTYPE_FIRST:
00740 if ($Result !== NULL)
00741 {
00742 $ReturnValue = $Result;
00743 break 2;
00744 }
00745 break;
00746
00747 case self::EVENTTYPE_NAMED:
00748 $CallbackName = is_array($Callback)
00749 ? (is_object($Callback[0])
00750 ? get_class($Callback[0])
00751 : $Callback[0])."::".$Callback[1]
00752 : $Callback;
00753 $ReturnValue[$CallbackName] = $Result;
00754 break;
00755
00756 default:
00757 break;
00758 }
00759 }
00760 }
00761
00762 # return value if any to caller
00763 return $ReturnValue;
00764 }
00765
00771 function IsStaticOnlyEvent($EventName)
00772 {
00773 return isset($this->PeriodicEvents[$EventName]) ? TRUE : FALSE;
00774 }
00775
00776
00777
00778 # ---- Task Management ---------------------------------------------------
00779
00781
00783 const PRIORITY_HIGH = 1;
00785 const PRIORITY_MEDIUM = 2;
00787 const PRIORITY_LOW = 3;
00789 const PRIORITY_BACKGROUND = 4;
00790
00803 function QueueTask($Callback, $Parameters = NULL,
00804 $Priority = self::PRIORITY_MEDIUM, $Description = "")
00805 {
00806 # pack task info and write to database
00807 if ($Parameters === NULL) { $Parameters = array(); }
00808 $this->DB->Query("INSERT INTO TaskQueue"
00809 ." (Callback, Parameters, Priority, Description)"
00810 ." VALUES ('".addslashes(serialize($Callback))."', '"
00811 .addslashes(serialize($Parameters))."', ".intval($Priority).", '"
00812 .addslashes($Description)."')");
00813 }
00814
00832 function QueueUniqueTask($Callback, $Parameters = NULL,
00833 $Priority = self::PRIORITY_MEDIUM, $Description = "")
00834 {
00835 if ($this->TaskIsInQueue($Callback, $Parameters))
00836 {
00837 $QueryResult = $this->DB->Query("SELECT TaskId,Priority FROM TaskQueue"
00838 ." WHERE Callback = '".addslashes(serialize($Callback))."'"
00839 .($Parameters ? " AND Parameters = '"
00840 .addslashes(serialize($Parameters))."'" : ""));
00841 if ($QueryResult !== FALSE)
00842 {
00843 $Record = $this->DB->FetchRow();
00844 if ($Record["Priority"] > $Priority)
00845 {
00846 $this->DB->Query("UPDATE TaskQueue"
00847 ." SET Priority = ".intval($Priority)
00848 ." WHERE TaskId = ".intval($Record["TaskId"]));
00849 }
00850 }
00851 return FALSE;
00852 }
00853 else
00854 {
00855 $this->QueueTask($Callback, $Parameters, $Priority, $Description);
00856 return TRUE;
00857 }
00858 }
00859
00869 function TaskIsInQueue($Callback, $Parameters = NULL)
00870 {
00871 $FoundCount = $this->DB->Query("SELECT COUNT(*) AS FoundCount FROM TaskQueue"
00872 ." WHERE Callback = '".addslashes(serialize($Callback))."'"
00873 .($Parameters ? " AND Parameters = '"
00874 .addslashes(serialize($Parameters))."'" : ""),
00875 "FoundCount")
00876 + $this->DB->Query("SELECT COUNT(*) AS FoundCount FROM RunningTasks"
00877 ." WHERE Callback = '".addslashes(serialize($Callback))."'"
00878 .($Parameters ? " AND Parameters = '"
00879 .addslashes(serialize($Parameters))."'" : ""),
00880 "FoundCount");
00881 return ($FoundCount ? TRUE : FALSE);
00882 }
00883
00889 function GetTaskQueueSize($Priority = NULL)
00890 {
00891 return $this->DB->Query("SELECT COUNT(*) AS QueueSize FROM TaskQueue"
00892 .($Priority ? " WHERE Priority = ".intval($Priority) : ""),
00893 "QueueSize");
00894 }
00895
00903 function GetQueuedTaskList($Count = 100, $Offset = 0)
00904 {
00905 return $this->GetTaskList("SELECT * FROM TaskQueue"
00906 ." ORDER BY Priority, TaskId ", $Count, $Offset);
00907 }
00908
00916 function GetRunningTaskList($Count = 100, $Offset = 0)
00917 {
00918 return $this->GetTaskList("SELECT * FROM RunningTasks"
00919 ." WHERE StartedAt >= '".date("Y-m-d H:i:s",
00920 (time() - ini_get("max_execution_time")))."'"
00921 ." ORDER BY StartedAt", $Count, $Offset);
00922 }
00923
00931 function GetOrphanedTaskList($Count = 100, $Offset = 0)
00932 {
00933 return $this->GetTaskList("SELECT * FROM RunningTasks"
00934 ." WHERE StartedAt < '".date("Y-m-d H:i:s",
00935 (time() - ini_get("max_execution_time")))."'"
00936 ." ORDER BY StartedAt", $Count, $Offset);
00937 }
00938
00943 function ReQueueOrphanedTask($TaskId)
00944 {
00945 $this->DB->Query("LOCK TABLES TaskQueue WRITE, RunningTasks WRITE");
00946 $this->DB->Query("INSERT INTO TaskQueue"
00947 ." (Callback,Parameters,Priority,Description) "
00948 ."SELECT Callback, Parameters, Priority, Description"
00949 ." FROM RunningTasks WHERE TaskId = ".intval($TaskId));
00950 $this->DB->Query("DELETE FROM RunningTasks WHERE TaskId = ".intval($TaskId));
00951 $this->DB->Query("UNLOCK TABLES");
00952 }
00953
00958 function DeleteTask($TaskId)
00959 {
00960 $this->DB->Query("DELETE FROM TaskQueue WHERE TaskId = ".intval($TaskId));
00961 $this->DB->Query("DELETE FROM RunningTasks WHERE TaskId = ".intval($TaskId));
00962 }
00963
00971 function GetTask($TaskId)
00972 {
00973 # assume task will not be found
00974 $Task = NULL;
00975
00976 # look for task in task queue
00977 $this->DB->Query("SELECT * FROM TaskQueue WHERE TaskId = ".intval($TaskId));
00978
00979 # if task was not found in queue
00980 if (!$this->DB->NumRowsSelected())
00981 {
00982 # look for task in running task list
00983 $this->DB->Query("SELECT * FROM RunningTasks WHERE TaskId = "
00984 .intval($TaskId));
00985 }
00986
00987 # if task was found
00988 if ($this->DB->NumRowsSelected())
00989 {
00990 # if task was periodic
00991 $Row = $this->DB->FetchRow();
00992 if ($Row["Callback"] ==
00993 serialize(array("ApplicationFramework", "PeriodicEventWrapper")))
00994 {
00995 # unpack periodic task callback
00996 $WrappedCallback = unserialize($Row["Parameters"]);
00997 $Task["Callback"] = $WrappedCallback[1];
00998 $Task["Parameters"] = NULL;
00999 }
01000 else
01001 {
01002 # unpack task callback and parameters
01003 $Task["Callback"] = unserialize($Row["Callback"]);
01004 $Task["Parameters"] = unserialize($Row["Parameters"]);
01005 }
01006 }
01007
01008 # return task to caller
01009 return $Task;
01010 }
01011
01017 function MaxTasks($NewValue = NULL)
01018 {
01019 if (func_num_args() && ($NewValue >= 1))
01020 {
01021 $this->DB->Query("UPDATE ApplicationFrameworkSettings"
01022 ." SET MaxTasksRunning = '".intval($NewValue)."'");
01023 $this->Settings["MaxTasksRunning"] = intval($NewValue);
01024 }
01025 return $this->Settings["MaxTasksRunning"];
01026 }
01027
01035 function MaxExecutionTime($NewValue = NULL)
01036 {
01037 if (func_num_args() && !ini_get("safe_mode"))
01038 {
01039 if ($NewValue != $this->Settings["MaxExecTime"])
01040 {
01041 $this->Settings["MaxExecTime"] = max($NewValue, 5);
01042 $this->DB->Query("UPDATE ApplicationFrameworkSettings"
01043 ." SET MaxExecTime = '"
01044 .intval($this->Settings["MaxExecTime"])."'");
01045 }
01046 ini_set("max_execution_time", $this->Settings["MaxExecTime"]);
01047 set_time_limit($this->Settings["MaxExecTime"]);
01048 }
01049 return ini_get("max_execution_time");
01050 }
01051
01052
01053
01054 # ---- PRIVATE INTERFACE -------------------------------------------------
01055
01056 private $ActiveUI = "default";
01057 private $BrowserDetectFunc;
01058 private $DB;
01059 private $DefaultPage = "Home";
01060 private $EnvIncludes = array();
01061 private $ExecutionStartTime;
01062 private $FoundUIFiles = array();
01063 private $HtmlCharset = "UTF-8";
01064 private $JumpToPage = NULL;
01065 private $MaxRunningTasksToTrack = 250;
01066 private static $ObjectDirectories = array();
01067 private $PostProcessingFuncs = array();
01068 private $RunningTask;
01069 private $Settings;
01070 private $SuppressHTML = FALSE;
01071
01072 private $PeriodicEvents = array(
01073 "EVENT_HOURLY" => self::EVENTTYPE_DEFAULT,
01074 "EVENT_DAILY" => self::EVENTTYPE_DEFAULT,
01075 "EVENT_WEEKLY" => self::EVENTTYPE_DEFAULT,
01076 "EVENT_MONTHLY" => self::EVENTTYPE_DEFAULT,
01077 "EVENT_PERIODIC" => self::EVENTTYPE_NAMED,
01078 );
01079 private $UIEvents = array(
01080 "EVENT_PAGE_LOAD" => self::EVENTTYPE_DEFAULT,
01081 "EVENT_PHP_FILE_LOAD" => self::EVENTTYPE_CHAIN,
01082 "EVENT_HTML_FILE_LOAD" => self::EVENTTYPE_CHAIN,
01083 "EVENT_HTML_FILE_LOAD_COMPLETE" => self::EVENTTYPE_DEFAULT,
01084 );
01085
01086 private function FindTemplate($FileList, $PageName)
01087 {
01088 $FileNameFound = NULL;
01089 foreach ($FileList as $FileName)
01090 {
01091 $FileName = str_replace("%ACTIVEUI%", $this->ActiveUI, $FileName);
01092 $FileName = str_replace("%PAGENAME%", $PageName, $FileName);
01093 if (file_exists($FileName))
01094 {
01095 $FileNameFound = $FileName;
01096 break;
01097 }
01098 }
01099 return $FileNameFound;
01100 }
01101
01108 private function GetRequiredFilesNotYetLoaded($PageContentFile)
01109 {
01110 # start out assuming no files required
01111 $RequiredFiles = array();
01112
01113 # if page content file supplied
01114 if ($PageContentFile)
01115 {
01116 # if file containing list of required files is available
01117 $Path = dirname($PageContentFile);
01118 $RequireListFile = $Path."/REQUIRES";
01119 if (file_exists($RequireListFile))
01120 {
01121 # read in list of required files
01122 $RequestedFiles = file($RequireListFile);
01123
01124 # for each line in required file list
01125 foreach ($RequestedFiles as $Line)
01126 {
01127 # if line is not a comment
01128 $Line = trim($Line);
01129 if (!preg_match("/^#/", $Line))
01130 {
01131 # if file has not already been loaded
01132 if (!in_array($Line, $this->FoundUIFiles))
01133 {
01134 # add to list of required files
01135 $RequiredFiles[] = $Line;
01136 }
01137 }
01138 }
01139 }
01140 }
01141
01142 # return list of required files to caller
01143 return $RequiredFiles;
01144 }
01145
01146 private function SetUpObjectAutoloading()
01147 {
01148 function __autoload($ClassName)
01149 {
01150 ApplicationFramework::AutoloadObjects($ClassName);
01151 }
01152 }
01153
01155 static function AutoloadObjects($ClassName)
01156 {
01157 foreach (self::$ObjectDirectories as $Location => $Prefix)
01158 {
01159 $FileName = $Location.$Prefix.$ClassName.".php";
01160 if (file_exists($FileName))
01161 {
01162 require_once($FileName);
01163 break;
01164 }
01165 }
01166 }
01169 private function LoadUIFunctions()
01170 {
01171 $Dirs = array(
01172 "local/interface/%ACTIVEUI%/include",
01173 "interface/%ACTIVEUI%/include",
01174 "local/interface/default/include",
01175 "interface/default/include",
01176 );
01177 foreach ($Dirs as $Dir)
01178 {
01179 $Dir = str_replace("%ACTIVEUI%", $this->ActiveUI, $Dir);
01180 if (is_dir($Dir))
01181 {
01182 $FileNames = scandir($Dir);
01183 foreach ($FileNames as $FileName)
01184 {
01185 if (preg_match("/^F-([A-Za-z_]+)\.php/", $FileName, $Matches)
01186 || preg_match("/^F-([A-Za-z_]+)\.html/", $FileName, $Matches))
01187 {
01188 if (!function_exists($Matches[1]))
01189 {
01190 include_once($Dir."/".$FileName);
01191 }
01192 }
01193 }
01194 }
01195 }
01196 }
01197
01198 private function ProcessPeriodicEvent($EventName, $Callback)
01199 {
01200 # retrieve last execution time for event if available
01201 $Signature = self::GetCallbackSignature($Callback);
01202 $LastRun = $this->DB->Query("SELECT LastRunAt FROM PeriodicEvents"
01203 ." WHERE Signature = '".addslashes($Signature)."'", "LastRunAt");
01204
01205 # determine whether enough time has passed for event to execute
01206 $EventPeriods = array(
01207 "EVENT_HOURLY" => 60*60,
01208 "EVENT_DAILY" => 60*60*24,
01209 "EVENT_WEEKLY" => 60*60*24*7,
01210 "EVENT_MONTHLY" => 60*60*24*30,
01211 "EVENT_PERIODIC" => 0,
01212 );
01213 $ShouldExecute = (($LastRun === NULL)
01214 || (time() > (strtotime($LastRun) + $EventPeriods[$EventName])))
01215 ? TRUE : FALSE;
01216
01217 # if event should run
01218 if ($ShouldExecute)
01219 {
01220 # add event to task queue
01221 $WrapperCallback = array("ApplicationFramework", "PeriodicEventWrapper");
01222 $WrapperParameters = array(
01223 $EventName, $Callback, array("LastRunAt" => $LastRun));
01224 $this->QueueUniqueTask($WrapperCallback, $WrapperParameters);
01225 }
01226 }
01227
01228 private static function PeriodicEventWrapper($EventName, $Callback, $Parameters)
01229 {
01230 static $DB;
01231 if (!isset($DB)) { $DB = new Database(); }
01232
01233 # run event
01234 $ReturnVal = call_user_func_array($Callback, $Parameters);
01235
01236 # if event is already in database
01237 $Signature = self::GetCallbackSignature($Callback);
01238 if ($DB->Query("SELECT COUNT(*) AS EventCount FROM PeriodicEvents"
01239 ." WHERE Signature = '".addslashes($Signature)."'", "EventCount"))
01240 {
01241 # update last run time for event
01242 $DB->Query("UPDATE PeriodicEvents SET LastRunAt = "
01243 .(($EventName == "EVENT_PERIODIC")
01244 ? "'".date("Y-m-d H:i:s", time() + ($ReturnVal * 60))."'"
01245 : "NOW()")
01246 ." WHERE Signature = '".addslashes($Signature)."'");
01247 }
01248 else
01249 {
01250 # add last run time for event to database
01251 $DB->Query("INSERT INTO PeriodicEvents (Signature, LastRunAt) VALUES "
01252 ."('".addslashes($Signature)."', "
01253 .(($EventName == "EVENT_PERIODIC")
01254 ? "'".date("Y-m-d H:i:s", time() + ($ReturnVal * 60))."'"
01255 : "NOW()").")");
01256 }
01257 }
01258
01259 private static function GetCallbackSignature($Callback)
01260 {
01261 return !is_array($Callback) ? $Callback
01262 : (is_object($Callback[0]) ? md5(serialize($Callback[0])) : $Callback[0])
01263 ."::".$Callback[1];
01264 }
01265
01266 private function PrepForTSR()
01267 {
01268 # if HTML has been output and it's time to launch another task
01269 # (only TSR if HTML has been output because otherwise browsers
01270 # may misbehave after connection is closed)
01271 if (($this->JumpToPage || !$this->SuppressHTML)
01272 && (time() > (strtotime($this->Settings["LastTaskRunAt"])
01273 + (ini_get("max_execution_time")
01274 / $this->Settings["MaxTasksRunning"]) + 5))
01275 && $this->GetTaskQueueSize())
01276 {
01277 # begin buffering output for TSR
01278 ob_start();
01279
01280 # let caller know it is time to launch another task
01281 return TRUE;
01282 }
01283 else
01284 {
01285 # let caller know it is not time to launch another task
01286 return FALSE;
01287 }
01288 }
01289
01290 private function LaunchTSR()
01291 {
01292 # set needed headers and
01293 ignore_user_abort(TRUE);
01294 header("Connection: close");
01295 header("Content-Length: ".ob_get_length());
01296
01297 # output buffered content
01298 ob_end_flush();
01299 flush();
01300
01301 # write out any outstanding data and end HTTP session
01302 session_write_close();
01303
01304 # if there is still a task in the queue
01305 if ($this->GetTaskQueueSize())
01306 {
01307 # turn on output buffering to (hopefully) record any crash output
01308 ob_start();
01309
01310 # lock tables and grab last task run time to double check
01311 $this->DB->Query("LOCK TABLES ApplicationFrameworkSettings WRITE");
01312 $this->DB->Query("SELECT * FROM ApplicationFrameworkSettings");
01313 $this->Settings = $this->DB->FetchRow();
01314
01315 # if still time to launch another task
01316 if (time() > (strtotime($this->Settings["LastTaskRunAt"])
01317 + (ini_get("max_execution_time")
01318 / $this->Settings["MaxTasksRunning"]) + 5))
01319 {
01320 # update the "last run" time and release tables
01321 $this->DB->Query("UPDATE ApplicationFrameworkSettings"
01322 ." SET LastTaskRunAt = '".date("Y-m-d H:i:s")."'");
01323 $this->DB->Query("UNLOCK TABLES");
01324
01325 # run tasks while there is a task in the queue and enough time left
01326 do
01327 {
01328 # run the next task
01329 $this->RunNextTask();
01330 }
01331 while ($this->GetTaskQueueSize()
01332 && ($this->GetSecondsBeforeTimeout() > 65));
01333 }
01334 else
01335 {
01336 # release tables
01337 $this->DB->Query("UNLOCK TABLES");
01338 }
01339 }
01340 }
01341
01349 private function GetTaskList($DBQuery, $Count, $Offset)
01350 {
01351 $this->DB->Query($DBQuery." LIMIT ".intval($Offset).",".intval($Count));
01352 $Tasks = array();
01353 while ($Row = $this->DB->FetchRow())
01354 {
01355 $Tasks[$Row["TaskId"]] = $Row;
01356 if ($Row["Callback"] ==
01357 serialize(array("ApplicationFramework", "PeriodicEventWrapper")))
01358 {
01359 $WrappedCallback = unserialize($Row["Parameters"]);
01360 $Tasks[$Row["TaskId"]]["Callback"] = $WrappedCallback[1];
01361 $Tasks[$Row["TaskId"]]["Parameters"] = NULL;
01362 }
01363 else
01364 {
01365 $Tasks[$Row["TaskId"]]["Callback"] = unserialize($Row["Callback"]);
01366 $Tasks[$Row["TaskId"]]["Parameters"] = unserialize($Row["Parameters"]);
01367 }
01368 }
01369 return $Tasks;
01370 }
01371
01375 private function RunNextTask()
01376 {
01377 # look for task at head of queue
01378 $this->DB->Query("SELECT * FROM TaskQueue ORDER BY Priority, TaskId LIMIT 1");
01379 $Task = $this->DB->FetchRow();
01380
01381 # if there was a task available
01382 if ($Task)
01383 {
01384 # move task from queue to running tasks list
01385 $this->DB->Query("INSERT INTO RunningTasks "
01386 ."(TaskId,Callback,Parameters,Priority,Description) "
01387 ."SELECT * FROM TaskQueue WHERE TaskId = "
01388 .intval($Task["TaskId"]));
01389 $this->DB->Query("DELETE FROM TaskQueue WHERE TaskId = "
01390 .intval($Task["TaskId"]));
01391
01392 # unpack stored task info
01393 $Callback = unserialize($Task["Callback"]);
01394 $Parameters = unserialize($Task["Parameters"]);
01395
01396 # attempt to load task callback if not already available
01397 $this->LoadFunction($Callback);
01398
01399 # run task
01400 $this->RunningTask = $Task;
01401 if ($Parameters)
01402 {
01403 call_user_func_array($Callback, $Parameters);
01404 }
01405 else
01406 {
01407 call_user_func($Callback);
01408 }
01409 unset($this->RunningTask);
01410
01411 # remove task from running tasks list
01412 $this->DB->Query("DELETE FROM RunningTasks"
01413 ." WHERE TaskId = ".intval($Task["TaskId"]));
01414
01415 # prune running tasks list if necessary
01416 $RunningTasksCount = $this->DB->Query(
01417 "SELECT COUNT(*) AS TaskCount FROM RunningTasks", "TaskCount");
01418 if ($RunningTasksCount > $this->MaxRunningTasksToTrack)
01419 {
01420 $this->DB->Query("DELETE FROM RunningTasks ORDER BY StartedAt"
01421 ." LIMIT ".($RunningTasksCount - $this->MaxRunningTasksToTrack));
01422 }
01423 }
01424 }
01425
01431 function OnCrash()
01432 {
01433 if (isset($this->RunningTask))
01434 {
01435 if (function_exists("error_get_last"))
01436 {
01437 $CrashInfo["LastError"] = error_get_last();
01438 }
01439 if (ob_get_length() !== FALSE)
01440 {
01441 $CrashInfo["OutputBuffer"] = ob_get_contents();
01442 }
01443 if (isset($CrashInfo))
01444 {
01445 $DB = new Database();
01446 $DB->Query("UPDATE RunningTasks SET CrashInfo = '"
01447 .addslashes(serialize($CrashInfo))
01448 ."' WHERE TaskId = ".intval($this->RunningTask["TaskId"]));
01449 }
01450 }
01451
01452 print("\n");
01453 return;
01454
01455 if (ob_get_length() !== FALSE)
01456 {
01457 ?>
01458 <table width="100%" cellpadding="5" style="border: 2px solid #666666; background: #FFCCCC; font-family: Courier New, Courier, monospace; margin-top: 10px; font-weight: bold;"><tr><td>
01459 <div style="font-size: 200%;">CRASH OUTPUT</div><?PHP
01460 ob_end_flush();
01461 ?></td></tr></table><?PHP
01462 }
01463 }
01464
01465 private $CommonTemplateList = array(
01466 "local/interface/%ACTIVEUI%/include/StdPage%PAGENAME%.tpl",
01467 "local/interface/%ACTIVEUI%/include/StdPage%PAGENAME%.html",
01468 "local/interface/%ACTIVEUI%/include/%PAGENAME%.tpl",
01469 "local/interface/%ACTIVEUI%/include/%PAGENAME%.html",
01470 "local/interface/%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.tpl",
01471 "local/interface/%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.html",
01472 "local/interface/%ACTIVEUI%/include/SPT--%PAGENAME%.tpl",
01473 "local/interface/%ACTIVEUI%/include/SPT--%PAGENAME%.html",
01474 "local/interface/%ACTIVEUI%/include/%PAGENAME%",
01475 "interface/%ACTIVEUI%/include/StdPage%PAGENAME%.tpl",
01476 "interface/%ACTIVEUI%/include/StdPage%PAGENAME%.html",
01477 "interface/%ACTIVEUI%/include/%PAGENAME%.tpl",
01478 "interface/%ACTIVEUI%/include/%PAGENAME%.html",
01479 "interface/%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.tpl",
01480 "interface/%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.html",
01481 "interface/%ACTIVEUI%/include/SPT--%PAGENAME%.tpl",
01482 "interface/%ACTIVEUI%/include/SPT--%PAGENAME%.html",
01483 "interface/%ACTIVEUI%/include/%PAGENAME%",
01484 "SPTUI--%ACTIVEUI%/include/StdPage%PAGENAME%.tpl",
01485 "SPTUI--%ACTIVEUI%/include/StdPage%PAGENAME%.html",
01486 "SPTUI--%ACTIVEUI%/include/%PAGENAME%.tpl",
01487 "SPTUI--%ACTIVEUI%/include/%PAGENAME%.html",
01488 "SPTUI--%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.tpl",
01489 "SPTUI--%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.html",
01490 "SPTUI--%ACTIVEUI%/include/SPT--%PAGENAME%.tpl",
01491 "SPTUI--%ACTIVEUI%/include/SPT--%PAGENAME%.html",
01492 "SPTUI--%ACTIVEUI%/include/%PAGENAME%",
01493 "%ACTIVEUI%/include/StdPage%PAGENAME%.tpl",
01494 "%ACTIVEUI%/include/StdPage%PAGENAME%.html",
01495 "%ACTIVEUI%/include/%PAGENAME%.tpl",
01496 "%ACTIVEUI%/include/%PAGENAME%.html",
01497 "%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.tpl",
01498 "%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.html",
01499 "%ACTIVEUI%/include/SPT--%PAGENAME%.tpl",
01500 "%ACTIVEUI%/include/SPT--%PAGENAME%.html",
01501 "%ACTIVEUI%/include/%PAGENAME%",
01502 "local/interface/default/include/StdPage%PAGENAME%.tpl",
01503 "local/interface/default/include/StdPage%PAGENAME%.html",
01504 "local/interface/default/include/%PAGENAME%.tpl",
01505 "local/interface/default/include/%PAGENAME%.html",
01506 "local/interface/default/include/SPT--StandardPage%PAGENAME%.tpl",
01507 "local/interface/default/include/SPT--StandardPage%PAGENAME%.html",
01508 "local/interface/default/include/SPT--%PAGENAME%.tpl",
01509 "local/interface/default/include/SPT--%PAGENAME%.html",
01510 "local/interface/default/include/%PAGENAME%",
01511 "interface/default/include/StdPage%PAGENAME%.tpl",
01512 "interface/default/include/StdPage%PAGENAME%.html",
01513 "interface/default/include/%PAGENAME%.tpl",
01514 "interface/default/include/%PAGENAME%.html",
01515 "interface/default/include/SPT--StandardPage%PAGENAME%.tpl",
01516 "interface/default/include/SPT--StandardPage%PAGENAME%.html",
01517 "interface/default/include/SPT--%PAGENAME%.tpl",
01518 "interface/default/include/SPT--%PAGENAME%.html",
01519 "interface/default/include/%PAGENAME%",
01520 );
01521 private $ContentTemplateList = array(
01522 "local/interface/%ACTIVEUI%/%PAGENAME%.tpl",
01523 "local/interface/%ACTIVEUI%/%PAGENAME%.html",
01524 "local/interface/%ACTIVEUI%/SPT--%PAGENAME%.tpl",
01525 "local/interface/%ACTIVEUI%/SPT--%PAGENAME%.html",
01526 "interface/%ACTIVEUI%/%PAGENAME%.tpl",
01527 "interface/%ACTIVEUI%/%PAGENAME%.html",
01528 "interface/%ACTIVEUI%/SPT--%PAGENAME%.tpl",
01529 "interface/%ACTIVEUI%/SPT--%PAGENAME%.html",
01530 "SPTUI--%ACTIVEUI%/%PAGENAME%.tpl",
01531 "SPTUI--%ACTIVEUI%/%PAGENAME%.html",
01532 "SPTUI--%ACTIVEUI%/SPT--%PAGENAME%.tpl",
01533 "SPTUI--%ACTIVEUI%/SPT--%PAGENAME%.html",
01534 "%ACTIVEUI%/%PAGENAME%.tpl",
01535 "%ACTIVEUI%/%PAGENAME%.html",
01536 "%ACTIVEUI%/SPT--%PAGENAME%.tpl",
01537 "%ACTIVEUI%/SPT--%PAGENAME%.html",
01538 "local/interface/default/%PAGENAME%.tpl",
01539 "local/interface/default/%PAGENAME%.html",
01540 "local/interface/default/SPT--%PAGENAME%.tpl",
01541 "local/interface/default/SPT--%PAGENAME%.html",
01542 "interface/default/%PAGENAME%.tpl",
01543 "interface/default/%PAGENAME%.html",
01544 "interface/default/SPT--%PAGENAME%.tpl",
01545 "interface/default/SPT--%PAGENAME%.html",
01546 );
01547 private $ImageFileList = array(
01548 "local/interface/%ACTIVEUI%/images/%PAGENAME%",
01549 "interface/%ACTIVEUI%/images/%PAGENAME%",
01550 "SPTUI--%ACTIVEUI%/images/%PAGENAME%",
01551 "%ACTIVEUI%/images/%PAGENAME%",
01552 "local/interface/default/images/%PAGENAME%",
01553 "interface/default/images/%PAGENAME%",
01554 );
01555 private $FunctionFileList = array(
01556 "local/interface/%ACTIVEUI%/include/%PAGENAME%.php",
01557 "local/interface/%ACTIVEUI%/include/%PAGENAME%.html",
01558 "interface/%ACTIVEUI%/include/%PAGENAME%.php",
01559 "interface/%ACTIVEUI%/include/%PAGENAME%.html",
01560 "SPTUI--%ACTIVEUI%/include/%PAGENAME%.php",
01561 "SPTUI--%ACTIVEUI%/include/%PAGENAME%.html",
01562 "%ACTIVEUI%/include/%PAGENAME%.php",
01563 "%ACTIVEUI%/include/%PAGENAME%.html",
01564 "local/interface/default/include/%PAGENAME%.php",
01565 "local/interface/default/include/%PAGENAME%.html",
01566 "local/include/%PAGENAME%.php",
01567 "local/include/%PAGENAME%.html",
01568 "interface/default/include/%PAGENAME%.php",
01569 "interface/default/include/%PAGENAME%.html",
01570 "include/%PAGENAME%.php",
01571 "include/%PAGENAME%.html",
01572 );
01573
01574 const NOVALUE = ".-+-.NO VALUE PASSED IN FOR ARGUMENT.-+-.";
01575 };
01576
01577
01578 ?>