00001 <?PHP
00002
00007 class ApplicationFramework {
00008
00009 # ---- PUBLIC INTERFACE --------------------------------------------------
00010
00012
00020 function __construct($ObjectDirectories = NULL)
00021 {
00022 # save execution start time
00023 $this->ExecutionStartTime = microtime(TRUE);
00024
00025 # save object directory search list
00026 if ($ObjectDirectories) { $this->AddObjectDirectories($ObjectDirectories); }
00027
00028 # set up object file autoloader
00029 $this->SetUpObjectAutoloading();
00030
00031 # set up function to output any buffered text in case of crash
00032 register_shutdown_function(array($this, "OnCrash"));
00033
00034 # set up our internal environment
00035 $this->DB = new Database();
00036
00037 # load our settings from database
00038 $this->DB->Query("SELECT * FROM ApplicationFrameworkSettings");
00039 $this->Settings = $this->DB->FetchRow();
00040 if (!$this->Settings)
00041 {
00042 $this->DB->Query("INSERT INTO ApplicationFrameworkSettings"
00043 ." (LastTaskRunAt) VALUES (NOW())");
00044 $this->DB->Query("SELECT * FROM ApplicationFrameworkSettings");
00045 $this->Settings = $this->DB->FetchRow();
00046 }
00047
00048 # set PHP maximum execution time
00049 $this->MaxExecutionTime($this->Settings["MaxExecTime"]);
00050
00051 # register events we handle internally
00052 $this->RegisterEvent($this->PeriodicEvents);
00053 $this->RegisterEvent($this->UIEvents);
00054 }
00062 function AddObjectDirectories($Dirs)
00063 {
00064 foreach ($Dirs as $Location => $Prefix)
00065 {
00066 $Location = $Location
00067 .((substr($Location, -1) != "/") ? "/" : "");
00068 self::$ObjectDirectories = array_merge(
00069 array($Location => $Prefix),
00070 self::$ObjectDirectories);
00071 }
00072 }
00073
00078 function LoadPage($PageName)
00079 {
00080 # buffer any output from includes or PHP file
00081 ob_start();
00082
00083 # include any files needed to set up execution environment
00084 foreach ($this->EnvIncludes as $IncludeFile)
00085 {
00086 include($IncludeFile);
00087 }
00088
00089 # signal page load
00090 $this->SignalEvent("EVENT_PAGE_LOAD", array("PageName" => $PageName));
00091
00092 # signal PHP file load
00093 $SignalResult = $this->SignalEvent("EVENT_PHP_FILE_LOAD", array(
00094 "PageName" => $PageName));
00095
00096 # if signal handler returned new page name value
00097 $NewPageName = $PageName;
00098 if (($SignalResult["PageName"] != $PageName)
00099 && strlen($SignalResult["PageName"]))
00100 {
00101 # if new page name value is page file
00102 if (file_exists($SignalResult["PageName"]))
00103 {
00104 # use new value for PHP file name
00105 $PageFile = $SignalResult["PageName"];
00106 }
00107 else
00108 {
00109 # use new value for page name
00110 $NewPageName = $SignalResult["PageName"];
00111 }
00112 }
00113
00114 # if we do not already have a PHP file
00115 if (!isset($PageFile))
00116 {
00117 # look for PHP file for page
00118 $OurPageFile = "pages/".$NewPageName.".php";
00119 $LocalPageFile = "local/pages/".$NewPageName.".php";
00120 $PageFile = file_exists($LocalPageFile) ? $LocalPageFile
00121 : (file_exists($OurPageFile) ? $OurPageFile
00122 : "pages/".$this->DefaultPage.".php");
00123 }
00124
00125 # load PHP file
00126 include($PageFile);
00127
00128 # save buffered output to be displayed later after HTML file loads
00129 $PageOutput = ob_get_contents();
00130 ob_end_clean();
00131
00132 # set up for possible TSR (Terminate and Stay Resident :))
00133 $ShouldTSR = $this->PrepForTSR();
00134
00135 # if PHP file indicated we should autorefresh to somewhere else
00136 if ($this->JumpToPage)
00137 {
00138 if (!strlen(trim($PageOutput)))
00139 {
00140 ?><html>
00141 <head>
00142 <meta http-equiv="refresh" content="0; URL=<?PHP
00143 print($this->JumpToPage); ?>">
00144 </head>
00145 <body bgcolor="white">
00146 </body>
00147 </html><?PHP
00148 }
00149 }
00150 # else if HTML loading is not suppressed
00151 elseif (!$this->SuppressHTML)
00152 {
00153 # set content-type to get rid of diacritic errors
00154 header("Content-Type: text/html; charset="
00155 .$this->HtmlCharset, TRUE);
00156
00157 # load common HTML file if available
00158 $HtmlFile = $this->FindCommonTemplate("Common");
00159 if ($HtmlFile) { include($HtmlFile); }
00160
00161 # begin buffering content
00162 ob_start();
00163
00164 # signal HTML file load
00165 $SignalResult = $this->SignalEvent("EVENT_HTML_FILE_LOAD", array(
00166 "PageName" => $PageName));
00167
00168 # if signal handler returned new page name value
00169 $NewPageName = $PageName;
00170 $HtmlFile = NULL;
00171 if (($SignalResult["PageName"] != $PageName)
00172 && strlen($SignalResult["PageName"]))
00173 {
00174 # if new page name value is HTML file
00175 if (file_exists($SignalResult["PageName"]))
00176 {
00177 # use new value for HTML file name
00178 $HtmlFile = $SignalResult["PageName"];
00179 }
00180 else
00181 {
00182 # use new value for page name
00183 $NewPageName = $SignalResult["PageName"];
00184 }
00185 }
00186
00187 # load page content HTML file if available
00188 if ($HtmlFile === NULL)
00189 {
00190 $HtmlFile = $this->FindTemplate($this->ContentTemplateList, $NewPageName);
00191 }
00192 if ($HtmlFile)
00193 {
00194 include($HtmlFile);
00195 }
00196 else
00197 {
00198 print("<h2>ERROR: No HTML/TPL template found"
00199 ." for this page.</h2>");
00200 }
00201
00202 # signal HTML file load complete
00203 $SignalResult = $this->SignalEvent("EVENT_HTML_FILE_LOAD_COMPLETE");
00204
00205 # stop buffering and save content
00206 $BufferedContent = ob_get_contents();
00207 ob_end_clean();
00208
00209 # load page start HTML file if available
00210 $HtmlFile = $this->FindCommonTemplate("Start");
00211 if ($HtmlFile) { include($HtmlFile); }
00212
00213 # write out page content
00214 print($BufferedContent);
00215
00216 # load page end HTML file if available
00217 $HtmlFile = $this->FindCommonTemplate("End");
00218 if ($HtmlFile) { include($HtmlFile); }
00219 }
00220
00221 # run any post-processing routines
00222 foreach ($this->PostProcessingFuncs as $Func)
00223 {
00224 call_user_func_array($Func["FunctionName"], $Func["Arguments"]);
00225 }
00226
00227 # write out any output buffered from page code execution
00228 if (strlen($PageOutput))
00229 {
00230 if (!$this->SuppressHTML)
00231 {
00232 ?><table width="100%" cellpadding="5"
00233 style="border: 2px solid #666666; background: #CCCCCC;
00234 font-family: Courier New, Courier, monospace;
00235 margin-top: 10px;"><tr><td><?PHP
00236 }
00237 if ($this->JumpToPage)
00238 {
00239 ?><div style="color: #666666;"><span style="font-size: 150%;">
00240 <b>Page Jump Aborted</b></span>
00241 (because of error or other unexpected output)<br />
00242 <b>Jump Target:</b>
00243 <i><?PHP print($this->JumpToPage); ?></i></div><?PHP
00244 }
00245 print($PageOutput);
00246 if (!$this->SuppressHTML)
00247 {
00248 ?></td></tr></table><?PHP
00249 }
00250 }
00251
00252 # terminate and stay resident (TSR!) if indicated and HTML has been output
00253 # (only TSR if HTML has been output because otherwise browsers will misbehave)
00254 if ($ShouldTSR) { $this->LaunchTSR(); }
00255 }
00256
00263 function SetJumpToPage($Page)
00264 {
00265 if ((strpos($Page, "?") === FALSE)
00266 && ((strpos($Page, "=") !== FALSE)
00267 || ((strpos($Page, ".php") === FALSE)
00268 && (strpos($Page, ".htm") === FALSE)
00269 && (strpos($Page, "/") === FALSE))))
00270 {
00271 $this->JumpToPage = "index.php?P=".$Page;
00272 }
00273 else
00274 {
00275 $this->JumpToPage = $Page;
00276 }
00277 }
00278
00283 function JumpToPageIsSet()
00284 {
00285 return ($this->JumpToPage === NULL) ? FALSE : TRUE;
00286 }
00287
00297 function HtmlCharset($NewSetting = NULL)
00298 {
00299 if ($NewSetting !== NULL) { $this->HtmlCharset = $NewSetting; }
00300 return $this->HtmlCharset;
00301 }
00302
00309 function SuppressHTMLOutput($NewSetting = TRUE)
00310 {
00311 $this->SuppressHTML = $NewSetting;
00312 }
00313
00320 function ActiveUserInterface($UIName = NULL)
00321 {
00322 if ($UIName !== NULL)
00323 {
00324 $this->ActiveUI = preg_replace("/^SPTUI--/", "", $UIName);
00325 }
00326 return $this->ActiveUI;
00327 }
00328
00344 function AddPostProcessingCall($FunctionName,
00345 &$Arg1 = self::NOVALUE, &$Arg2 = self::NOVALUE, &$Arg3 = self::NOVALUE,
00346 &$Arg4 = self::NOVALUE, &$Arg5 = self::NOVALUE, &$Arg6 = self::NOVALUE,
00347 &$Arg7 = self::NOVALUE, &$Arg8 = self::NOVALUE, &$Arg9 = self::NOVALUE)
00348 {
00349 $FuncIndex = count($this->PostProcessingFuncs);
00350 $this->PostProcessingFuncs[$FuncIndex]["FunctionName"] = $FunctionName;
00351 $this->PostProcessingFuncs[$FuncIndex]["Arguments"] = array();
00352 $Index = 1;
00353 while (isset(${"Arg".$Index}) && (${"Arg".$Index} !== self::NOVALUE))
00354 {
00355 $this->PostProcessingFuncs[$FuncIndex]["Arguments"][$Index]
00356 =& ${"Arg".$Index};
00357 $Index++;
00358 }
00359 }
00360
00366 function AddEnvInclude($FileName)
00367 {
00368 $this->EnvIncludes[] = $FileName;
00369 }
00370
00376 function GUIFile($FileName)
00377 {
00378 # pull off file name suffix
00379 $NamePieces = explode(".", $FileName);
00380 $Suffix = strtolower(array_pop($NamePieces));
00381
00382 # determine which location to search based on file suffix
00383 $ImageSuffixes = array("gif", "jpg", "png");
00384 $FileList = in_array($Suffix, $ImageSuffixes)
00385 ? $this->ImageFileList : $this->CommonTemplateList;
00386
00387 # search for file and return result to caller
00388 return $this->FindTemplate($FileList, $FileName);
00389 }
00390
00400 function PUIFile($FileName)
00401 {
00402 $FullFileName = $this->GUIFile($FileName);
00403 if ($FullFileName) { print($FullFileName); }
00404 }
00405
00410 function FindCommonTemplate($PageName)
00411 {
00412 return $this->FindTemplate(
00413 array_merge($this->CommonTemplateList, $this->ContentTemplateList),
00414 $PageName);;
00415 }
00416
00425 function LoadFunction($Callback)
00426 {
00427 if (!is_callable($Callback) && is_string($Callback))
00428 {
00429 $Locations = $this->FunctionFileList;
00430 foreach (self::$ObjectDirectories as $Location => $Prefix)
00431 {
00432 $Locations[] = $Location."%PAGENAME%.php";
00433 $Locations[] = $Location."%PAGENAME%.html";
00434 }
00435 $FunctionFileName = $this->FindTemplate($Locations, "F-".$Callback);
00436 if ($FunctionFileName)
00437 {
00438 include_once($FunctionFileName);
00439 }
00440 }
00441 return is_callable($Callback);
00442 }
00443
00448 function GetElapsedExecutionTime()
00449 {
00450 return microtime(TRUE) - $this->ExecutionStartTime;
00451 }
00452
00457 function GetSecondsBeforeTimeout()
00458 {
00459 return ini_get("max_execution_time") - $this->GetElapsedExecutionTime();
00460 }
00461
00462
00463
00464 # ---- Event Handling ----------------------------------------------------
00465
00467
00471 const EVENTTYPE_DEFAULT = 1;
00477 const EVENTTYPE_CHAIN = 2;
00483 const EVENTTYPE_FIRST = 3;
00491 const EVENTTYPE_NAMED = 4;
00492
00494 const ORDER_FIRST = 1;
00496 const ORDER_MIDDLE = 2;
00498 const ORDER_LAST = 3;
00499
00508 function RegisterEvent($EventsOrEventName, $EventType = NULL)
00509 {
00510 # convert parameters to array if not already in that form
00511 $Events = is_array($EventsOrEventName) ? $EventsOrEventName
00512 : array($EventsOrEventName => $Type);
00513
00514 # for each event
00515 foreach ($Events as $Name => $Type)
00516 {
00517 # store event information
00518 $this->RegisteredEvents[$Name]["Type"] = $Type;
00519 $this->RegisteredEvents[$Name]["Hooks"] = array();
00520 }
00521 }
00522
00536 function HookEvent($EventsOrEventName, $Callback = NULL, $Order = self::ORDER_MIDDLE)
00537 {
00538 # convert parameters to array if not already in that form
00539 $Events = is_array($EventsOrEventName) ? $EventsOrEventName
00540 : array($EventsOrEventName => $Callback);
00541
00542 # for each event
00543 $Success = TRUE;
00544 foreach ($Events as $EventName => $EventCallback)
00545 {
00546 # if callback is valid
00547 if (is_callable($EventCallback))
00548 {
00549 # if this is a periodic event we process internally
00550 if (isset($this->PeriodicEvents[$EventName]))
00551 {
00552 # process event now
00553 $this->ProcessPeriodicEvent($EventName, $EventCallback);
00554 }
00555 # if specified event has been registered
00556 elseif (isset($this->RegisteredEvents[$EventName]))
00557 {
00558 # add callback for event
00559 $this->RegisteredEvents[$EventName]["Hooks"][]
00560 = array("Callback" => $EventCallback, "Order" => $Order);
00561
00562 # sort callbacks by order
00563 if (count($this->RegisteredEvents[$EventName]["Hooks"]) > 1)
00564 {
00565 usort($this->RegisteredEvents[$EventName]["Hooks"],
00566 array("ApplicationFramework", "HookEvent_OrderCompare"));
00567 }
00568 }
00569 else
00570 {
00571 $Success = FALSE;
00572 }
00573 }
00574 else
00575 {
00576 $Success = FALSE;
00577 }
00578 }
00579
00580 # report to caller whether all callbacks were hooked
00581 return $Success;
00582 }
00583 private static function HookEvent_OrderCompare($A, $B)
00584 {
00585 if ($A["Order"] == $B["Order"]) { return 0; }
00586 return ($A["Order"] < $B["Order"]) ? -1 : 1;
00587 }
00588
00597 function SignalEvent($EventName, $Parameters = NULL)
00598 {
00599 $ReturnValue = NULL;
00600
00601 # if event has been registered
00602 if (isset($this->RegisteredEvents[$EventName]))
00603 {
00604 # set up default return value (if not NULL)
00605 switch ($this->RegisteredEvents[$EventName]["Type"])
00606 {
00607 case self::EVENTTYPE_CHAIN:
00608 $ReturnValue = $Parameters;
00609 break;
00610
00611 case self::EVENTTYPE_NAMED:
00612 $ReturnValue = array();
00613 break;
00614 }
00615
00616 # for each callback for this event
00617 foreach ($this->RegisteredEvents[$EventName]["Hooks"] as $Hook)
00618 {
00619 # invoke callback
00620 $Callback = $Hook["Callback"];
00621 $Result = ($Parameters !== NULL)
00622 ? call_user_func_array($Callback, $Parameters)
00623 : call_user_func($Callback);
00624
00625 # process return value based on event type
00626 switch ($this->RegisteredEvents[$EventName]["Type"])
00627 {
00628 case self::EVENTTYPE_CHAIN:
00629 $ReturnValue = $Result;
00630 $Parameters = $Result;
00631 break;
00632
00633 case self::EVENTTYPE_FIRST:
00634 if ($Result !== NULL)
00635 {
00636 $ReturnValue = $Result;
00637 break 2;
00638 }
00639 break;
00640
00641 case self::EVENTTYPE_NAMED:
00642 $CallbackName = is_array($Callback)
00643 ? (is_object($Callback[0])
00644 ? get_class($Callback[0])
00645 : $Callback[0])."::".$Callback[1]
00646 : $Callback;
00647 $ReturnValue[$CallbackName] = $Result;
00648 break;
00649
00650 default:
00651 break;
00652 }
00653 }
00654 }
00655
00656 # return value if any to caller
00657 return $ReturnValue;
00658 }
00659
00665 function IsStaticOnlyEvent($EventName)
00666 {
00667 return isset($this->PeriodicEvents[$EventName]) ? TRUE : FALSE;
00668 }
00669
00670
00671
00672 # ---- Task Management ---------------------------------------------------
00673
00675
00677 const PRIORITY_HIGH = 1;
00679 const PRIORITY_MEDIUM = 2;
00681 const PRIORITY_LOW = 3;
00683 const PRIORITY_BACKGROUND = 4;
00684
00697 function QueueTask($Callback, $Parameters = NULL,
00698 $Priority = self::PRIORITY_MEDIUM, $Description = "")
00699 {
00700 # pack task info and write to database
00701 if ($Parameters === NULL) { $Parameters = array(); }
00702 $this->DB->Query("INSERT INTO TaskQueue"
00703 ." (Callback, Parameters, Priority, Description)"
00704 ." VALUES ('".addslashes(serialize($Callback))."', '"
00705 .addslashes(serialize($Parameters))."', ".intval($Priority).", '"
00706 .addslashes($Description)."')");
00707 }
00708
00726 function QueueUniqueTask($Callback, $Parameters = NULL,
00727 $Priority = self::PRIORITY_MEDIUM, $Description = "")
00728 {
00729 if ($this->TaskIsInQueue($Callback, $Parameters))
00730 {
00731 $QueryResult = $this->DB->Query("SELECT TaskId,Priority FROM TaskQueue"
00732 ." WHERE Callback = '".addslashes(serialize($Callback))."'"
00733 .($Parameters ? " AND Parameters = '"
00734 .addslashes(serialize($Parameters))."'" : ""));
00735 if ($QueryResult !== FALSE)
00736 {
00737 $Record = $this->DB->FetchRow();
00738 if ($Record["Priority"] > $Priority)
00739 {
00740 $this->DB->Query("UPDATE TaskQueue"
00741 ." SET Priority = ".intval($Priority)
00742 ." WHERE TaskId = ".intval($Record["TaskId"]));
00743 }
00744 }
00745 return FALSE;
00746 }
00747 else
00748 {
00749 $this->QueueTask($Callback, $Parameters, $Priority, $Description);
00750 return TRUE;
00751 }
00752 }
00753
00763 function TaskIsInQueue($Callback, $Parameters = NULL)
00764 {
00765 $FoundCount = $this->DB->Query("SELECT COUNT(*) AS FoundCount FROM TaskQueue"
00766 ." WHERE Callback = '".addslashes(serialize($Callback))."'"
00767 .($Parameters ? " AND Parameters = '"
00768 .addslashes(serialize($Parameters))."'" : ""),
00769 "FoundCount")
00770 + $this->DB->Query("SELECT COUNT(*) AS FoundCount FROM RunningTasks"
00771 ." WHERE Callback = '".addslashes(serialize($Callback))."'"
00772 .($Parameters ? " AND Parameters = '"
00773 .addslashes(serialize($Parameters))."'" : ""),
00774 "FoundCount");
00775 return ($FoundCount ? TRUE : FALSE);
00776 }
00777
00783 function GetTaskQueueSize($Priority = NULL)
00784 {
00785 return $this->DB->Query("SELECT COUNT(*) AS QueueSize FROM TaskQueue"
00786 .($Priority ? " WHERE Priority = ".intval($Priority) : ""),
00787 "QueueSize");
00788 }
00789
00797 function GetQueuedTaskList($Count = 100, $Offset = 0)
00798 {
00799 return $this->GetTaskList("SELECT * FROM TaskQueue"
00800 ." ORDER BY Priority, TaskId ", $Count, $Offset);
00801 }
00802
00810 function GetRunningTaskList($Count = 100, $Offset = 0)
00811 {
00812 return $this->GetTaskList("SELECT * FROM RunningTasks"
00813 ." WHERE StartedAt >= '".date("Y-m-d H:i:s",
00814 (time() - ini_get("max_execution_time")))."'"
00815 ." ORDER BY StartedAt", $Count, $Offset);
00816 }
00817
00825 function GetOrphanedTaskList($Count = 100, $Offset = 0)
00826 {
00827 return $this->GetTaskList("SELECT * FROM RunningTasks"
00828 ." WHERE StartedAt < '".date("Y-m-d H:i:s",
00829 (time() - ini_get("max_execution_time")))."'"
00830 ." ORDER BY StartedAt", $Count, $Offset);
00831 }
00832
00837 function ReQueueOrphanedTask($TaskId)
00838 {
00839 $this->DB->Query("LOCK TABLES TaskQueue WRITE, RunningTasks WRITE");
00840 $this->DB->Query("INSERT INTO TaskQueue"
00841 ." (Callback,Parameters,Priority,Description) "
00842 ."SELECT Callback, Parameters, Priority, Description"
00843 ." FROM RunningTasks WHERE TaskId = ".intval($TaskId));
00844 $this->DB->Query("DELETE FROM RunningTasks WHERE TaskId = ".intval($TaskId));
00845 $this->DB->Query("UNLOCK TABLES");
00846 }
00847
00852 function DeleteOrphanedTask($TaskId)
00853 {
00854 $this->DB->Query("DELETE FROM RunningTasks WHERE TaskId = ".intval($TaskId));
00855 }
00856
00862 function MaxTasks($NewValue = NULL)
00863 {
00864 if (func_num_args() && ($NewValue >= 1))
00865 {
00866 $this->DB->Query("UPDATE ApplicationFrameworkSettings"
00867 ." SET MaxTasksRunning = '".intval($NewValue)."'");
00868 $this->Settings["MaxTasksRunning"] = intval($NewValue);
00869 }
00870 return $this->Settings["MaxTasksRunning"];
00871 }
00872
00880 function MaxExecutionTime($NewValue = NULL)
00881 {
00882 if (func_num_args())
00883 {
00884 if ($NewValue != $this->Settings["MaxExecTime"])
00885 {
00886 $this->Settings["MaxExecTime"] = max($NewValue, 5);
00887 $this->DB->Query("UPDATE ApplicationFrameworkSettings"
00888 ." SET MaxExecTime = '"
00889 .intval($this->Settings["MaxExecTime"])."'");
00890 }
00891 ini_set("max_execution_time", $this->Settings["MaxExecTime"]);
00892 set_time_limit($this->Settings["MaxExecTime"]);
00893 }
00894 return ini_get("max_execution_time");
00895 }
00896
00897
00898
00899 # ---- PRIVATE INTERFACE -------------------------------------------------
00900
00901 private $JumpToPage = NULL;
00902 private $SuppressHTML = FALSE;
00903 private $DefaultPage = "Home";
00904 private $ActiveUI = "default";
00905 private $HtmlCharset = "UTF-8";
00906 private $PostProcessingFuncs = array();
00907 private $EnvIncludes = array();
00908 private $DB;
00909 private $Settings;
00910 private $ExecutionStartTime;
00911 private static $ObjectDirectories = array();
00912 private $MaxRunningTasksToTrack = 250;
00913 private $RunningTask;
00914
00915 private $PeriodicEvents = array(
00916 "EVENT_HOURLY" => self::EVENTTYPE_DEFAULT,
00917 "EVENT_DAILY" => self::EVENTTYPE_DEFAULT,
00918 "EVENT_WEEKLY" => self::EVENTTYPE_DEFAULT,
00919 "EVENT_MONTHLY" => self::EVENTTYPE_DEFAULT,
00920 "EVENT_PERIODIC" => self::EVENTTYPE_NAMED,
00921 );
00922 private $UIEvents = array(
00923 "EVENT_PAGE_LOAD" => self::EVENTTYPE_DEFAULT,
00924 "EVENT_PHP_FILE_LOAD" => self::EVENTTYPE_CHAIN,
00925 "EVENT_HTML_FILE_LOAD" => self::EVENTTYPE_CHAIN,
00926 "EVENT_HTML_FILE_LOAD_COMPLETE" => self::EVENTTYPE_DEFAULT,
00927 );
00928
00929 private function FindTemplate($FileList, $PageName)
00930 {
00931 $FileNameFound = NULL;
00932 foreach ($FileList as $FileName)
00933 {
00934 $FileName = str_replace("%ACTIVEUI%", $this->ActiveUI, $FileName);
00935 $FileName = str_replace("%PAGENAME%", $PageName, $FileName);
00936 if (file_exists($FileName))
00937 {
00938 $FileNameFound = $FileName;
00939 break;
00940 }
00941 }
00942 return $FileNameFound;
00943 }
00944
00945 private function SetUpObjectAutoloading()
00946 {
00947 function __autoload($ClassName)
00948 {
00949 ApplicationFramework::AutoloadObjects($ClassName);
00950 }
00951 }
00952
00954 static function AutoloadObjects($ClassName)
00955 {
00956 foreach (self::$ObjectDirectories as $Location => $Prefix)
00957 {
00958 $FileName = $Location.$Prefix.$ClassName.".php";
00959 if (file_exists($FileName))
00960 {
00961 require_once($FileName);
00962 break;
00963 }
00964 }
00965 }
00968 private function ProcessPeriodicEvent($EventName, $Callback)
00969 {
00970 # retrieve last execution time for event if available
00971 $Signature = self::GetCallbackSignature($Callback);
00972 $LastRun = $this->DB->Query("SELECT LastRunAt FROM PeriodicEvents"
00973 ." WHERE Signature = '".addslashes($Signature)."'", "LastRunAt");
00974
00975 # determine whether enough time has passed for event to execute
00976 $EventPeriods = array(
00977 "EVENT_HOURLY" => 60*60,
00978 "EVENT_DAILY" => 60*60*24,
00979 "EVENT_WEEKLY" => 60*60*24*7,
00980 "EVENT_MONTHLY" => 60*60*24*30,
00981 "EVENT_PERIODIC" => 0,
00982 );
00983 $ShouldExecute = (($LastRun === NULL)
00984 || (time() > (strtotime($LastRun) + $EventPeriods[$EventName])))
00985 ? TRUE : FALSE;
00986
00987 # if event should run
00988 if ($ShouldExecute)
00989 {
00990 # add event to task queue
00991 $WrapperCallback = array("ApplicationFramework", "PeriodicEventWrapper");
00992 $WrapperParameters = array(
00993 $EventName, $Callback, array("LastRunAt" => $LastRun));
00994 $this->QueueUniqueTask($WrapperCallback, $WrapperParameters);
00995 }
00996 }
00997
00998 private static function PeriodicEventWrapper($EventName, $Callback, $Parameters)
00999 {
01000 static $DB;
01001 if (!isset($DB)) { $DB = new Database(); }
01002
01003 # run event
01004 $ReturnVal = call_user_func_array($Callback, $Parameters);
01005
01006 # if event is already in database
01007 $Signature = self::GetCallbackSignature($Callback);
01008 if ($DB->Query("SELECT COUNT(*) AS EventCount FROM PeriodicEvents"
01009 ." WHERE Signature = '".addslashes($Signature)."'", "EventCount"))
01010 {
01011 # update last run time for event
01012 $DB->Query("UPDATE PeriodicEvents SET LastRunAt = "
01013 .(($EventName == "EVENT_PERIODIC")
01014 ? "'".date("Y-m-d H:i:s", time() + ($ReturnVal * 60))."'"
01015 : "NOW()")
01016 ." WHERE Signature = '".addslashes($Signature)."'");
01017 }
01018 else
01019 {
01020 # add last run time for event to database
01021 $DB->Query("INSERT INTO PeriodicEvents (Signature, LastRunAt) VALUES "
01022 ."('".addslashes($Signature)."', "
01023 .(($EventName == "EVENT_PERIODIC")
01024 ? "'".date("Y-m-d H:i:s", time() + ($ReturnVal * 60))."'"
01025 : "NOW()").")");
01026 }
01027 }
01028
01029 private static function GetCallbackSignature($Callback)
01030 {
01031 return !is_array($Callback) ? $Callback
01032 : (is_object($Callback[0]) ? md5(serialize($Callback[0])) : $Callback[0])
01033 ."::".$Callback[1];
01034 }
01035
01036 private function PrepForTSR()
01037 {
01038 # if HTML has been output and it's time to launch another task
01039 # (only TSR if HTML has been output because otherwise browsers
01040 # may misbehave after connection is closed)
01041 if (($this->JumpToPage || !$this->SuppressHTML)
01042 && (time() > (strtotime($this->Settings["LastTaskRunAt"])
01043 + (ini_get("max_execution_time")
01044 / $this->Settings["MaxTasksRunning"]) + 5))
01045 && $this->GetTaskQueueSize())
01046 {
01047 # begin buffering output for TSR
01048 ob_start();
01049
01050 # let caller know it is time to launch another task
01051 return TRUE;
01052 }
01053 else
01054 {
01055 # let caller know it is not time to launch another task
01056 return FALSE;
01057 }
01058 }
01059
01060 private function LaunchTSR()
01061 {
01062 # set needed headers and
01063 ignore_user_abort(TRUE);
01064 header("Connection: close");
01065 header("Content-Length: ".ob_get_length());
01066
01067 # output buffered content
01068 ob_end_flush();
01069 flush();
01070
01071 # write out any outstanding data and end HTTP session
01072 session_write_close();
01073
01074 # if there is still a task in the queue
01075 if ($this->GetTaskQueueSize())
01076 {
01077 # turn on output buffering to (hopefully) record any crash output
01078 ob_start();
01079
01080 # lock tables and grab last task run time to double check
01081 $this->DB->Query("LOCK TABLES ApplicationFrameworkSettings WRITE");
01082 $this->DB->Query("SELECT * FROM ApplicationFrameworkSettings");
01083 $this->Settings = $this->DB->FetchRow();
01084
01085 # if still time to launch another task
01086 if (time() > (strtotime($this->Settings["LastTaskRunAt"])
01087 + (ini_get("max_execution_time")
01088 / $this->Settings["MaxTasksRunning"]) + 5))
01089 {
01090 # update the "last run" time and release tables
01091 $this->DB->Query("UPDATE ApplicationFrameworkSettings"
01092 ." SET LastTaskRunAt = '".date("Y-m-d H:i:s")."'");
01093 $this->DB->Query("UNLOCK TABLES");
01094
01095 # run tasks while there is a task in the queue and enough time left
01096 do
01097 {
01098 # run the next task
01099 $this->RunNextTask();
01100 }
01101 while ($this->GetTaskQueueSize()
01102 && ($this->GetSecondsBeforeTimeout() > 65));
01103 }
01104 else
01105 {
01106 # release tables
01107 $this->DB->Query("UNLOCK TABLES");
01108 }
01109 }
01110 }
01111
01119 private function GetTaskList($DBQuery, $Count, $Offset)
01120 {
01121 $this->DB->Query($DBQuery." LIMIT ".intval($Offset).",".intval($Count));
01122 $Tasks = array();
01123 while ($Row = $this->DB->FetchRow())
01124 {
01125 $Tasks[$Row["TaskId"]] = $Row;
01126 if ($Row["Callback"] ==
01127 serialize(array("ApplicationFramework", "PeriodicEventWrapper")))
01128 {
01129 $WrappedCallback = unserialize($Row["Parameters"]);
01130 $Tasks[$Row["TaskId"]]["Callback"] = $WrappedCallback[1];
01131 $Tasks[$Row["TaskId"]]["Parameters"] = NULL;
01132 }
01133 else
01134 {
01135 $Tasks[$Row["TaskId"]]["Callback"] = unserialize($Row["Callback"]);
01136 $Tasks[$Row["TaskId"]]["Parameters"] = unserialize($Row["Parameters"]);
01137 }
01138 }
01139 return $Tasks;
01140 }
01141
01145 private function RunNextTask()
01146 {
01147 # look for task at head of queue
01148 $this->DB->Query("SELECT * FROM TaskQueue ORDER BY Priority, TaskId LIMIT 1");
01149 $Task = $this->DB->FetchRow();
01150
01151 # if there was a task available
01152 if ($Task)
01153 {
01154 # move task from queue to running tasks list
01155 $this->DB->Query("INSERT INTO RunningTasks "
01156 ."(TaskId,Callback,Parameters,Priority,Description) "
01157 ."SELECT * FROM TaskQueue WHERE TaskId = "
01158 .intval($Task["TaskId"]));
01159 $this->DB->Query("DELETE FROM TaskQueue WHERE TaskId = "
01160 .intval($Task["TaskId"]));
01161
01162 # unpack stored task info
01163 $Callback = unserialize($Task["Callback"]);
01164 $Parameters = unserialize($Task["Parameters"]);
01165
01166 # attempt to load task callback if not already available
01167 $this->LoadFunction($Callback);
01168
01169 # run task
01170 $this->RunningTask = $Task;
01171 call_user_func_array($Callback, $Parameters);
01172 unset($this->RunningTask);
01173
01174 # remove task from running tasks list
01175 $this->DB->Query("DELETE FROM RunningTasks"
01176 ." WHERE TaskId = ".intval($Task["TaskId"]));
01177
01178 # prune running tasks list if necessary
01179 $RunningTaskCount = $this->DB->Query(
01180 "SELECT COUNT(*) AS TaskCount FROM RunningTasks", "TaskCount");
01181 if ($RunningTasksCount > $this->MaxRunningTasksToTrack)
01182 {
01183 $this->DB->Query("DELETE FROM RunningTasks ORDER BY StartedAt"
01184 ." LIMIT ".($RunningTasksCount - $this->MaxRunningTasksToTrack));
01185 }
01186 }
01187 }
01188
01195 function OnCrash()
01196 {
01197 if (isset($this->RunningTask))
01198 {
01199 if (function_exists("error_get_last"))
01200 {
01201 $CrashInfo["LastError"] = error_get_last();
01202 }
01203 if (ob_get_length() !== FALSE)
01204 {
01205 $CrashInfo["OutputBuffer"] = ob_get_contents();
01206 }
01207 if (isset($CrashInfo))
01208 {
01209 $DB = new Database();
01210 $DB->Query("UPDATE RunningTasks SET CrashInfo = '"
01211 .addslashes(serialize($CrashInfo))
01212 ."' WHERE TaskId = ".intval($this->RunningTask["TaskId"]));
01213 }
01214 }
01215
01216 print("\n");
01217 return;
01218
01219 if (ob_get_length() !== FALSE)
01220 {
01221 ?>
01222 <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>
01223 <div style="font-size: 200%;">CRASH OUTPUT</div><?PHP
01224 ob_end_flush();
01225 ?></td></tr></table><?PHP
01226 }
01227 }
01230 private $CommonTemplateList = array(
01231 "local/interface/%ACTIVEUI%/include/StdPage%PAGENAME%.tpl",
01232 "local/interface/%ACTIVEUI%/include/StdPage%PAGENAME%.html",
01233 "local/interface/%ACTIVEUI%/include/%PAGENAME%.tpl",
01234 "local/interface/%ACTIVEUI%/include/%PAGENAME%.html",
01235 "local/interface/%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.tpl",
01236 "local/interface/%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.html",
01237 "local/interface/%ACTIVEUI%/include/SPT--%PAGENAME%.tpl",
01238 "local/interface/%ACTIVEUI%/include/SPT--%PAGENAME%.html",
01239 "local/interface/%ACTIVEUI%/include/%PAGENAME%",
01240 "interface/%ACTIVEUI%/include/StdPage%PAGENAME%.tpl",
01241 "interface/%ACTIVEUI%/include/StdPage%PAGENAME%.html",
01242 "interface/%ACTIVEUI%/include/%PAGENAME%.tpl",
01243 "interface/%ACTIVEUI%/include/%PAGENAME%.html",
01244 "interface/%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.tpl",
01245 "interface/%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.html",
01246 "interface/%ACTIVEUI%/include/SPT--%PAGENAME%.tpl",
01247 "interface/%ACTIVEUI%/include/SPT--%PAGENAME%.html",
01248 "interface/%ACTIVEUI%/include/%PAGENAME%",
01249 "SPTUI--%ACTIVEUI%/include/StdPage%PAGENAME%.tpl",
01250 "SPTUI--%ACTIVEUI%/include/StdPage%PAGENAME%.html",
01251 "SPTUI--%ACTIVEUI%/include/%PAGENAME%.tpl",
01252 "SPTUI--%ACTIVEUI%/include/%PAGENAME%.html",
01253 "SPTUI--%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.tpl",
01254 "SPTUI--%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.html",
01255 "SPTUI--%ACTIVEUI%/include/SPT--%PAGENAME%.tpl",
01256 "SPTUI--%ACTIVEUI%/include/SPT--%PAGENAME%.html",
01257 "SPTUI--%ACTIVEUI%/include/%PAGENAME%",
01258 "%ACTIVEUI%/include/StdPage%PAGENAME%.tpl",
01259 "%ACTIVEUI%/include/StdPage%PAGENAME%.html",
01260 "%ACTIVEUI%/include/%PAGENAME%.tpl",
01261 "%ACTIVEUI%/include/%PAGENAME%.html",
01262 "%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.tpl",
01263 "%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.html",
01264 "%ACTIVEUI%/include/SPT--%PAGENAME%.tpl",
01265 "%ACTIVEUI%/include/SPT--%PAGENAME%.html",
01266 "%ACTIVEUI%/include/%PAGENAME%",
01267 "local/interface/default/include/StdPage%PAGENAME%.tpl",
01268 "local/interface/default/include/StdPage%PAGENAME%.html",
01269 "local/interface/default/include/%PAGENAME%.tpl",
01270 "local/interface/default/include/%PAGENAME%.html",
01271 "local/interface/default/include/SPT--StandardPage%PAGENAME%.tpl",
01272 "local/interface/default/include/SPT--StandardPage%PAGENAME%.html",
01273 "local/interface/default/include/SPT--%PAGENAME%.tpl",
01274 "local/interface/default/include/SPT--%PAGENAME%.html",
01275 "local/interface/default/include/%PAGENAME%",
01276 "interface/default/include/StdPage%PAGENAME%.tpl",
01277 "interface/default/include/StdPage%PAGENAME%.html",
01278 "interface/default/include/%PAGENAME%.tpl",
01279 "interface/default/include/%PAGENAME%.html",
01280 "interface/default/include/SPT--StandardPage%PAGENAME%.tpl",
01281 "interface/default/include/SPT--StandardPage%PAGENAME%.html",
01282 "interface/default/include/SPT--%PAGENAME%.tpl",
01283 "interface/default/include/SPT--%PAGENAME%.html",
01284 "interface/default/include/%PAGENAME%",
01285 );
01286 private $ContentTemplateList = array(
01287 "local/interface/%ACTIVEUI%/%PAGENAME%.tpl",
01288 "local/interface/%ACTIVEUI%/%PAGENAME%.html",
01289 "local/interface/%ACTIVEUI%/SPT--%PAGENAME%.tpl",
01290 "local/interface/%ACTIVEUI%/SPT--%PAGENAME%.html",
01291 "interface/%ACTIVEUI%/%PAGENAME%.tpl",
01292 "interface/%ACTIVEUI%/%PAGENAME%.html",
01293 "interface/%ACTIVEUI%/SPT--%PAGENAME%.tpl",
01294 "interface/%ACTIVEUI%/SPT--%PAGENAME%.html",
01295 "SPTUI--%ACTIVEUI%/%PAGENAME%.tpl",
01296 "SPTUI--%ACTIVEUI%/%PAGENAME%.html",
01297 "SPTUI--%ACTIVEUI%/SPT--%PAGENAME%.tpl",
01298 "SPTUI--%ACTIVEUI%/SPT--%PAGENAME%.html",
01299 "%ACTIVEUI%/%PAGENAME%.tpl",
01300 "%ACTIVEUI%/%PAGENAME%.html",
01301 "%ACTIVEUI%/SPT--%PAGENAME%.tpl",
01302 "%ACTIVEUI%/SPT--%PAGENAME%.html",
01303 "local/interface/default/%PAGENAME%.tpl",
01304 "local/interface/default/%PAGENAME%.html",
01305 "local/interface/default/SPT--%PAGENAME%.tpl",
01306 "local/interface/default/SPT--%PAGENAME%.html",
01307 "interface/default/%PAGENAME%.tpl",
01308 "interface/default/%PAGENAME%.html",
01309 "interface/default/SPT--%PAGENAME%.tpl",
01310 "interface/default/SPT--%PAGENAME%.html",
01311 );
01312 private $ImageFileList = array(
01313 "local/interface/%ACTIVEUI%/images/%PAGENAME%",
01314 "interface/%ACTIVEUI%/images/%PAGENAME%",
01315 "SPTUI--%ACTIVEUI%/images/%PAGENAME%",
01316 "%ACTIVEUI%/images/%PAGENAME%",
01317 "local/interface/default/images/%PAGENAME%",
01318 "interface/default/images/%PAGENAME%",
01319 );
01320 private $FunctionFileList = array(
01321 "local/interface/%ACTIVEUI%/include/%PAGENAME%.php",
01322 "local/interface/%ACTIVEUI%/include/%PAGENAME%.html",
01323 "interface/%ACTIVEUI%/include/%PAGENAME%.php",
01324 "interface/%ACTIVEUI%/include/%PAGENAME%.html",
01325 "SPTUI--%ACTIVEUI%/include/%PAGENAME%.php",
01326 "SPTUI--%ACTIVEUI%/include/%PAGENAME%.html",
01327 "%ACTIVEUI%/include/%PAGENAME%.php",
01328 "%ACTIVEUI%/include/%PAGENAME%.html",
01329 "local/interface/default/include/%PAGENAME%.php",
01330 "local/interface/default/include/%PAGENAME%.html",
01331 "local/include/%PAGENAME%.php",
01332 "local/include/%PAGENAME%.html",
01333 "interface/default/include/%PAGENAME%.php",
01334 "interface/default/include/%PAGENAME%.html",
01335 "include/%PAGENAME%.php",
01336 "include/%PAGENAME%.html",
01337 );
01338
01340 const NOVALUE = ".-+-.NO VALUE PASSED IN FOR ARGUMENT.-+-.";
01342 };
01343
01344
01345 ?>