CWIS Developer Documentation
ApplicationFramework.php
Go to the documentation of this file.
1 <?PHP
2 #
3 # FILE: ApplicationFramework.php
4 #
5 # Part of the ScoutLib application support library
6 # Copyright 2009-2014 Edward Almasy and Internet Scout Research Group
7 # http://scout.wisc.edu
8 #
9 
15 
16  # ---- PUBLIC INTERFACE --------------------------------------------------
17  /*@(*/
19 
24  function __construct()
25  {
26  # save execution start time
27  $this->ExecutionStartTime = microtime(TRUE);
28 
29  # begin/restore PHP session
30  $SessionDomain = isset($_SERVER["SERVER_NAME"]) ? $_SERVER["SERVER_NAME"]
31  : isset($_SERVER["HTTP_HOST"]) ? $_SERVER["HTTP_HOST"]
32  : php_uname("n");
33  if (is_writable(session_save_path()))
34  {
35  $SessionStorage = session_save_path()
36  ."/".self::$AppName."_".md5($SessionDomain.dirname(__FILE__));
37  if (!is_dir($SessionStorage)) { mkdir($SessionStorage, 0700 ); }
38  if (is_writable($SessionStorage)) { session_save_path($SessionStorage); }
39  }
40  ini_set("session.gc_maxlifetime", self::$SessionLifetime);
41  session_set_cookie_params(
42  self::$SessionLifetime, "/", $SessionDomain);
43  session_start();
44 
45  # set up object file autoloader
46  $this->SetUpObjectAutoloading();
47 
48  # set up function to output any buffered text in case of crash
49  register_shutdown_function(array($this, "OnCrash"));
50 
51  # set up our internal environment
52  $this->DB = new Database();
53 
54  # set up our exception handler
55  set_exception_handler(array($this, "GlobalExceptionHandler"));
56 
57  # perform any work needed to undo PHP magic quotes
58  $this->UndoMagicQuotes();
59 
60  # load our settings from database
61  $this->LoadSettings();
62 
63  # set PHP maximum execution time
64  $this->MaxExecutionTime($this->Settings["MaxExecTime"]);
65 
66  # register events we handle internally
67  $this->RegisterEvent($this->PeriodicEvents);
68  $this->RegisterEvent($this->UIEvents);
69 
70  # attempt to create SCSS cache directory if needed and it does not exist
71  if ($this->UseScss && !is_dir(self::$ScssCacheDir))
72  { mkdir(self::$ScssCacheDir, 0777, TRUE); }
73  }
80  function __destruct()
81  {
82  # if template location cache is flagged to be saved
83  if ($this->SaveTemplateLocationCache)
84  {
85  # write template location cache out and update cache expiration
86  $this->DB->Query("UPDATE ApplicationFrameworkSettings"
87  ." SET TemplateLocationCache = '"
88  .addslashes(serialize(
89  $this->Settings["TemplateLocationCache"]))."',"
90  ." TemplateLocationCacheExpiration = "
91  ." NOW() + INTERVAL "
92  .$this->Settings["TemplateLocationCacheInterval"]
93  ." MINUTE");
94  }
95 
96  # if object location cache is flagged to be saved
97  if (self::$SaveObjectLocationCache)
98  {
99  # write object location cache out and update cache expiration
100  $this->DB->Query("UPDATE ApplicationFrameworkSettings"
101  ." SET ObjectLocationCache = '"
102  .addslashes(serialize(
103  self::$ObjectLocationCache))."',"
104  ." ObjectLocationCacheExpiration = "
105  ." NOW() + INTERVAL "
106  .self::$ObjectLocationCacheInterval
107  ." MINUTE");
108  }
109  }
116  function GlobalExceptionHandler($Exception)
117  {
118  # display exception info
119  $Location = $Exception->getFile()."[".$Exception->getLine()."]";
120  ?><table width="100%" cellpadding="5"
121  style="border: 2px solid #666666; background: #CCCCCC;
122  font-family: Courier New, Courier, monospace;
123  margin-top: 10px;"><tr><td>
124  <div style="color: #666666;">
125  <span style="font-size: 150%;">
126  <b>Uncaught Exception</b></span><br />
127  <b>Message:</b> <i><?PHP print $Exception->getMessage(); ?></i><br />
128  <b>Location:</b> <i><?PHP print $Location; ?></i><br />
129  <b>Trace:</b>
130  <blockquote><pre><?PHP print $Exception->getTraceAsString();
131  ?></pre></blockquote>
132  </div>
133  </td></tr></table><?PHP
134 
135  # log exception if possible
136  $LogMsg = "Uncaught exception (".$Exception->getMessage().").";
137  $this->LogError(self::LOGLVL_ERROR, $LogMsg);
138  }
152  static function AddObjectDirectory(
153  $Dir, $Prefix = "", $ClassPattern = NULL, $ClassReplacement = NULL)
154  {
155  # make sure directory has trailing slash
156  $Dir = $Dir.((substr($Dir, -1) != "/") ? "/" : "");
157 
158  # add directory to directory list
159  self::$ObjectDirectories = array_merge(
160  array($Dir => array(
161  "Prefix" => $Prefix,
162  "ClassPattern" => $ClassPattern,
163  "ClassReplacement" => $ClassReplacement,
164  )),
165  self::$ObjectDirectories);
166  }
167 
187  function AddImageDirectories($Dir, $SearchLast = FALSE, $SkipSlashCheck = FALSE)
188  {
189  # add directories to existing image directory list
190  $this->ImageDirList = $this->AddToDirList(
191  $this->ImageDirList, $Dir, $SearchLast, $SkipSlashCheck);
192  }
193 
214  function AddIncludeDirectories($Dir, $SearchLast = FALSE, $SkipSlashCheck = FALSE)
215  {
216  # add directories to existing image directory list
217  $this->IncludeDirList = $this->AddToDirList(
218  $this->IncludeDirList, $Dir, $SearchLast, $SkipSlashCheck);
219  }
220 
240  function AddInterfaceDirectories($Dir, $SearchLast = FALSE, $SkipSlashCheck = FALSE)
241  {
242  # add directories to existing image directory list
243  $this->InterfaceDirList = $this->AddToDirList(
244  $this->InterfaceDirList, $Dir, $SearchLast, $SkipSlashCheck);
245  }
246 
266  function AddFunctionDirectories($Dir, $SearchLast = FALSE, $SkipSlashCheck = FALSE)
267  {
268  # add directories to existing image directory list
269  $this->FunctionDirList = $this->AddToDirList(
270  $this->FunctionDirList, $Dir, $SearchLast, $SkipSlashCheck);
271  }
272 
278  function SetBrowserDetectionFunc($DetectionFunc)
279  {
280  $this->BrowserDetectFunc = $DetectionFunc;
281  }
282 
289  function AddUnbufferedCallback($Callback, $Parameters=array())
290  {
291  if (is_callable($Callback))
292  {
293  $this->UnbufferedCallbacks[] = array($Callback, $Parameters);
294  }
295  }
296 
304  {
305  return $this->UpdateSetting("TemplateLocationCacheInterval", $NewInterval);
306  }
307 
315  {
316  return $this->UpdateSetting("ObjectLocationCacheInterval", $NewInterval);
317  }
318 
332  $BacktraceOptions = 0, $BacktraceLimit = 0)
333  {
334  if (version_compare(PHP_VERSION, "5.4.0", ">="))
335  {
336  $this->SavedContext = debug_backtrace(
337  $BacktraceOptions, $BacktraceLimit);
338  }
339  else
340  {
341  $this->SavedContext = debug_backtrace($BacktraceOptions);
342  }
343  array_shift($this->SavedContext);
344  }
345 
350  function LoadPage($PageName)
351  {
352  # perform any clean URL rewriting
353  $PageName = $this->RewriteCleanUrls($PageName);
354 
355  # sanitize incoming page name and save local copy
356  $PageName = preg_replace("/[^a-zA-Z0-9_.-]/", "", $PageName);
357  $this->PageName = $PageName;
358 
359  # buffer any output from includes or PHP file
360  ob_start();
361 
362  # include any files needed to set up execution environment
363  foreach ($this->EnvIncludes as $IncludeFile)
364  {
365  include($IncludeFile);
366  }
367 
368  # signal page load
369  $this->SignalEvent("EVENT_PAGE_LOAD", array("PageName" => $PageName));
370 
371  # signal PHP file load
372  $SignalResult = $this->SignalEvent("EVENT_PHP_FILE_LOAD", array(
373  "PageName" => $PageName));
374 
375  # if signal handler returned new page name value
376  $NewPageName = $PageName;
377  if (($SignalResult["PageName"] != $PageName)
378  && strlen($SignalResult["PageName"]))
379  {
380  # if new page name value is page file
381  if (file_exists($SignalResult["PageName"]))
382  {
383  # use new value for PHP file name
384  $PageFile = $SignalResult["PageName"];
385  }
386  else
387  {
388  # use new value for page name
389  $NewPageName = $SignalResult["PageName"];
390  }
391 
392  # update local copy of page name
393  $this->PageName = $NewPageName;
394  }
395 
396  # if we do not already have a PHP file
397  if (!isset($PageFile))
398  {
399  # look for PHP file for page
400  $OurPageFile = "pages/".$NewPageName.".php";
401  $LocalPageFile = "local/pages/".$NewPageName.".php";
402  $PageFile = file_exists($LocalPageFile) ? $LocalPageFile
403  : (file_exists($OurPageFile) ? $OurPageFile
404  : "pages/".$this->DefaultPage.".php");
405  }
406 
407  # load PHP file
408  include($PageFile);
409 
410  # save buffered output to be displayed later after HTML file loads
411  $PageOutput = ob_get_contents();
412  ob_end_clean();
413 
414  # signal PHP file load is complete
415  ob_start();
416  $Context["Variables"] = get_defined_vars();
417  $this->SignalEvent("EVENT_PHP_FILE_LOAD_COMPLETE",
418  array("PageName" => $PageName, "Context" => $Context));
419  $PageCompleteOutput = ob_get_contents();
420  ob_end_clean();
421 
422  # set up for possible TSR (Terminate and Stay Resident :))
423  $ShouldTSR = $this->PrepForTSR();
424 
425  # if PHP file indicated we should autorefresh to somewhere else
426  if ($this->JumpToPage)
427  {
428  if (!strlen(trim($PageOutput)))
429  {
430  ?><html>
431  <head>
432  <meta http-equiv="refresh" content="0; URL=<?PHP
433  print($this->JumpToPage); ?>">
434  </head>
435  <body bgcolor="white">
436  </body>
437  </html><?PHP
438  }
439  }
440  # else if HTML loading is not suppressed
441  elseif (!$this->SuppressHTML)
442  {
443  # set content-type to get rid of diacritic errors
444  header("Content-Type: text/html; charset="
445  .$this->HtmlCharset, TRUE);
446 
447  # load common HTML file (defines common functions) if available
448  $CommonHtmlFile = $this->FindFile($this->IncludeDirList,
449  "Common", array("tpl", "html"));
450  if ($CommonHtmlFile) { include($CommonHtmlFile); }
451 
452  # load UI functions
453  $this->LoadUIFunctions();
454 
455  # begin buffering content
456  ob_start();
457 
458  # signal HTML file load
459  $SignalResult = $this->SignalEvent("EVENT_HTML_FILE_LOAD", array(
460  "PageName" => $PageName));
461 
462  # if signal handler returned new page name value
463  $NewPageName = $PageName;
464  $PageContentFile = NULL;
465  if (($SignalResult["PageName"] != $PageName)
466  && strlen($SignalResult["PageName"]))
467  {
468  # if new page name value is HTML file
469  if (file_exists($SignalResult["PageName"]))
470  {
471  # use new value for HTML file name
472  $PageContentFile = $SignalResult["PageName"];
473  }
474  else
475  {
476  # use new value for page name
477  $NewPageName = $SignalResult["PageName"];
478  }
479  }
480 
481  # load page content HTML file if available
482  if ($PageContentFile === NULL)
483  {
484  $PageContentFile = $this->FindFile(
485  $this->InterfaceDirList, $NewPageName,
486  array("tpl", "html"));
487  }
488  if ($PageContentFile)
489  {
490  include($PageContentFile);
491  }
492  else
493  {
494  print "<h2>ERROR: No HTML/TPL template found"
495  ." for this page.</h2>";
496  }
497 
498  # signal HTML file load complete
499  $SignalResult = $this->SignalEvent("EVENT_HTML_FILE_LOAD_COMPLETE");
500 
501  # stop buffering and save output
502  $PageContentOutput = ob_get_contents();
503  ob_end_clean();
504 
505  # load page start HTML file if available
506  ob_start();
507  $PageStartFile = $this->FindFile($this->IncludeDirList, "Start",
508  array("tpl", "html"), array("StdPage", "StandardPage"));
509  if ($PageStartFile) { include($PageStartFile); }
510  $PageStartOutput = ob_get_contents();
511  ob_end_clean();
512 
513  # load page end HTML file if available
514  ob_start();
515  $PageEndFile = $this->FindFile($this->IncludeDirList, "End",
516  array("tpl", "html"), array("StdPage", "StandardPage"));
517  if ($PageEndFile) { include($PageEndFile); }
518  $PageEndOutput = ob_get_contents();
519  ob_end_clean();
520 
521  # get list of any required files not loaded
522  $RequiredFiles = $this->GetRequiredFilesNotYetLoaded($PageContentFile);
523 
524  # if a browser detection function has been made available
525  if (is_callable($this->BrowserDetectFunc))
526  {
527  # call function to get browser list
528  $Browsers = call_user_func($this->BrowserDetectFunc);
529 
530  # for each required file
531  $NewRequiredFiles = array();
532  foreach ($RequiredFiles as $File)
533  {
534  # if file name includes browser keyword
535  if (preg_match("/%BROWSER%/", $File))
536  {
537  # for each browser
538  foreach ($Browsers as $Browser)
539  {
540  # substitute in browser name and add to new file list
541  $NewRequiredFiles[] = preg_replace(
542  "/%BROWSER%/", $Browser, $File);
543  }
544  }
545  else
546  {
547  # add to new file list
548  $NewRequiredFiles[] = $File;
549  }
550  }
551  $RequiredFiles = $NewRequiredFiles;
552  }
553  else
554  {
555  # filter out any files with browser keyword in their name
556  $NewRequiredFiles = array();
557  foreach ($RequiredFiles as $File)
558  {
559  if (!preg_match("/%BROWSER%/", $File))
560  {
561  $NewRequiredFiles[] = $File;
562  }
563  }
564  $RequiredFiles = $NewRequiredFiles;
565  }
566 
567  # for each required file
568  foreach ($RequiredFiles as $File)
569  {
570  # locate specific file to use
571  $FilePath = $this->GUIFile($File);
572 
573  # if file was found
574  if ($FilePath)
575  {
576  # determine file type
577  $NamePieces = explode(".", $File);
578  $FileSuffix = strtolower(array_pop($NamePieces));
579 
580  # add file to HTML output based on file type
581  $FilePath = htmlspecialchars($FilePath);
582  switch ($FileSuffix)
583  {
584  case "js":
585  $Tag = '<script type="text/javascript" src="'
586  .$FilePath.'"></script>';
587  $PageEndOutput = preg_replace(
588  "#</body>#i", $Tag."\n</body>", $PageEndOutput, 1);
589  break;
590 
591  case "css":
592  $Tag = '<link rel="stylesheet" type="text/css"'
593  .' media="all" href="'.$FilePath.'">';
594  $PageStartOutput = preg_replace(
595  "#</head>#i", $Tag."\n</head>", $PageStartOutput, 1);
596  break;
597  }
598  }
599  }
600 
601  # assemble full page
602  $FullPageOutput = $PageStartOutput.$PageContentOutput.$PageEndOutput;
603 
604  # perform any regular expression replacements in output
605  $FullPageOutput = preg_replace($this->OutputModificationPatterns,
606  $this->OutputModificationReplacements, $FullPageOutput);
607 
608  # perform any callback replacements in output
609  foreach ($this->OutputModificationCallbacks as $Info)
610  {
611  $this->OutputModificationCallbackInfo = $Info;
612  $FullPageOutput = preg_replace_callback($Info["SearchPattern"],
613  array($this, "OutputModificationCallbackShell"),
614  $FullPageOutput);
615  }
616 
617  # if relative paths may not work because we were invoked via clean URL
618  if ($this->CleanUrlRewritePerformed || self::WasUrlRewritten())
619  {
620  # if using the <base> tag is okay
621  $BaseUrl = $this->BaseUrl();
622  if ($this->UseBaseTag)
623  {
624  # add <base> tag to header
625  $PageStartOutput = preg_replace("%<head>%",
626  "<head><base href=\"".$BaseUrl."\" />",
627  $PageStartOutput);
628 
629  # re-assemble full page with new header
630  $FullPageOutput = $PageStartOutput.$PageContentOutput.$PageEndOutput;
631 
632  # the absolute URL to the current page
633  $FullUrl = $BaseUrl . $this->GetPageLocation();
634 
635  # make HREF attribute values with just a fragment ID
636  # absolute since they don't work with the <base> tag because
637  # they are relative to the current page/URL, not the site
638  # root
639  $FullPageOutput = preg_replace(
640  array("%href=\"(#[^:\" ]+)\"%i", "%href='(#[^:' ]+)'%i"),
641  array("href=\"".$FullUrl."$1\"", "href='".$FullUrl."$1'"),
642  $FullPageOutput);
643  }
644  else
645  {
646  # try to fix any relative paths throughout code
647  $FullPageOutput = preg_replace(array(
648  "%src=\"/?([^?*:;{}\\\\\" ]+)\.(js|css|gif|png|jpg)\"%i",
649  "%src='/?([^?*:;{}\\\\' ]+)\.(js|css|gif|png|jpg)'%i",
650  # don't rewrite HREF attributes that are just
651  # fragment IDs because they are relative to the
652  # current page/URL, not the site root
653  "%href=\"/?([^#][^:\" ]*)\"%i",
654  "%href='/?([^#][^:' ]*)'%i",
655  "%action=\"/?([^#][^:\" ]*)\"%i",
656  "%action='/?([^#][^:' ]*)'%i",
657  "%@import\s+url\(\"/?([^:\" ]+)\"\s*\)%i",
658  "%@import\s+url\('/?([^:\" ]+)'\s*\)%i",
659  "%@import\s+\"/?([^:\" ]+)\"\s*%i",
660  "%@import\s+'/?([^:\" ]+)'\s*%i",
661  ),
662  array(
663  "src=\"".$BaseUrl."$1.$2\"",
664  "src=\"".$BaseUrl."$1.$2\"",
665  "href=\"".$BaseUrl."$1\"",
666  "href=\"".$BaseUrl."$1\"",
667  "action=\"".$BaseUrl."$1\"",
668  "action=\"".$BaseUrl."$1\"",
669  "@import url(\"".$BaseUrl."$1\")",
670  "@import url('".$BaseUrl."$1')",
671  "@import \"".$BaseUrl."$1\"",
672  "@import '".$BaseUrl."$1'",
673  ),
674  $FullPageOutput);
675  }
676  }
677 
678  # provide the opportunity to modify full page output
679  $SignalResult = $this->SignalEvent("EVENT_PAGE_OUTPUT_FILTER", array(
680  "PageOutput" => $FullPageOutput));
681  if (isset($SignalResult["PageOutput"])
682  && strlen($SignalResult["PageOutput"]))
683  {
684  $FullPageOutput = $SignalResult["PageOutput"];
685  }
686 
687  # write out full page
688  print $FullPageOutput;
689  }
690 
691  # run any post-processing routines
692  foreach ($this->PostProcessingFuncs as $Func)
693  {
694  call_user_func_array($Func["FunctionName"], $Func["Arguments"]);
695  }
696 
697  # write out any output buffered from page code execution
698  if (strlen($PageOutput))
699  {
700  if (!$this->SuppressHTML)
701  {
702  ?><table width="100%" cellpadding="5"
703  style="border: 2px solid #666666; background: #CCCCCC;
704  font-family: Courier New, Courier, monospace;
705  margin-top: 10px;"><tr><td><?PHP
706  }
707  if ($this->JumpToPage)
708  {
709  ?><div style="color: #666666;"><span style="font-size: 150%;">
710  <b>Page Jump Aborted</b></span>
711  (because of error or other unexpected output)<br />
712  <b>Jump Target:</b>
713  <i><?PHP print($this->JumpToPage); ?></i></div><?PHP
714  }
715  print $PageOutput;
716  if (!$this->SuppressHTML)
717  {
718  ?></td></tr></table><?PHP
719  }
720  }
721 
722  # write out any output buffered from the page code execution complete signal
723  if (!$this->JumpToPage && !$this->SuppressHTML && strlen($PageCompleteOutput))
724  {
725  print $PageCompleteOutput;
726  }
727 
728  # execute callbacks that should not have their output buffered
729  foreach ($this->UnbufferedCallbacks as $Callback)
730  {
731  call_user_func_array($Callback[0], $Callback[1]);
732  }
733 
734  # log high memory usage
735  if (function_exists("memory_get_peak_usage"))
736  {
737  $MemoryThreshold = ($this->HighMemoryUsageThreshold()
738  * $this->GetPhpMemoryLimit()) / 100;
739  if ($this->LogHighMemoryUsage()
740  && (memory_get_peak_usage() >= $MemoryThreshold))
741  {
742  $HighMemUsageMsg = "High peak memory usage ("
743  .intval(memory_get_peak_usage()).") for "
744  .$this->FullUrl()." from "
745  .$_SERVER["REMOTE_ADDR"];
746  $this->LogMessage(self::LOGLVL_INFO, $HighMemUsageMsg);
747  }
748  }
749 
750  # log slow page loads
751  if ($this->LogSlowPageLoads()
752  && ($this->GetElapsedExecutionTime()
753  >= ($this->SlowPageLoadThreshold())))
754  {
755  $SlowPageLoadMsg = "Slow page load ("
756  .intval($this->GetElapsedExecutionTime())."s) for "
757  .$this->FullUrl()." from "
758  .$_SERVER["REMOTE_ADDR"];
759  $this->LogMessage(self::LOGLVL_INFO, $SlowPageLoadMsg);
760  }
761 
762  # terminate and stay resident (TSR!) if indicated and HTML has been output
763  # (only TSR if HTML has been output because otherwise browsers will misbehave)
764  if ($ShouldTSR) { $this->LaunchTSR(); }
765  }
766 
772  function GetPageName()
773  {
774  return $this->PageName;
775  }
776 
782  function GetPageLocation()
783  {
784  # retrieve current URL
785  $Url = $this->GetScriptUrl();
786 
787  # remove the base path if present
788  $BasePath = $this->Settings["BasePath"];
789  if (stripos($Url, $BasePath) === 0)
790  {
791  $Url = substr($Url, strlen($BasePath));
792  }
793 
794  return $Url;
795  }
796 
802  function GetPageUrl()
803  {
804  return self::BaseUrl() . $this->GetPageLocation();
805  }
806 
815  function SetJumpToPage($Page, $IsLiteral = FALSE)
816  {
817  if (!is_null($Page)
818  && (!$IsLiteral)
819  && (strpos($Page, "?") === FALSE)
820  && ((strpos($Page, "=") !== FALSE)
821  || ((stripos($Page, ".php") === FALSE)
822  && (stripos($Page, ".htm") === FALSE)
823  && (strpos($Page, "/") === FALSE)))
824  && (stripos($Page, "http://") !== 0)
825  && (stripos($Page, "https://") !== 0))
826  {
827  $this->JumpToPage = self::BaseUrl() . "index.php?P=".$Page;
828  }
829  else
830  {
831  $this->JumpToPage = $Page;
832  }
833  }
834 
839  function JumpToPageIsSet()
840  {
841  return ($this->JumpToPage === NULL) ? FALSE : TRUE;
842  }
843 
853  function HtmlCharset($NewSetting = NULL)
854  {
855  if ($NewSetting !== NULL) { $this->HtmlCharset = $NewSetting; }
856  return $this->HtmlCharset;
857  }
858 
865  public function UseMinimizedJavascript($NewSetting = NULL)
866  {
867  if ($NewSetting !== NULL) { $this->UseMinimizedJavascript = $NewSetting; }
868  return $this->UseMinimizedJavascript;
869  }
870 
881  function UseBaseTag($NewValue = NULL)
882  {
883  if ($NewValue !== NULL) { $this->UseBaseTag = $NewValue ? TRUE : FALSE; }
884  return $this->UseBaseTag;
885  }
886 
893  function SuppressHTMLOutput($NewSetting = TRUE)
894  {
895  $this->SuppressHTML = $NewSetting;
896  }
897 
904  function ActiveUserInterface($UIName = NULL)
905  {
906  if ($UIName !== NULL)
907  {
908  $this->ActiveUI = preg_replace("/^SPTUI--/", "", $UIName);
909  }
910  return $this->ActiveUI;
911  }
912 
918  function GetUserInterfaces()
919  {
920  # possible UI directories
921  $InterfaceDirs = array(
922  "interface",
923  "local/interface");
924 
925  # start out with an empty list
926  $Interfaces = array();
927 
928  # for each possible UI directory
929  foreach ($InterfaceDirs as $InterfaceDir)
930  {
931  $Dir = dir($InterfaceDir);
932 
933  # for each file in current directory
934  while (($DirEntry = $Dir->read()) !== FALSE)
935  {
936  $InterfacePath = $InterfaceDir."/".$DirEntry;
937 
938  # skip anything that doesn't have a name in the required format
939  if (!preg_match('/^[a-zA-Z0-9]+$/', $DirEntry))
940  {
941  continue;
942  }
943 
944  # skip anything that isn't a directory
945  if (!is_dir($InterfacePath))
946  {
947  continue;
948  }
949 
950  # read the UI name (if available)
951  $UIName = @file_get_contents($InterfacePath."/NAME");
952 
953  # use the directory name if the UI name isn't available
954  if ($UIName === FALSE || !strlen($UIName))
955  {
956  $UIName = $DirEntry;
957  }
958 
959  $Interfaces[$InterfacePath] = $UIName;
960  }
961 
962  $Dir->close();
963  }
964 
965  # return list to caller
966  return $Interfaces;
967  }
968 
984  function AddPostProcessingCall($FunctionName,
985  &$Arg1 = self::NOVALUE, &$Arg2 = self::NOVALUE, &$Arg3 = self::NOVALUE,
986  &$Arg4 = self::NOVALUE, &$Arg5 = self::NOVALUE, &$Arg6 = self::NOVALUE,
987  &$Arg7 = self::NOVALUE, &$Arg8 = self::NOVALUE, &$Arg9 = self::NOVALUE)
988  {
989  $FuncIndex = count($this->PostProcessingFuncs);
990  $this->PostProcessingFuncs[$FuncIndex]["FunctionName"] = $FunctionName;
991  $this->PostProcessingFuncs[$FuncIndex]["Arguments"] = array();
992  $Index = 1;
993  while (isset(${"Arg".$Index}) && (${"Arg".$Index} !== self::NOVALUE))
994  {
995  $this->PostProcessingFuncs[$FuncIndex]["Arguments"][$Index]
996  =& ${"Arg".$Index};
997  $Index++;
998  }
999  }
1000 
1006  function AddEnvInclude($FileName)
1007  {
1008  $this->EnvIncludes[] = $FileName;
1009  }
1010 
1017  function GUIFile($FileName)
1018  {
1019  # determine file type (being clever because this is run so often)
1020  static $FTOther = 0;
1021  static $FTCSS = 1;
1022  static $FTImage = 2;
1023  static $FTJavaScript = 3;
1024  $FileSuffix = strtolower(substr($FileName, -3));
1025  if ($FileSuffix == "css") { $FileType = $FTCSS; }
1026  elseif ($FileSuffix == ".js") { $FileType = $FTJavaScript; }
1027  elseif (($FileSuffix == "gif")
1028  || ($FileSuffix == "jpg")
1029  || ($FileSuffix == "png")) { $FileType = $FTImage; }
1030  else { $FileType = $FTOther; }
1031 
1032  # determine which location to search based on file suffix
1033  $DirList = ($FileType == $FTImage)
1034  ? $this->ImageDirList : $this->IncludeDirList;
1035 
1036  # if directed to get a minimized JavaScript file
1037  if (($FileType == $FTJavaScript) && $this->UseMinimizedJavascript)
1038  {
1039  # first try to find the minimized JavaScript file
1040  $MinimizedFileName = substr_replace($FileName, ".min", -3, 0);
1041  $FoundFileName = $this->FindFile($DirList, $MinimizedFileName);
1042 
1043  # search for the regular file if a minimized file wasn't found
1044  if (is_null($FoundFileName))
1045  {
1046  $FoundFileName = $this->FindFile($DirList, $FileName);
1047  }
1048  }
1049  # else if directed to use SCSS files
1050  elseif (($FileType == $FTCSS) && $this->UseScss)
1051  {
1052  # look for SCSS version of file
1053  $SourceFileName = preg_replace("/.css$/", ".scss", $FileName);
1054  $FoundSourceFileName = $this->FindFile($DirList, $SourceFileName);
1055 
1056  # if SCSS file not found
1057  if ($FoundSourceFileName === NULL)
1058  {
1059  # look for CSS file
1060  $FoundFileName = $this->FindFile($DirList, $FileName);
1061  }
1062  else
1063  {
1064  # compile SCSS file (if updated) and return resulting CSS file
1065  $FoundFileName = $this->CompileScssFile($FoundSourceFileName);
1066  }
1067  }
1068  # otherwise just search for the file
1069  else
1070  {
1071  $FoundFileName = $this->FindFile($DirList, $FileName);
1072  }
1073 
1074  # add non-image files to list of found files (used for required files loading)
1075  if ($FileType != $FTImage)
1076  { $this->FoundUIFiles[] = basename($FoundFileName); }
1077 
1078  # return file name to caller
1079  return $FoundFileName;
1080  }
1081 
1091  function PUIFile($FileName)
1092  {
1093  $FullFileName = $this->GUIFile($FileName);
1094  if ($FullFileName) { print($FullFileName); }
1095  }
1096 
1104  function RequireUIFile($FileName)
1105  {
1106  $this->AdditionalRequiredUIFiles[] = $FileName;
1107  }
1108 
1117  function LoadFunction($Callback)
1118  {
1119  # if specified function is not currently available
1120  if (!is_callable($Callback))
1121  {
1122  # if function info looks legal
1123  if (is_string($Callback) && strlen($Callback))
1124  {
1125  # start with function directory list
1126  $Locations = $this->FunctionDirList;
1127 
1128  # add object directories to list
1129  $Locations = array_merge(
1130  $Locations, array_keys(self::$ObjectDirectories));
1131 
1132  # look for function file
1133  $FunctionFileName = $this->FindFile($Locations, "F-".$Callback,
1134  array("php", "html"));
1135 
1136  # if function file was found
1137  if ($FunctionFileName)
1138  {
1139  # load function file
1140  include_once($FunctionFileName);
1141  }
1142  else
1143  {
1144  # log error indicating function load failed
1145  $this->LogError(self::LOGLVL_ERROR, "Unable to load function"
1146  ." for callback \"".$Callback."\".");
1147  }
1148  }
1149  else
1150  {
1151  # log error indicating specified function info was bad
1152  $this->LogError(self::LOGLVL_ERROR, "Unloadable callback value"
1153  ." (".$Callback.")"
1154  ." passed to AF::LoadFunction().");
1155  }
1156  }
1157 
1158  # report to caller whether function load succeeded
1159  return is_callable($Callback);
1160  }
1161 
1167  {
1168  return microtime(TRUE) - $this->ExecutionStartTime;
1169  }
1170 
1176  {
1177  return ini_get("max_execution_time") - $this->GetElapsedExecutionTime();
1178  }
1179 
1180  /*@)*/ /* Application Framework */
1181 
1182 
1183  # ---- Logging -----------------------------------------------------------
1184  /*@(*/
1186 
1197  function LogSlowPageLoads($NewValue = DB_NOVALUE)
1198  {
1199  return $this->UpdateSetting("LogSlowPageLoads", $NewValue);
1200  }
1201 
1209  function SlowPageLoadThreshold($NewValue = DB_NOVALUE)
1210  {
1211  return $this->UpdateSetting("SlowPageLoadThreshold", $NewValue);
1212  }
1213 
1224  function LogHighMemoryUsage($NewValue = DB_NOVALUE)
1225  {
1226  return $this->UpdateSetting("LogHighMemoryUsage", $NewValue);
1227  }
1228 
1237  function HighMemoryUsageThreshold($NewValue = DB_NOVALUE)
1238  {
1239  return $this->UpdateSetting("HighMemoryUsageThreshold", $NewValue);
1240  }
1241 
1255  function LogError($Level, $Msg)
1256  {
1257  # if error level is at or below current logging level
1258  if ($this->Settings["LoggingLevel"] >= $Level)
1259  {
1260  # attempt to log error message
1261  $Result = $this->LogMessage($Level, $Msg);
1262 
1263  # if logging attempt failed and level indicated significant error
1264  if (($Result === FALSE) && ($Level <= self::LOGLVL_ERROR))
1265  {
1266  # throw exception about inability to log error
1267  static $AlreadyThrewException = FALSE;
1268  if (!$AlreadyThrewException)
1269  {
1270  $AlreadyThrewException = TRUE;
1271  throw new Exception("Unable to log error (".$Level.": ".$Msg.").");
1272  }
1273  }
1274 
1275  # report to caller whether message was logged
1276  return $Result;
1277  }
1278  else
1279  {
1280  # report to caller that message was not logged
1281  return FALSE;
1282  }
1283  }
1284 
1296  function LogMessage($Level, $Msg)
1297  {
1298  # if message level is at or below current logging level
1299  if ($this->Settings["LoggingLevel"] >= $Level)
1300  {
1301  # attempt to open log file
1302  $FHndl = @fopen($this->LogFileName, "a");
1303 
1304  # if log file could not be open
1305  if ($FHndl === FALSE)
1306  {
1307  # report to caller that message was not logged
1308  return FALSE;
1309  }
1310  else
1311  {
1312  # format log entry
1313  $ErrorAbbrevs = array(
1314  self::LOGLVL_FATAL => "FTL",
1315  self::LOGLVL_ERROR => "ERR",
1316  self::LOGLVL_WARNING => "WRN",
1317  self::LOGLVL_INFO => "INF",
1318  self::LOGLVL_DEBUG => "DBG",
1319  self::LOGLVL_TRACE => "TRC",
1320  );
1321  $LogEntry = date("Y-m-d H:i:s")
1322  ." ".($this->RunningInBackground ? "B" : "F")
1323  ." ".$ErrorAbbrevs[$Level]
1324  ." ".$Msg;
1325 
1326  # write entry to log
1327  $Success = fwrite($FHndl, $LogEntry."\n");
1328 
1329  # close log file
1330  fclose($FHndl);
1331 
1332  # report to caller whether message was logged
1333  return ($Success === FALSE) ? FALSE : TRUE;
1334  }
1335  }
1336  else
1337  {
1338  # report to caller that message was not logged
1339  return FALSE;
1340  }
1341  }
1342 
1364  function LoggingLevel($NewValue = DB_NOVALUE)
1365  {
1366  # constrain new level (if supplied) to within legal bounds
1367  if ($NewValue !== DB_NOVALUE)
1368  {
1369  $NewValue = max(min($NewValue, 6), 1);
1370  }
1371 
1372  # set new logging level (if supplied) and return current level to caller
1373  return $this->UpdateSetting("LoggingLevel", $NewValue);
1374  }
1375 
1382  function LogFile($NewValue = NULL)
1383  {
1384  if ($NewValue !== NULL) { $this->LogFileName = $NewValue; }
1385  return $this->LogFileName;
1386  }
1387 
1392  const LOGLVL_TRACE = 6;
1397  const LOGLVL_DEBUG = 5;
1403  const LOGLVL_INFO = 4;
1408  const LOGLVL_WARNING = 3;
1414  const LOGLVL_ERROR = 2;
1419  const LOGLVL_FATAL = 1;
1420 
1421  /*@)*/ /* Logging */
1422 
1423 
1424  # ---- Event Handling ----------------------------------------------------
1425  /*@(*/
1427 
1437  const EVENTTYPE_CHAIN = 2;
1443  const EVENTTYPE_FIRST = 3;
1451  const EVENTTYPE_NAMED = 4;
1452 
1454  const ORDER_FIRST = 1;
1456  const ORDER_MIDDLE = 2;
1458  const ORDER_LAST = 3;
1459 
1468  function RegisterEvent($EventsOrEventName, $EventType = NULL)
1469  {
1470  # convert parameters to array if not already in that form
1471  $Events = is_array($EventsOrEventName) ? $EventsOrEventName
1472  : array($EventsOrEventName => $EventType);
1473 
1474  # for each event
1475  foreach ($Events as $Name => $Type)
1476  {
1477  # store event information
1478  $this->RegisteredEvents[$Name]["Type"] = $Type;
1479  $this->RegisteredEvents[$Name]["Hooks"] = array();
1480  }
1481  }
1482 
1489  function IsRegisteredEvent($EventName)
1490  {
1491  return array_key_exists($EventName, $this->RegisteredEvents)
1492  ? TRUE : FALSE;
1493  }
1494 
1501  function IsHookedEvent($EventName)
1502  {
1503  # the event isn't hooked to if it isn't even registered
1504  if (!$this->IsRegisteredEvent($EventName))
1505  {
1506  return FALSE;
1507  }
1508 
1509  # return TRUE if there is at least one callback hooked to the event
1510  return count($this->RegisteredEvents[$EventName]["Hooks"]) > 0;
1511  }
1512 
1526  function HookEvent($EventsOrEventName, $Callback = NULL, $Order = self::ORDER_MIDDLE)
1527  {
1528  # convert parameters to array if not already in that form
1529  $Events = is_array($EventsOrEventName) ? $EventsOrEventName
1530  : array($EventsOrEventName => $Callback);
1531 
1532  # for each event
1533  $Success = TRUE;
1534  foreach ($Events as $EventName => $EventCallback)
1535  {
1536  # if callback is valid
1537  if (is_callable($EventCallback))
1538  {
1539  # if this is a periodic event we process internally
1540  if (isset($this->PeriodicEvents[$EventName]))
1541  {
1542  # process event now
1543  $this->ProcessPeriodicEvent($EventName, $EventCallback);
1544  }
1545  # if specified event has been registered
1546  elseif (isset($this->RegisteredEvents[$EventName]))
1547  {
1548  # add callback for event
1549  $this->RegisteredEvents[$EventName]["Hooks"][]
1550  = array("Callback" => $EventCallback, "Order" => $Order);
1551 
1552  # sort callbacks by order
1553  if (count($this->RegisteredEvents[$EventName]["Hooks"]) > 1)
1554  {
1555  usort($this->RegisteredEvents[$EventName]["Hooks"],
1556  array("ApplicationFramework", "HookEvent_OrderCompare"));
1557  }
1558  }
1559  else
1560  {
1561  $Success = FALSE;
1562  }
1563  }
1564  else
1565  {
1566  $Success = FALSE;
1567  }
1568  }
1569 
1570  # report to caller whether all callbacks were hooked
1571  return $Success;
1572  }
1574  private static function HookEvent_OrderCompare($A, $B)
1575  {
1576  if ($A["Order"] == $B["Order"]) { return 0; }
1577  return ($A["Order"] < $B["Order"]) ? -1 : 1;
1578  }
1579 
1590  function SignalEvent($EventName, $Parameters = NULL)
1591  {
1592  $ReturnValue = NULL;
1593 
1594  # if event has been registered
1595  if (isset($this->RegisteredEvents[$EventName]))
1596  {
1597  # set up default return value (if not NULL)
1598  switch ($this->RegisteredEvents[$EventName]["Type"])
1599  {
1600  case self::EVENTTYPE_CHAIN:
1601  $ReturnValue = $Parameters;
1602  break;
1603 
1604  case self::EVENTTYPE_NAMED:
1605  $ReturnValue = array();
1606  break;
1607  }
1608 
1609  # for each callback for this event
1610  foreach ($this->RegisteredEvents[$EventName]["Hooks"] as $Hook)
1611  {
1612  # invoke callback
1613  $Callback = $Hook["Callback"];
1614  $Result = ($Parameters !== NULL)
1615  ? call_user_func_array($Callback, $Parameters)
1616  : call_user_func($Callback);
1617 
1618  # process return value based on event type
1619  switch ($this->RegisteredEvents[$EventName]["Type"])
1620  {
1621  case self::EVENTTYPE_CHAIN:
1622  if ($Result !== NULL)
1623  {
1624  foreach ($Parameters as $Index => $Value)
1625  {
1626  if (array_key_exists($Index, $Result))
1627  {
1628  $Parameters[$Index] = $Result[$Index];
1629  }
1630  }
1631  $ReturnValue = $Parameters;
1632  }
1633  break;
1634 
1635  case self::EVENTTYPE_FIRST:
1636  if ($Result !== NULL)
1637  {
1638  $ReturnValue = $Result;
1639  break 2;
1640  }
1641  break;
1642 
1643  case self::EVENTTYPE_NAMED:
1644  $CallbackName = is_array($Callback)
1645  ? (is_object($Callback[0])
1646  ? get_class($Callback[0])
1647  : $Callback[0])."::".$Callback[1]
1648  : $Callback;
1649  $ReturnValue[$CallbackName] = $Result;
1650  break;
1651 
1652  default:
1653  break;
1654  }
1655  }
1656  }
1657  else
1658  {
1659  $this->LogError(self::LOGLVL_WARNING,
1660  "Unregistered event signaled (".$EventName.").");
1661  }
1662 
1663  # return value if any to caller
1664  return $ReturnValue;
1665  }
1666 
1672  function IsStaticOnlyEvent($EventName)
1673  {
1674  return isset($this->PeriodicEvents[$EventName]) ? TRUE : FALSE;
1675  }
1676 
1687  function EventWillNextRunAt($EventName, $Callback)
1688  {
1689  # if event is not a periodic event report failure to caller
1690  if (!array_key_exists($EventName, $this->EventPeriods)) { return FALSE; }
1691 
1692  # retrieve last execution time for event if available
1693  $Signature = self::GetCallbackSignature($Callback);
1694  $LastRunTime = $this->DB->Query("SELECT LastRunAt FROM PeriodicEvents"
1695  ." WHERE Signature = '".addslashes($Signature)."'", "LastRunAt");
1696 
1697  # if event was not found report failure to caller
1698  if ($LastRunTime === NULL) { return FALSE; }
1699 
1700  # calculate next run time based on event period
1701  $NextRunTime = strtotime($LastRunTime) + $this->EventPeriods[$EventName];
1702 
1703  # report next run time to caller
1704  return $NextRunTime;
1705  }
1706 
1723  {
1724  # retrieve last execution times
1725  $this->DB->Query("SELECT * FROM PeriodicEvents");
1726  $LastRunTimes = $this->DB->FetchColumn("LastRunAt", "Signature");
1727 
1728  # for each known event
1729  $Events = array();
1730  foreach ($this->KnownPeriodicEvents as $Signature => $Info)
1731  {
1732  # if last run time for event is available
1733  if (array_key_exists($Signature, $LastRunTimes))
1734  {
1735  # calculate next run time for event
1736  $LastRun = strtotime($LastRunTimes[$Signature]);
1737  $NextRun = $LastRun + $this->EventPeriods[$Info["Period"]];
1738  if ($Info["Period"] == "EVENT_PERIODIC") { $LastRun = FALSE; }
1739  }
1740  else
1741  {
1742  # set info to indicate run times are not known
1743  $LastRun = FALSE;
1744  $NextRun = FALSE;
1745  }
1746 
1747  # add event info to list
1748  $Events[$Signature] = $Info;
1749  $Events[$Signature]["LastRun"] = $LastRun;
1750  $Events[$Signature]["NextRun"] = $NextRun;
1751  $Events[$Signature]["Parameters"] = NULL;
1752  }
1753 
1754  # return list of known events to caller
1755  return $Events;
1756  }
1757 
1758  /*@)*/ /* Event Handling */
1759 
1760 
1761  # ---- Task Management ---------------------------------------------------
1762  /*@(*/
1764 
1766  const PRIORITY_HIGH = 1;
1768  const PRIORITY_MEDIUM = 2;
1770  const PRIORITY_LOW = 3;
1773 
1786  function QueueTask($Callback, $Parameters = NULL,
1787  $Priority = self::PRIORITY_LOW, $Description = "")
1788  {
1789  # pack task info and write to database
1790  if ($Parameters === NULL) { $Parameters = array(); }
1791  $this->DB->Query("INSERT INTO TaskQueue"
1792  ." (Callback, Parameters, Priority, Description)"
1793  ." VALUES ('".addslashes(serialize($Callback))."', '"
1794  .addslashes(serialize($Parameters))."', ".intval($Priority).", '"
1795  .addslashes($Description)."')");
1796  }
1797 
1815  function QueueUniqueTask($Callback, $Parameters = NULL,
1816  $Priority = self::PRIORITY_LOW, $Description = "")
1817  {
1818  if ($this->TaskIsInQueue($Callback, $Parameters))
1819  {
1820  $QueryResult = $this->DB->Query("SELECT TaskId,Priority FROM TaskQueue"
1821  ." WHERE Callback = '".addslashes(serialize($Callback))."'"
1822  .($Parameters ? " AND Parameters = '"
1823  .addslashes(serialize($Parameters))."'" : ""));
1824  if ($QueryResult !== FALSE)
1825  {
1826  $Record = $this->DB->FetchRow();
1827  if ($Record["Priority"] > $Priority)
1828  {
1829  $this->DB->Query("UPDATE TaskQueue"
1830  ." SET Priority = ".intval($Priority)
1831  ." WHERE TaskId = ".intval($Record["TaskId"]));
1832  }
1833  }
1834  return FALSE;
1835  }
1836  else
1837  {
1838  $this->QueueTask($Callback, $Parameters, $Priority, $Description);
1839  return TRUE;
1840  }
1841  }
1842 
1852  function TaskIsInQueue($Callback, $Parameters = NULL)
1853  {
1854  $QueuedCount = $this->DB->Query(
1855  "SELECT COUNT(*) AS FoundCount FROM TaskQueue"
1856  ." WHERE Callback = '".addslashes(serialize($Callback))."'"
1857  .($Parameters ? " AND Parameters = '"
1858  .addslashes(serialize($Parameters))."'" : ""),
1859  "FoundCount");
1860  $RunningCount = $this->DB->Query(
1861  "SELECT COUNT(*) AS FoundCount FROM RunningTasks"
1862  ." WHERE Callback = '".addslashes(serialize($Callback))."'"
1863  .($Parameters ? " AND Parameters = '"
1864  .addslashes(serialize($Parameters))."'" : ""),
1865  "FoundCount");
1866  $FoundCount = $QueuedCount + $RunningCount;
1867  return ($FoundCount ? TRUE : FALSE);
1868  }
1869 
1875  function GetTaskQueueSize($Priority = NULL)
1876  {
1877  return $this->GetQueuedTaskCount(NULL, NULL, $Priority);
1878  }
1879 
1887  function GetQueuedTaskList($Count = 100, $Offset = 0)
1888  {
1889  return $this->GetTaskList("SELECT * FROM TaskQueue"
1890  ." ORDER BY Priority, TaskId ", $Count, $Offset);
1891  }
1892 
1906  function GetQueuedTaskCount($Callback = NULL,
1907  $Parameters = NULL, $Priority = NULL, $Description = NULL)
1908  {
1909  $Query = "SELECT COUNT(*) AS TaskCount FROM TaskQueue";
1910  $Sep = " WHERE";
1911  if ($Callback !== NULL)
1912  {
1913  $Query .= $Sep." Callback = '".addslashes(serialize($Callback))."'";
1914  $Sep = " AND";
1915  }
1916  if ($Parameters !== NULL)
1917  {
1918  $Query .= $Sep." Parameters = '".addslashes(serialize($Parameters))."'";
1919  $Sep = " AND";
1920  }
1921  if ($Priority !== NULL)
1922  {
1923  $Query .= $Sep." Priority = ".intval($Priority);
1924  $Sep = " AND";
1925  }
1926  if ($Description !== NULL)
1927  {
1928  $Query .= $Sep." Description = '".addslashes($Description)."'";
1929  }
1930  return $this->DB->Query($Query, "TaskCount");
1931  }
1932 
1940  function GetRunningTaskList($Count = 100, $Offset = 0)
1941  {
1942  return $this->GetTaskList("SELECT * FROM RunningTasks"
1943  ." WHERE StartedAt >= '".date("Y-m-d H:i:s",
1944  (time() - ini_get("max_execution_time")))."'"
1945  ." ORDER BY StartedAt", $Count, $Offset);
1946  }
1947 
1955  function GetOrphanedTaskList($Count = 100, $Offset = 0)
1956  {
1957  return $this->GetTaskList("SELECT * FROM RunningTasks"
1958  ." WHERE StartedAt < '".date("Y-m-d H:i:s",
1959  (time() - ini_get("max_execution_time")))."'"
1960  ." ORDER BY StartedAt", $Count, $Offset);
1961  }
1962 
1968  {
1969  return $this->DB->Query("SELECT COUNT(*) AS Count FROM RunningTasks"
1970  ." WHERE StartedAt < '".date("Y-m-d H:i:s",
1971  (time() - ini_get("max_execution_time")))."'",
1972  "Count");
1973  }
1974 
1980  function ReQueueOrphanedTask($TaskId, $NewPriority = NULL)
1981  {
1982  $this->DB->Query("LOCK TABLES TaskQueue WRITE, RunningTasks WRITE");
1983  $this->DB->Query("INSERT INTO TaskQueue"
1984  ." (Callback,Parameters,Priority,Description) "
1985  ."SELECT Callback, Parameters, Priority, Description"
1986  ." FROM RunningTasks WHERE TaskId = ".intval($TaskId));
1987  if ($NewPriority !== NULL)
1988  {
1989  $NewTaskId = $this->DB->LastInsertId();
1990  $this->DB->Query("UPDATE TaskQueue SET Priority = "
1991  .intval($NewPriority)
1992  ." WHERE TaskId = ".intval($NewTaskId));
1993  }
1994  $this->DB->Query("DELETE FROM RunningTasks WHERE TaskId = ".intval($TaskId));
1995  $this->DB->Query("UNLOCK TABLES");
1996  }
1997 
2002  function DeleteTask($TaskId)
2003  {
2004  $this->DB->Query("DELETE FROM TaskQueue WHERE TaskId = ".intval($TaskId));
2005  $this->DB->Query("DELETE FROM RunningTasks WHERE TaskId = ".intval($TaskId));
2006  }
2007 
2015  function GetTask($TaskId)
2016  {
2017  # assume task will not be found
2018  $Task = NULL;
2019 
2020  # look for task in task queue
2021  $this->DB->Query("SELECT * FROM TaskQueue WHERE TaskId = ".intval($TaskId));
2022 
2023  # if task was not found in queue
2024  if (!$this->DB->NumRowsSelected())
2025  {
2026  # look for task in running task list
2027  $this->DB->Query("SELECT * FROM RunningTasks WHERE TaskId = "
2028  .intval($TaskId));
2029  }
2030 
2031  # if task was found
2032  if ($this->DB->NumRowsSelected())
2033  {
2034  # if task was periodic
2035  $Row = $this->DB->FetchRow();
2036  if ($Row["Callback"] ==
2037  serialize(array("ApplicationFramework", "PeriodicEventWrapper")))
2038  {
2039  # unpack periodic task callback
2040  $WrappedCallback = unserialize($Row["Parameters"]);
2041  $Task["Callback"] = $WrappedCallback[1];
2042  $Task["Parameters"] = $WrappedCallback[2];
2043  }
2044  else
2045  {
2046  # unpack task callback and parameters
2047  $Task["Callback"] = unserialize($Row["Callback"]);
2048  $Task["Parameters"] = unserialize($Row["Parameters"]);
2049  }
2050  }
2051 
2052  # return task to caller
2053  return $Task;
2054  }
2055 
2063  function TaskExecutionEnabled($NewValue = DB_NOVALUE)
2064  {
2065  return $this->UpdateSetting("TaskExecutionEnabled", $NewValue);
2066  }
2067 
2073  function MaxTasks($NewValue = DB_NOVALUE)
2074  {
2075  return $this->UpdateSetting("MaxTasksRunning", $NewValue);
2076  }
2077 
2085  function MaxExecutionTime($NewValue = NULL)
2086  {
2087  if (func_num_args() && !ini_get("safe_mode"))
2088  {
2089  if ($NewValue != $this->Settings["MaxExecTime"])
2090  {
2091  $this->Settings["MaxExecTime"] = max($NewValue, 5);
2092  $this->DB->Query("UPDATE ApplicationFrameworkSettings"
2093  ." SET MaxExecTime = '"
2094  .intval($this->Settings["MaxExecTime"])."'");
2095  }
2096  ini_set("max_execution_time", $this->Settings["MaxExecTime"]);
2097  set_time_limit($this->Settings["MaxExecTime"]);
2098  }
2099  return ini_get("max_execution_time");
2100  }
2101 
2102  /*@)*/ /* Task Management */
2103 
2104 
2105  # ---- Clean URL Support -------------------------------------------------
2106  /*@(*/
2108 
2135  function AddCleanUrl($Pattern, $Page, $GetVars = NULL, $Template = NULL)
2136  {
2137  # save clean URL mapping parameters
2138  $this->CleanUrlMappings[] = array(
2139  "Pattern" => $Pattern,
2140  "Page" => $Page,
2141  "GetVars" => $GetVars,
2142  );
2143 
2144  # if replacement template specified
2145  if ($Template !== NULL)
2146  {
2147  # if GET parameters specified
2148  if (count($GetVars))
2149  {
2150  # retrieve all possible permutations of GET parameters
2151  $GetPerms = $this->ArrayPermutations(array_keys($GetVars));
2152 
2153  # for each permutation of GET parameters
2154  foreach ($GetPerms as $VarPermutation)
2155  {
2156  # construct search pattern for permutation
2157  $SearchPattern = "/href=([\"'])index\\.php\\?P=".$Page;
2158  $GetVarSegment = "";
2159  foreach ($VarPermutation as $GetVar)
2160  {
2161  if (preg_match("%\\\$[0-9]+%", $GetVars[$GetVar]))
2162  {
2163  $GetVarSegment .= "&amp;".$GetVar."=((?:(?!\\1)[^&])+)";
2164  }
2165  else
2166  {
2167  $GetVarSegment .= "&amp;".$GetVar."=".$GetVars[$GetVar];
2168  }
2169  }
2170  $SearchPattern .= $GetVarSegment."\\1/i";
2171 
2172  # if template is actually a callback
2173  if (is_callable($Template))
2174  {
2175  # add pattern to HTML output mod callbacks list
2176  $this->OutputModificationCallbacks[] = array(
2177  "Pattern" => $Pattern,
2178  "Page" => $Page,
2179  "SearchPattern" => $SearchPattern,
2180  "Callback" => $Template,
2181  );
2182  }
2183  else
2184  {
2185  # construct replacement string for permutation
2186  $Replacement = $Template;
2187  $Index = 2;
2188  foreach ($VarPermutation as $GetVar)
2189  {
2190  $Replacement = str_replace(
2191  "\$".$GetVar, "\$".$Index, $Replacement);
2192  $Index++;
2193  }
2194  $Replacement = "href=\"".$Replacement."\"";
2195 
2196  # add pattern to HTML output modifications list
2197  $this->OutputModificationPatterns[] = $SearchPattern;
2198  $this->OutputModificationReplacements[] = $Replacement;
2199  }
2200  }
2201  }
2202  else
2203  {
2204  # construct search pattern
2205  $SearchPattern = "/href=\"index\\.php\\?P=".$Page."\"/i";
2206 
2207  # if template is actually a callback
2208  if (is_callable($Template))
2209  {
2210  # add pattern to HTML output mod callbacks list
2211  $this->OutputModificationCallbacks[] = array(
2212  "Pattern" => $Pattern,
2213  "Page" => $Page,
2214  "SearchPattern" => $SearchPattern,
2215  "Callback" => $Template,
2216  );
2217  }
2218  else
2219  {
2220  # add simple pattern to HTML output modifications list
2221  $this->OutputModificationPatterns[] = $SearchPattern;
2222  $this->OutputModificationReplacements[] = "href=\"".$Template."\"";
2223  }
2224  }
2225  }
2226  }
2227 
2233  function CleanUrlIsMapped($Path)
2234  {
2235  foreach ($this->CleanUrlMappings as $Info)
2236  {
2237  if (preg_match($Info["Pattern"], $Path))
2238  {
2239  return TRUE;
2240  }
2241  }
2242  return FALSE;
2243  }
2244 
2251  function GetCleanUrlForPath($Path)
2252  {
2253  # the search patterns and callbacks require a specific format
2254  $Format = "href=\"".str_replace("&", "&amp;", $Path)."\"";
2255  $Search = $Format;
2256 
2257  # perform any regular expression replacements on the search string
2258  $Search = preg_replace(
2259  $this->OutputModificationPatterns,
2260  $this->OutputModificationReplacements,
2261  $Search);
2262 
2263  # only run the callbacks if a replacement hasn't already been performed
2264  if ($Search == $Format)
2265  {
2266  # perform any callback replacements on the search string
2267  foreach ($this->OutputModificationCallbacks as $Info)
2268  {
2269  # make the information available to the callback
2270  $this->OutputModificationCallbackInfo = $Info;
2271 
2272  # execute the callback
2273  $Search = preg_replace_callback(
2274  $Info["SearchPattern"],
2275  array($this, "OutputModificationCallbackShell"),
2276  $Search);
2277  }
2278  }
2279 
2280  # return the path untouched if no replacements were performed
2281  if ($Search == $Format)
2282  {
2283  return $Path;
2284  }
2285 
2286  # remove the bits added to the search string to get it recognized by
2287  # the replacement expressions and callbacks
2288  $Result = substr($Search, 6, -1);
2289 
2290  return $Result;
2291  }
2292 
2299  public function GetUncleanUrlForPath($Path)
2300  {
2301  # for each clean URL mapping
2302  foreach ($this->CleanUrlMappings as $Info)
2303  {
2304  # if current path matches the clean URL pattern
2305  if (preg_match($Info["Pattern"], $Path, $Matches))
2306  {
2307  # the GET parameters for the URL, starting with the page name
2308  $GetVars = array("P" => $Info["Page"]);
2309 
2310  # if additional $_GET variables specified for clean URL
2311  if ($Info["GetVars"] !== NULL)
2312  {
2313  # for each $_GET variable specified for clean URL
2314  foreach ($Info["GetVars"] as $VarName => $VarTemplate)
2315  {
2316  # start with template for variable value
2317  $Value = $VarTemplate;
2318 
2319  # for each subpattern matched in current URL
2320  foreach ($Matches as $Index => $Match)
2321  {
2322  # if not first (whole) match
2323  if ($Index > 0)
2324  {
2325  # make any substitutions in template
2326  $Value = str_replace("$".$Index, $Match, $Value);
2327  }
2328  }
2329 
2330  # add the GET variable
2331  $GetVars[$VarName] = $Value;
2332  }
2333  }
2334 
2335  # return the unclean URL
2336  return "index.php?" . http_build_query($GetVars);
2337  }
2338  }
2339 
2340  # return the path unchanged
2341  return $Path;
2342  }
2343 
2349  function GetCleanUrl()
2350  {
2351  return $this->GetCleanUrlForPath($this->GetUncleanUrl());
2352  }
2353 
2358  function GetUncleanUrl()
2359  {
2360  $GetVars = array("P" => $this->GetPageName()) + $_GET;
2361  return "index.php?" . http_build_query($GetVars);
2362  }
2363 
2364  /*@)*/ /* Clean URL Support */
2365 
2366 
2367  # ---- Server Environment ------------------------------------------------
2368  /*@(*/
2370 
2376  static function SessionLifetime($NewValue = NULL)
2377  {
2378  if ($NewValue !== NULL)
2379  {
2380  self::$SessionLifetime = $NewValue;
2381  }
2382  return self::$SessionLifetime;
2383  }
2384 
2390  static function HtaccessSupport()
2391  {
2392  # HTACCESS_SUPPORT is set in the .htaccess file
2393  return isset($_SERVER["HTACCESS_SUPPORT"]);
2394  }
2395 
2403  static function RootUrl()
2404  {
2405  # return override value if one is set
2406  if (self::$RootUrlOverride !== NULL)
2407  {
2408  return self::$RootUrlOverride;
2409  }
2410 
2411  # determine scheme name
2412  $Protocol = (isset($_SERVER["HTTPS"]) ? "https" : "http");
2413 
2414  # if HTTP_HOST is preferred or SERVER_NAME points to localhost
2415  # and HTTP_HOST is set
2416  if ((self::$PreferHttpHost || ($_SERVER["SERVER_NAME"] == "127.0.0.1"))
2417  && isset($_SERVER["HTTP_HOST"]))
2418  {
2419  # use HTTP_HOST for domain name
2420  $DomainName = $_SERVER["HTTP_HOST"];
2421  }
2422  else
2423  {
2424  # use SERVER_NAME for domain name
2425  $DomainName = $_SERVER["HTTP_HOST"];
2426  }
2427 
2428  # build URL root and return to caller
2429  return $Protocol."://".$DomainName;
2430  }
2431 
2446  static function RootUrlOverride($NewValue = self::NOVALUE)
2447  {
2448  if ($NewValue !== self::NOVALUE)
2449  {
2450  self::$RootUrlOverride = strlen(trim($NewValue)) ? $NewValue : NULL;
2451  }
2452  return self::$RootUrlOverride;
2453  }
2454 
2464  static function BaseUrl()
2465  {
2466  $BaseUrl = self::RootUrl().dirname($_SERVER["SCRIPT_NAME"]);
2467  if (substr($BaseUrl, -1) != "/") { $BaseUrl .= "/"; }
2468  return $BaseUrl;
2469  }
2470 
2478  static function FullUrl()
2479  {
2480  return self::RootUrl().$_SERVER["REQUEST_URI"];
2481  }
2482 
2493  static function PreferHttpHost($NewValue = NULL)
2494  {
2495  if ($NewValue !== NULL)
2496  {
2497  self::$PreferHttpHost = ($NewValue ? TRUE : FALSE);
2498  }
2499  return self::$PreferHttpHost;
2500  }
2501 
2506  static function BasePath()
2507  {
2508  $BasePath = dirname($_SERVER["SCRIPT_NAME"]);
2509 
2510  if (substr($BasePath, -1) != "/")
2511  {
2512  $BasePath .= "/";
2513  }
2514 
2515  return $BasePath;
2516  }
2517 
2523  static function GetScriptUrl()
2524  {
2525  if (array_key_exists("SCRIPT_URL", $_SERVER))
2526  {
2527  return $_SERVER["SCRIPT_URL"];
2528  }
2529  elseif (array_key_exists("REDIRECT_URL", $_SERVER))
2530  {
2531  return $_SERVER["REDIRECT_URL"];
2532  }
2533  elseif (array_key_exists("REQUEST_URI", $_SERVER))
2534  {
2535  $Pieces = parse_url($_SERVER["REQUEST_URI"]);
2536  return $Pieces["path"];
2537  }
2538  else
2539  {
2540  return NULL;
2541  }
2542  }
2543 
2552  static function WasUrlRewritten($ScriptName="index.php")
2553  {
2554  # needed to get the path of the URL minus the query and fragment pieces
2555  $Components = parse_url(self::GetScriptUrl());
2556 
2557  # if parsing was successful and a path is set
2558  if (is_array($Components) && isset($Components["path"]))
2559  {
2560  $BasePath = self::BasePath();
2561  $Path = $Components["path"];
2562 
2563  # the URL was rewritten if the path isn't the base path, i.e., the
2564  # home page, and the file in the URL isn't the script generating the
2565  # page
2566  if ($BasePath != $Path && basename($Path) != $ScriptName)
2567  {
2568  return TRUE;
2569  }
2570  }
2571 
2572  # the URL wasn't rewritten
2573  return FALSE;
2574  }
2575 
2581  static function GetFreeMemory()
2582  {
2583  return self::GetPhpMemoryLimit() - memory_get_usage();
2584  }
2585 
2591  static function GetPhpMemoryLimit()
2592  {
2593  $Str = strtoupper(ini_get("memory_limit"));
2594  if (substr($Str, -1) == "B") { $Str = substr($Str, 0, strlen($Str) - 1); }
2595  switch (substr($Str, -1))
2596  {
2597  case "K": $MemoryLimit = (int)$Str * 1024; break;
2598  case "M": $MemoryLimit = (int)$Str * 1048576; break;
2599  case "G": $MemoryLimit = (int)$Str * 1073741824; break;
2600  default: $MemoryLimit = (int)$Str; break;
2601  }
2602  return $MemoryLimit;
2603  }
2604 
2605  /*@)*/ /* Server Environment */
2606 
2607 
2608  # ---- Backward Compatibility --------------------------------------------
2609  /*@(*/
2611 
2616  function FindCommonTemplate($BaseName)
2617  {
2618  return $this->FindFile(
2619  $this->IncludeDirList, $BaseName, array("tpl", "html"));
2620  }
2621 
2622  /*@)*/ /* Backward Compatibility */
2623 
2624 
2625  # ---- PRIVATE INTERFACE -------------------------------------------------
2626 
2627  private $ActiveUI = "default";
2628  private $BrowserDetectFunc;
2629  private $CleanUrlMappings = array();
2630  private $CleanUrlRewritePerformed = FALSE;
2631  private $DB;
2632  private $DefaultPage = "Home";
2633  private $EnvIncludes = array();
2634  private $ExecutionStartTime;
2635  private $FoundUIFiles = array();
2636  private $AdditionalRequiredUIFiles = array();
2637  private $GenerateCompactCss = TRUE;
2638  private $HtmlCharset = "UTF-8";
2639  private $JumpToPage = NULL;
2640  private $LogFileName = "local/logs/site.log";
2641  private $MaxRunningTasksToTrack = 250;
2642  private $OutputModificationPatterns = array();
2643  private $OutputModificationReplacements = array();
2644  private $OutputModificationCallbacks = array();
2645  private $OutputModificationCallbackInfo;
2646  private $PageName;
2647  private $PostProcessingFuncs = array();
2648  private $RunningInBackground = FALSE;
2649  private $RunningTask;
2650  private $SavedContext;
2651  private $Settings;
2652  private $SuppressHTML = FALSE;
2653  private $SaveTemplateLocationCache = FALSE;
2654  private $UnbufferedCallbacks = array();
2655  private $UseBaseTag = FALSE;
2656  private $UseMinimizedJavascript = FALSE;
2657  private $UseScss = TRUE;
2658 
2659  private static $AppName = "ScoutAF";
2660  private static $ObjectDirectories = array();
2661  private static $ObjectLocationCache;
2662  private static $ObjectLocationCacheInterval = 60;
2663  private static $ObjectLocationCacheExpiration;
2664  private static $PreferHttpHost = FALSE;
2665  private static $RootUrlOverride = NULL;
2666  private static $SaveObjectLocationCache = FALSE;
2667  private static $ScssCacheDir = "local/data/caches/SCSS";
2668  private static $SessionLifetime = 1440;
2669 
2674  private $NoTSR = FALSE;
2675 
2676  private $KnownPeriodicEvents = array();
2677  private $PeriodicEvents = array(
2678  "EVENT_HOURLY" => self::EVENTTYPE_DEFAULT,
2679  "EVENT_DAILY" => self::EVENTTYPE_DEFAULT,
2680  "EVENT_WEEKLY" => self::EVENTTYPE_DEFAULT,
2681  "EVENT_MONTHLY" => self::EVENTTYPE_DEFAULT,
2682  "EVENT_PERIODIC" => self::EVENTTYPE_NAMED,
2683  );
2684  private $EventPeriods = array(
2685  "EVENT_HOURLY" => 3600,
2686  "EVENT_DAILY" => 86400,
2687  "EVENT_WEEKLY" => 604800,
2688  "EVENT_MONTHLY" => 2592000,
2689  "EVENT_PERIODIC" => 0,
2690  );
2691  private $UIEvents = array(
2692  "EVENT_PAGE_LOAD" => self::EVENTTYPE_DEFAULT,
2693  "EVENT_PHP_FILE_LOAD" => self::EVENTTYPE_CHAIN,
2694  "EVENT_PHP_FILE_LOAD_COMPLETE" => self::EVENTTYPE_DEFAULT,
2695  "EVENT_HTML_FILE_LOAD" => self::EVENTTYPE_CHAIN,
2696  "EVENT_HTML_FILE_LOAD_COMPLETE" => self::EVENTTYPE_DEFAULT,
2697  "EVENT_PAGE_OUTPUT_FILTER" => self::EVENTTYPE_CHAIN,
2698  );
2699 
2703  private function LoadSettings()
2704  {
2705  # read settings in from database
2706  $this->DB->Query("SELECT * FROM ApplicationFrameworkSettings");
2707  $this->Settings = $this->DB->FetchRow();
2708 
2709  # if settings were not previously initialized
2710  if (!$this->Settings)
2711  {
2712  # initialize settings in database
2713  $this->DB->Query("INSERT INTO ApplicationFrameworkSettings"
2714  ." (LastTaskRunAt) VALUES ('2000-01-02 03:04:05')");
2715 
2716  # read new settings in from database
2717  $this->DB->Query("SELECT * FROM ApplicationFrameworkSettings");
2718  $this->Settings = $this->DB->FetchRow();
2719  }
2720 
2721  # if base path was not previously set or we appear to have moved
2722  if (!array_key_exists("BasePath", $this->Settings)
2723  || (!strlen($this->Settings["BasePath"]))
2724  || (!array_key_exists("BasePathCheck", $this->Settings))
2725  || (__FILE__ != $this->Settings["BasePathCheck"]))
2726  {
2727  # attempt to extract base path from Apache .htaccess file
2728  if (is_readable(".htaccess"))
2729  {
2730  $Lines = file(".htaccess");
2731  foreach ($Lines as $Line)
2732  {
2733  if (preg_match("/\\s*RewriteBase\\s+/", $Line))
2734  {
2735  $Pieces = preg_split(
2736  "/\\s+/", $Line, NULL, PREG_SPLIT_NO_EMPTY);
2737  $BasePath = $Pieces[1];
2738  }
2739  }
2740  }
2741 
2742  # if base path was found
2743  if (isset($BasePath))
2744  {
2745  # save base path locally
2746  $this->Settings["BasePath"] = $BasePath;
2747 
2748  # save base path to database
2749  $this->DB->Query("UPDATE ApplicationFrameworkSettings"
2750  ." SET BasePath = '".addslashes($BasePath)."'"
2751  .", BasePathCheck = '".addslashes(__FILE__)."'");
2752  }
2753  }
2754 
2755  # if template location cache has been saved to database
2756  if (isset($this->Settings["TemplateLocationCache"]))
2757  {
2758  # unserialize cache values into array and use if valid
2759  $Cache = unserialize($this->Settings["TemplateLocationCache"]);
2760  $this->Settings["TemplateLocationCache"] =
2761  count($Cache) ? $Cache : array();
2762  }
2763  else
2764  {
2765  # start with empty cache
2766  $this->Settings["TemplateLocationCache"] = array();
2767  }
2768 
2769  # if object location cache has been saved to database
2770  if (isset($this->Settings["ObjectLocationCache"]))
2771  {
2772  # unserialize cache values into array and use if valid
2773  $Cache = unserialize($this->Settings["ObjectLocationCache"]);
2774  $this->Settings["ObjectLocationCache"] =
2775  count($Cache) ? $Cache : array();
2776 
2777  # store static versions for use when autoloading objects
2778  self::$ObjectLocationCache =
2779  $this->Settings["ObjectLocationCache"];
2780  self::$ObjectLocationCacheInterval =
2781  $this->Settings["ObjectLocationCacheInterval"];
2782  self::$ObjectLocationCacheExpiration =
2783  $this->Settings["ObjectLocationCacheExpiration"];
2784  }
2785  else
2786  {
2787  # start with empty cache
2788  $this->Settings["ObjectLocationCache"] = array();
2789  }
2790  }
2791 
2798  private function RewriteCleanUrls($PageName)
2799  {
2800  # if URL rewriting is supported by the server
2801  if ($this->HtaccessSupport())
2802  {
2803  # retrieve current URL and remove base path if present
2804  $Url = $this->GetPageLocation();
2805 
2806  # for each clean URL mapping
2807  foreach ($this->CleanUrlMappings as $Info)
2808  {
2809  # if current URL matches clean URL pattern
2810  if (preg_match($Info["Pattern"], $Url, $Matches))
2811  {
2812  # set new page
2813  $PageName = $Info["Page"];
2814 
2815  # if $_GET variables specified for clean URL
2816  if ($Info["GetVars"] !== NULL)
2817  {
2818  # for each $_GET variable specified for clean URL
2819  foreach ($Info["GetVars"] as $VarName => $VarTemplate)
2820  {
2821  # start with template for variable value
2822  $Value = $VarTemplate;
2823 
2824  # for each subpattern matched in current URL
2825  foreach ($Matches as $Index => $Match)
2826  {
2827  # if not first (whole) match
2828  if ($Index > 0)
2829  {
2830  # make any substitutions in template
2831  $Value = str_replace("$".$Index, $Match, $Value);
2832  }
2833  }
2834 
2835  # set $_GET variable
2836  $_GET[$VarName] = $Value;
2837  }
2838  }
2839 
2840  # set flag indicating clean URL mapped
2841  $this->CleanUrlRewritePerformed = TRUE;
2842 
2843  # stop looking for a mapping
2844  break;
2845  }
2846  }
2847  }
2848 
2849  # return (possibly) updated page name to caller
2850  return $PageName;
2851  }
2852 
2871  private function FindFile($DirectoryList, $BaseName,
2872  $PossibleSuffixes = NULL, $PossiblePrefixes = NULL)
2873  {
2874  # generate template cache index for this page
2875  $CacheIndex = md5(serialize($DirectoryList))
2876  .":".$this->ActiveUI.":".$BaseName;
2877 
2878  # if we have cached location and cache expiration time has not elapsed
2879  if (($this->Settings["TemplateLocationCacheInterval"] > 0)
2880  && count($this->Settings["TemplateLocationCache"])
2881  && array_key_exists($CacheIndex,
2882  $this->Settings["TemplateLocationCache"])
2883  && (time() < strtotime(
2884  $this->Settings["TemplateLocationCacheExpiration"])))
2885  {
2886  # use template location from cache
2887  $FoundFileName = $this->Settings[
2888  "TemplateLocationCache"][$CacheIndex];
2889  }
2890  else
2891  {
2892  # if suffixes specified and base name does not include suffix
2893  if (count($PossibleSuffixes)
2894  && !preg_match("/\.[a-zA-Z0-9]+$/", $BaseName))
2895  {
2896  # add versions of file names with suffixes to file name list
2897  $FileNames = array();
2898  foreach ($PossibleSuffixes as $Suffix)
2899  {
2900  $FileNames[] = $BaseName.".".$Suffix;
2901  }
2902  }
2903  else
2904  {
2905  # use base name as file name
2906  $FileNames = array($BaseName);
2907  }
2908 
2909  # if prefixes specified
2910  if (count($PossiblePrefixes))
2911  {
2912  # add versions of file names with prefixes to file name list
2913  $NewFileNames = array();
2914  foreach ($FileNames as $FileName)
2915  {
2916  foreach ($PossiblePrefixes as $Prefix)
2917  {
2918  $NewFileNames[] = $Prefix.$FileName;
2919  }
2920  }
2921  $FileNames = $NewFileNames;
2922  }
2923 
2924  # for each possible location
2925  $FoundFileName = NULL;
2926  foreach ($DirectoryList as $Dir)
2927  {
2928  # substitute active UI name into path
2929  $Dir = str_replace("%ACTIVEUI%", $this->ActiveUI, $Dir);
2930 
2931  # for each possible file name
2932  foreach ($FileNames as $File)
2933  {
2934  # if template is found at location
2935  if (file_exists($Dir.$File))
2936  {
2937  # save full template file name and stop looking
2938  $FoundFileName = $Dir.$File;
2939  break 2;
2940  }
2941  }
2942  }
2943 
2944  # save location in cache
2945  $this->Settings["TemplateLocationCache"][$CacheIndex]
2946  = $FoundFileName;
2947 
2948  # set flag indicating that cache should be saved
2949  $this->SaveTemplateLocationCache = TRUE;
2950  }
2951 
2952  # return full template file name to caller
2953  return $FoundFileName;
2954  }
2955 
2963  private function CompileScssFile($SrcFile)
2964  {
2965  # build path to CSS file
2966  $DstFile = "local/data/caches/SCSS/"
2967  .str_replace("/", "_", dirname($SrcFile))
2968  ."_".basename($SrcFile);
2969  $DstFile = substr_replace($DstFile, "css", -4);
2970 
2971  # if SCSS file is newer than CSS file
2972  if (!file_exists($DstFile)
2973  || (filemtime($SrcFile) > filemtime($DstFile)))
2974  {
2975  # if CSS cache directory and CSS file path appear writable
2976  static $CacheDirIsWritable;
2977  if (!isset($CacheDirIsWritable))
2978  { $CacheDirIsWritable = is_writable(self::$ScssCacheDir); }
2979  if (is_writable($DstFile)
2980  || (!file_exists($DstFile) && $CacheDirIsWritable))
2981  {
2982  # compile SCSS file to CSS file
2983  $ScssCompiler = new scssc();
2984  $ScssCompiler->setFormatter($this->GenerateCompactCss
2985  ? "scss_formatter_compressed" : "scss_formatter");
2986  $ScssCode = file_get_contents($SrcFile);
2987  try
2988  {
2989  $CssCode = $ScssCompiler->compile($ScssCode);
2990  file_put_contents($DstFile, $CssCode);
2991  }
2992  catch (Exception $Ex)
2993  {
2994  $this->LogError(self::LOGLVL_ERROR,
2995  "Error compiling SCSS file ".$SrcFile.": "
2996  .$Ex->getMessage());
2997  $DstFile = NULL;
2998  }
2999  }
3000  else
3001  {
3002  # log error and set CSS file path to indicate failure
3003  $this->LogError(self::LOGLVL_ERROR,
3004  "Unable to write out CSS file for SCSS file ".$SrcFile);
3005  $DstFile = NULL;
3006  }
3007  }
3008 
3009  # return CSS file path to caller
3010  return $DstFile;
3011  }
3012 
3019  private function GetRequiredFilesNotYetLoaded($PageContentFile)
3020  {
3021  # start out assuming no files required
3022  $RequiredFiles = array();
3023 
3024  # if page content file supplied
3025  if ($PageContentFile)
3026  {
3027  # if file containing list of required files is available
3028  $Path = dirname($PageContentFile);
3029  $RequireListFile = $Path."/REQUIRES";
3030  if (file_exists($RequireListFile))
3031  {
3032  # read in list of required files
3033  $RequestedFiles = file($RequireListFile);
3034 
3035  # for each line in required file list
3036  foreach ($RequestedFiles as $Line)
3037  {
3038  # if line is not a comment
3039  $Line = trim($Line);
3040  if (!preg_match("/^#/", $Line))
3041  {
3042  # if file has not already been loaded
3043  if (!in_array($Line, $this->FoundUIFiles))
3044  {
3045  # add to list of required files
3046  $RequiredFiles[] = $Line;
3047  }
3048  }
3049  }
3050  }
3051  }
3052 
3053  # add in additional required files if any
3054  if (count($this->AdditionalRequiredUIFiles))
3055  {
3056  # make sure there are no duplicates
3057  $AdditionalRequiredUIFiles = array_unique(
3058  $this->AdditionalRequiredUIFiles);
3059 
3060  $RequiredFiles = array_merge(
3061  $RequiredFiles, $AdditionalRequiredUIFiles);
3062  }
3063 
3064  # return list of required files to caller
3065  return $RequiredFiles;
3066  }
3067 
3071  private function SetUpObjectAutoloading()
3072  {
3074  function __autoload($ClassName)
3075  {
3077  }
3078  }
3079 
3085  static function AutoloadObjects($ClassName)
3086  {
3087  # if caching is not turned off
3088  # and we have a cached location for class
3089  # and cache expiration has not elapsed
3090  # and file at cached location is readable
3091  if ((self::$ObjectLocationCacheInterval > 0)
3092  && count(self::$ObjectLocationCache)
3093  && array_key_exists($ClassName,
3094  self::$ObjectLocationCache)
3095  && (time() < strtotime(
3096  self::$ObjectLocationCacheExpiration))
3097  && is_readable(self::$ObjectLocationCache[$ClassName]))
3098  {
3099  # use object location from cache
3100  require_once(self::$ObjectLocationCache[$ClassName]);
3101  }
3102  else
3103  {
3104  # for each possible object file directory
3105  static $FileLists;
3106  foreach (self::$ObjectDirectories as $Location => $Info)
3107  {
3108  # if directory looks valid
3109  if (is_dir($Location))
3110  {
3111  # build class file name
3112  $NewClassName = ($Info["ClassPattern"] && $Info["ClassReplacement"])
3113  ? preg_replace($Info["ClassPattern"],
3114  $Info["ClassReplacement"], $ClassName)
3115  : $ClassName;
3116 
3117  # read in directory contents if not already retrieved
3118  if (!isset($FileLists[$Location]))
3119  {
3120  $FileLists[$Location] = self::ReadDirectoryTree(
3121  $Location, '/^.+\.php$/i');
3122  }
3123 
3124  # for each file in target directory
3125  $FileNames = $FileLists[$Location];
3126  $TargetName = strtolower($Info["Prefix"].$NewClassName.".php");
3127  foreach ($FileNames as $FileName)
3128  {
3129  # if file matches our target object file name
3130  if (strtolower($FileName) == $TargetName)
3131  {
3132  # include object file
3133  require_once($Location.$FileName);
3134 
3135  # save location to cache
3136  self::$ObjectLocationCache[$ClassName]
3137  = $Location.$FileName;
3138 
3139  # set flag indicating that cache should be saved
3140  self::$SaveObjectLocationCache = TRUE;
3141 
3142  # stop looking
3143  break 2;
3144  }
3145  }
3146  }
3147  }
3148  }
3149  }
3159  private static function ReadDirectoryTree($Directory, $Pattern)
3160  {
3161  $CurrentDir = getcwd();
3162  chdir($Directory);
3163  $DirIter = new RecursiveDirectoryIterator(".");
3164  $IterIter = new RecursiveIteratorIterator($DirIter);
3165  $RegexResults = new RegexIterator($IterIter, $Pattern,
3166  RecursiveRegexIterator::GET_MATCH);
3167  $FileList = array();
3168  foreach ($RegexResults as $Result)
3169  {
3170  $FileList[] = substr($Result[0], 2);
3171  }
3172  chdir($CurrentDir);
3173  return $FileList;
3174  }
3175 
3179  private function UndoMagicQuotes()
3180  {
3181  # if this PHP version has magic quotes support
3182  if (version_compare(PHP_VERSION, "5.4.0", "<"))
3183  {
3184  # turn off runtime magic quotes if on
3185  if (get_magic_quotes_runtime())
3186  {
3187  // @codingStandardsIgnoreStart
3188  set_magic_quotes_runtime(FALSE);
3189  // @codingStandardsIgnoreEnd
3190  }
3191 
3192  # if magic quotes GPC is on
3193  if (get_magic_quotes_gpc())
3194  {
3195  # strip added slashes from incoming variables
3196  $GPC = array(&$_GET, &$_POST, &$_COOKIE, &$_REQUEST);
3197  array_walk_recursive($GPC,
3198  array($this, "UndoMagicQuotes_StripCallback"));
3199  }
3200  }
3201  }
3202  private function UndoMagicQuotes_StripCallback(&$Value)
3203  {
3204  $Value = stripslashes($Value);
3205  }
3206 
3211  private function LoadUIFunctions()
3212  {
3213  $Dirs = array(
3214  "local/interface/%ACTIVEUI%/include",
3215  "interface/%ACTIVEUI%/include",
3216  "local/interface/default/include",
3217  "interface/default/include",
3218  );
3219  foreach ($Dirs as $Dir)
3220  {
3221  $Dir = str_replace("%ACTIVEUI%", $this->ActiveUI, $Dir);
3222  if (is_dir($Dir))
3223  {
3224  $FileNames = scandir($Dir);
3225  foreach ($FileNames as $FileName)
3226  {
3227  if (preg_match("/^F-([A-Za-z0-9_]+)\.php/", $FileName, $Matches)
3228  || preg_match("/^F-([A-Za-z0-9_]+)\.html/", $FileName, $Matches))
3229  {
3230  if (!function_exists($Matches[1]))
3231  {
3232  include_once($Dir."/".$FileName);
3233  }
3234  }
3235  }
3236  }
3237  }
3238  }
3239 
3245  private function ProcessPeriodicEvent($EventName, $Callback)
3246  {
3247  # retrieve last execution time for event if available
3248  $Signature = self::GetCallbackSignature($Callback);
3249  $LastRun = $this->DB->Query("SELECT LastRunAt FROM PeriodicEvents"
3250  ." WHERE Signature = '".addslashes($Signature)."'", "LastRunAt");
3251 
3252  # determine whether enough time has passed for event to execute
3253  $ShouldExecute = (($LastRun === NULL)
3254  || (time() > (strtotime($LastRun) + $this->EventPeriods[$EventName])))
3255  ? TRUE : FALSE;
3256 
3257  # if event should run
3258  if ($ShouldExecute)
3259  {
3260  # add event to task queue
3261  $WrapperCallback = array("ApplicationFramework", "PeriodicEventWrapper");
3262  $WrapperParameters = array(
3263  $EventName, $Callback, array("LastRunAt" => $LastRun));
3264  $this->QueueUniqueTask($WrapperCallback, $WrapperParameters);
3265  }
3266 
3267  # add event to list of periodic events
3268  $this->KnownPeriodicEvents[$Signature] = array(
3269  "Period" => $EventName,
3270  "Callback" => $Callback,
3271  "Queued" => $ShouldExecute);
3272  }
3273 
3281  private static function PeriodicEventWrapper($EventName, $Callback, $Parameters)
3282  {
3283  static $DB;
3284  if (!isset($DB)) { $DB = new Database(); }
3285 
3286  # run event
3287  $ReturnVal = call_user_func_array($Callback, $Parameters);
3288 
3289  # if event is already in database
3290  $Signature = self::GetCallbackSignature($Callback);
3291  if ($DB->Query("SELECT COUNT(*) AS EventCount FROM PeriodicEvents"
3292  ." WHERE Signature = '".addslashes($Signature)."'", "EventCount"))
3293  {
3294  # update last run time for event
3295  $DB->Query("UPDATE PeriodicEvents SET LastRunAt = "
3296  .(($EventName == "EVENT_PERIODIC")
3297  ? "'".date("Y-m-d H:i:s", time() + ($ReturnVal * 60))."'"
3298  : "NOW()")
3299  ." WHERE Signature = '".addslashes($Signature)."'");
3300  }
3301  else
3302  {
3303  # add last run time for event to database
3304  $DB->Query("INSERT INTO PeriodicEvents (Signature, LastRunAt) VALUES "
3305  ."('".addslashes($Signature)."', "
3306  .(($EventName == "EVENT_PERIODIC")
3307  ? "'".date("Y-m-d H:i:s", time() + ($ReturnVal * 60))."'"
3308  : "NOW()").")");
3309  }
3310  }
3311 
3317  private static function GetCallbackSignature($Callback)
3318  {
3319  return !is_array($Callback) ? $Callback
3320  : (is_object($Callback[0]) ? md5(serialize($Callback[0])) : $Callback[0])
3321  ."::".$Callback[1];
3322  }
3323 
3328  private function PrepForTSR()
3329  {
3330  # if HTML has been output and it's time to launch another task
3331  # (only TSR if HTML has been output because otherwise browsers
3332  # may misbehave after connection is closed)
3333  if (($this->JumpToPage || !$this->SuppressHTML)
3334  && (time() > (strtotime($this->Settings["LastTaskRunAt"])
3335  + (ini_get("max_execution_time")
3336  / $this->Settings["MaxTasksRunning"]) + 5))
3337  && $this->GetTaskQueueSize()
3338  && $this->Settings["TaskExecutionEnabled"])
3339  {
3340  # begin buffering output for TSR
3341  ob_start();
3342 
3343  # let caller know it is time to launch another task
3344  return TRUE;
3345  }
3346  else
3347  {
3348  # let caller know it is not time to launch another task
3349  return FALSE;
3350  }
3351  }
3352 
3357  private function LaunchTSR()
3358  {
3359  # set headers to close out connection to browser
3360  if (!$this->NoTSR)
3361  {
3362  ignore_user_abort(TRUE);
3363  header("Connection: close");
3364  header("Content-Length: ".ob_get_length());
3365  }
3366 
3367  # output buffered content
3368  while (ob_get_level()) { ob_end_flush(); }
3369  flush();
3370 
3371  # write out any outstanding data and end HTTP session
3372  session_write_close();
3373 
3374  # set flag indicating that we are now running in background
3375  $this->RunningInBackground = TRUE;
3376 
3377  # if there is still a task in the queue
3378  if ($this->GetTaskQueueSize())
3379  {
3380  # turn on output buffering to (hopefully) record any crash output
3381  ob_start();
3382 
3383  # lock tables and grab last task run time to double check
3384  $this->DB->Query("LOCK TABLES ApplicationFrameworkSettings WRITE");
3385  $this->LoadSettings();
3386 
3387  # if still time to launch another task
3388  if (time() > (strtotime($this->Settings["LastTaskRunAt"])
3389  + (ini_get("max_execution_time")
3390  / $this->Settings["MaxTasksRunning"]) + 5))
3391  {
3392  # update the "last run" time and release tables
3393  $this->DB->Query("UPDATE ApplicationFrameworkSettings"
3394  ." SET LastTaskRunAt = '".date("Y-m-d H:i:s")."'");
3395  $this->DB->Query("UNLOCK TABLES");
3396 
3397  # run tasks while there is a task in the queue and enough time left
3398  do
3399  {
3400  # run the next task
3401  $this->RunNextTask();
3402  }
3403  while ($this->GetTaskQueueSize()
3404  && ($this->GetSecondsBeforeTimeout() > 65));
3405  }
3406  else
3407  {
3408  # release tables
3409  $this->DB->Query("UNLOCK TABLES");
3410  }
3411  }
3412  }
3413 
3421  private function GetTaskList($DBQuery, $Count, $Offset)
3422  {
3423  $this->DB->Query($DBQuery." LIMIT ".intval($Offset).",".intval($Count));
3424  $Tasks = array();
3425  while ($Row = $this->DB->FetchRow())
3426  {
3427  $Tasks[$Row["TaskId"]] = $Row;
3428  if ($Row["Callback"] ==
3429  serialize(array("ApplicationFramework", "PeriodicEventWrapper")))
3430  {
3431  $WrappedCallback = unserialize($Row["Parameters"]);
3432  $Tasks[$Row["TaskId"]]["Callback"] = $WrappedCallback[1];
3433  $Tasks[$Row["TaskId"]]["Parameters"] = NULL;
3434  }
3435  else
3436  {
3437  $Tasks[$Row["TaskId"]]["Callback"] = unserialize($Row["Callback"]);
3438  $Tasks[$Row["TaskId"]]["Parameters"] = unserialize($Row["Parameters"]);
3439  }
3440  }
3441  return $Tasks;
3442  }
3443 
3447  private function RunNextTask()
3448  {
3449  # lock tables to prevent same task from being run by multiple sessions
3450  $this->DB->Query("LOCK TABLES TaskQueue WRITE, RunningTasks WRITE");
3451 
3452  # look for task at head of queue
3453  $this->DB->Query("SELECT * FROM TaskQueue ORDER BY Priority, TaskId LIMIT 1");
3454  $Task = $this->DB->FetchRow();
3455 
3456  # if there was a task available
3457  if ($Task)
3458  {
3459  # move task from queue to running tasks list
3460  $this->DB->Query("INSERT INTO RunningTasks "
3461  ."(TaskId,Callback,Parameters,Priority,Description) "
3462  ."SELECT * FROM TaskQueue WHERE TaskId = "
3463  .intval($Task["TaskId"]));
3464  $this->DB->Query("DELETE FROM TaskQueue WHERE TaskId = "
3465  .intval($Task["TaskId"]));
3466 
3467  # release table locks to again allow other sessions to run tasks
3468  $this->DB->Query("UNLOCK TABLES");
3469 
3470  # unpack stored task info
3471  $Callback = unserialize($Task["Callback"]);
3472  $Parameters = unserialize($Task["Parameters"]);
3473 
3474  # attempt to load task callback if not already available
3475  $this->LoadFunction($Callback);
3476 
3477  # run task
3478  $this->RunningTask = $Task;
3479  if ($Parameters)
3480  {
3481  call_user_func_array($Callback, $Parameters);
3482  }
3483  else
3484  {
3485  call_user_func($Callback);
3486  }
3487  unset($this->RunningTask);
3488 
3489  # remove task from running tasks list
3490  $this->DB->Query("DELETE FROM RunningTasks"
3491  ." WHERE TaskId = ".intval($Task["TaskId"]));
3492 
3493  # prune running tasks list if necessary
3494  $RunningTasksCount = $this->DB->Query(
3495  "SELECT COUNT(*) AS TaskCount FROM RunningTasks", "TaskCount");
3496  if ($RunningTasksCount > $this->MaxRunningTasksToTrack)
3497  {
3498  $this->DB->Query("DELETE FROM RunningTasks ORDER BY StartedAt"
3499  ." LIMIT ".($RunningTasksCount - $this->MaxRunningTasksToTrack));
3500  }
3501  }
3502  else
3503  {
3504  # release table locks to again allow other sessions to run tasks
3505  $this->DB->Query("UNLOCK TABLES");
3506  }
3507  }
3508 
3514  function OnCrash()
3515  {
3516  # attempt to remove any memory limits
3517  ini_set("memory_limit", -1);
3518 
3519  # if there is a background task currently running
3520  if (isset($this->RunningTask))
3521  {
3522  # add info about error that caused crash (if available)
3523  if (function_exists("error_get_last"))
3524  {
3525  $CrashInfo["LastError"] = error_get_last();
3526  }
3527 
3528  # add info about current output buffer contents (if available)
3529  if (ob_get_length() !== FALSE)
3530  {
3531  $CrashInfo["OutputBuffer"] = ob_get_contents();
3532  }
3533 
3534  # if backtrace info is available for the crash
3535  $Backtrace = debug_backtrace();
3536  if (count($Backtrace) > 1)
3537  {
3538  # discard the current context from the backtrace
3539  array_shift($Backtrace);
3540 
3541  # add the backtrace to the crash info
3542  $CrashInfo["Backtrace"] = $Backtrace;
3543  }
3544  # else if saved backtrace info is available
3545  elseif (isset($this->SavedContext))
3546  {
3547  # add the saved backtrace to the crash info
3548  $CrashInfo["Backtrace"] = $this->SavedContext;
3549  }
3550 
3551  # if we have crash info to recod
3552  if (isset($CrashInfo))
3553  {
3554  # save crash info for currently running task
3555  $DB = new Database();
3556  $DB->Query("UPDATE RunningTasks SET CrashInfo = '"
3557  .addslashes(serialize($CrashInfo))
3558  ."' WHERE TaskId = ".intval($this->RunningTask["TaskId"]));
3559  }
3560  }
3561 
3562  print("\n");
3563  return;
3564  }
3565 
3582  private function AddToDirList($DirList, $Dir, $SearchLast, $SkipSlashCheck)
3583  {
3584  # convert incoming directory to array of directories (if needed)
3585  $Dirs = is_array($Dir) ? $Dir : array($Dir);
3586 
3587  # reverse array so directories are searched in specified order
3588  $Dirs = array_reverse($Dirs);
3589 
3590  # for each directory
3591  foreach ($Dirs as $Location)
3592  {
3593  # make sure directory includes trailing slash
3594  if (!$SkipSlashCheck)
3595  {
3596  $Location = $Location
3597  .((substr($Location, -1) != "/") ? "/" : "");
3598  }
3599 
3600  # remove directory from list if already present
3601  if (in_array($Location, $DirList))
3602  {
3603  $DirList = array_diff(
3604  $DirList, array($Location));
3605  }
3606 
3607  # add directory to list of directories
3608  if ($SearchLast)
3609  {
3610  array_push($DirList, $Location);
3611  }
3612  else
3613  {
3614  array_unshift($DirList, $Location);
3615  }
3616  }
3617 
3618  # return updated directory list to caller
3619  return $DirList;
3620  }
3621 
3629  private function ArrayPermutations($Items, $Perms = array())
3630  {
3631  if (empty($Items))
3632  {
3633  $Result = array($Perms);
3634  }
3635  else
3636  {
3637  $Result = array();
3638  for ($Index = count($Items) - 1; $Index >= 0; --$Index)
3639  {
3640  $NewItems = $Items;
3641  $NewPerms = $Perms;
3642  list($Segment) = array_splice($NewItems, $Index, 1);
3643  array_unshift($NewPerms, $Segment);
3644  $Result = array_merge($Result,
3645  $this->ArrayPermutations($NewItems, $NewPerms));
3646  }
3647  }
3648  return $Result;
3649  }
3650 
3657  private function OutputModificationCallbackShell($Matches)
3658  {
3659  # call previously-stored external function
3660  return call_user_func($this->OutputModificationCallbackInfo["Callback"],
3661  $Matches,
3662  $this->OutputModificationCallbackInfo["Pattern"],
3663  $this->OutputModificationCallbackInfo["Page"],
3664  $this->OutputModificationCallbackInfo["SearchPattern"]);
3665  }
3666 
3673  function UpdateSetting($FieldName, $NewValue = DB_NOVALUE)
3674  {
3675  return $this->DB->UpdateValue("ApplicationFrameworkSettings",
3676  $FieldName, $NewValue, NULL, $this->Settings);
3677  }
3678 
3680  private $InterfaceDirList = array(
3681  "local/interface/%ACTIVEUI%/",
3682  "interface/%ACTIVEUI%/",
3683  "local/interface/default/",
3684  "interface/default/",
3685  );
3690  private $IncludeDirList = array(
3691  "local/interface/%ACTIVEUI%/include/",
3692  "interface/%ACTIVEUI%/include/",
3693  "local/interface/default/include/",
3694  "interface/default/include/",
3695  );
3697  private $ImageDirList = array(
3698  "local/interface/%ACTIVEUI%/images/",
3699  "interface/%ACTIVEUI%/images/",
3700  "local/interface/default/images/",
3701  "interface/default/images/",
3702  );
3704  private $FunctionDirList = array(
3705  "local/interface/%ACTIVEUI%/include/",
3706  "interface/%ACTIVEUI%/include/",
3707  "local/interface/default/include/",
3708  "interface/default/include/",
3709  "local/include/",
3710  "include/",
3711  );
3712 
3713  const NOVALUE = ".-+-.NO VALUE PASSED IN FOR ARGUMENT.-+-.";
3714 }
3715 
3716 
MaxTasks($NewValue=DB_NOVALUE)
Get/set maximum number of tasks to have running simultaneously.
const LOGLVL_ERROR
ERROR error logging level.
GetOrphanedTaskList($Count=100, $Offset=0)
Retrieve list of tasks currently in queue.
SuppressHTMLOutput($NewSetting=TRUE)
Suppress loading of HTML files.
AddInterfaceDirectories($Dir, $SearchLast=FALSE, $SkipSlashCheck=FALSE)
Add additional directory(s) to be searched for user interface (HTML/TPL) files.
AddIncludeDirectories($Dir, $SearchLast=FALSE, $SkipSlashCheck=FALSE)
Add additional directory(s) to be searched for user interface include (CSS, JavaScript, common PHP, common HTML, etc) files.
static GetScriptUrl()
Retrieve SCRIPT_URL server value, pulling it from elsewhere if that variable isn't set...
const LOGLVL_INFO
INFO error logging level.
AddUnbufferedCallback($Callback, $Parameters=array())
Add a callback that will be executed after buffered content has been output and that won't have its o...
QueueUniqueTask($Callback, $Parameters=NULL, $Priority=self::PRIORITY_LOW, $Description="")
Add task to queue if not already in queue or currently running.
const LOGLVL_FATAL
FATAL error logging level.
UseMinimizedJavascript($NewSetting=NULL)
Get/set whether or not to check for and use minimized JavaScript files when getting a JavaScript UI f...
AddPostProcessingCall($FunctionName, &$Arg1=self::NOVALUE, &$Arg2=self::NOVALUE, &$Arg3=self::NOVALUE, &$Arg4=self::NOVALUE, &$Arg5=self::NOVALUE, &$Arg6=self::NOVALUE, &$Arg7=self::NOVALUE, &$Arg8=self::NOVALUE, &$Arg9=self::NOVALUE)
Add function to be called after HTML has been loaded.
GetCleanUrlForPath($Path)
Get the clean URL mapped for a path.
const PRIORITY_LOW
Lower priority.
Abstraction for forum messages and resource comments.
Definition: Message.php:15
GetQueuedTaskList($Count=100, $Offset=0)
Retrieve list of tasks currently in queue.
LogFile($NewValue=NULL)
Get/set log file name.
GetCleanUrl()
Get the clean URL for the current page if one is available.
MaxExecutionTime($NewValue=NULL)
Get/set maximum PHP execution time.
static PreferHttpHost($NewValue=NULL)
Get/set whether to prefer $_SERVER["HTTP_HOST"] (if available) over $_SERVER["SERVER_NAME"] when dete...
RequireUIFile($FileName)
Add file to list of required UI files.
static FullUrl()
Get current full URL, before any clean URL remapping and with any query string (e.g.
Top-level framework for web applications.
static BaseUrl()
Get current base URL (the part before index.php) (e.g.
GetOrphanedTaskCount()
Retrieve current number of orphaned tasks.
SQL database abstraction object with smart query caching.
LogSlowPageLoads($NewValue=DB_NOVALUE)
Get/set whether logging of long page load times is enabled.
SlowPageLoadThreshold($NewValue=DB_NOVALUE)
Get/set how long a page load can take before it should be considered "slow" and may be logged...
GetTaskQueueSize($Priority=NULL)
Retrieve current number of tasks in queue.
const DB_NOVALUE
static RootUrlOverride($NewValue=self::NOVALUE)
Get/set root URL override.
GetQueuedTaskCount($Callback=NULL, $Parameters=NULL, $Priority=NULL, $Description=NULL)
Get number of queued tasks that match supplied values.
static AutoloadObjects($ClassName)
DeleteTask($TaskId)
Remove task from task queues.
const LOGLVL_DEBUG
DEBUG error logging leve.
const EVENTTYPE_NAMED
Named result event type.
const EVENTTYPE_FIRST
First response event type.
static WasUrlRewritten($ScriptName="index.php")
Determine if the URL was rewritten, i.e., the script is being accessed through a URL that isn't direc...
const EVENTTYPE_DEFAULT
Default event type.
GetPageUrl()
Get the full URL to the page.
IsStaticOnlyEvent($EventName)
Report whether specified event only allows static callbacks.
IsRegisteredEvent($EventName)
Check if event has been registered (is available to be signaled).
static AddObjectDirectory($Dir, $Prefix="", $ClassPattern=NULL, $ClassReplacement=NULL)
Add directory to be searched for object files when autoloading.
SignalEvent($EventName, $Parameters=NULL)
Signal that an event has occured.
static BasePath()
Get current base path (usually the part after the host name).
const LOGLVL_TRACE
TRACE error logging level.
const LOGLVL_WARNING
WARNING error logging level.
const PRIORITY_MEDIUM
Medium (default) priority.
LogError($Level, $Msg)
Write error message to log.
OnCrash()
Called automatically at program termination to ensure output is written out.
GetKnownPeriodicEvents()
Get list of known periodic events.
const EVENTTYPE_CHAIN
Result chaining event type.
SCSS compiler written in PHP.
Definition: scssc.php:45
GetSecondsBeforeTimeout()
Get remaining available (PHP) execution time.
TaskIsInQueue($Callback, $Parameters=NULL)
Check if task is already in queue or currently running.
AddFunctionDirectories($Dir, $SearchLast=FALSE, $SkipSlashCheck=FALSE)
Add additional directory(s) to be searched for function ("F-") files.
LoggingLevel($NewValue=DB_NOVALUE)
Get/set logging level.
const ORDER_MIDDLE
Run hooked function after ORDER_FIRST and before ORDER_LAST events.
PHP
Definition: OAIClient.php:39
RegisterEvent($EventsOrEventName, $EventType=NULL)
Register one or more events that may be signaled.
SetJumpToPage($Page, $IsLiteral=FALSE)
Set URL of page to autoload after PHP page file is executed.
GetPageName()
Get name of page being loaded.
CleanUrlIsMapped($Path)
Report whether clean URL has already been mapped.
const PRIORITY_HIGH
Highest priority.
LoadFunction($Callback)
Attempt to load code for function or method if not currently available.
GetRunningTaskList($Count=100, $Offset=0)
Retrieve list of tasks currently in queue.
FindCommonTemplate($BaseName)
Preserved for backward compatibility for use with code written prior to October 2012.
EventWillNextRunAt($EventName, $Callback)
Get date/time a periodic event will next run.
static SessionLifetime($NewValue=NULL)
Get/set session timeout in seconds.
HookEvent($EventsOrEventName, $Callback=NULL, $Order=self::ORDER_MIDDLE)
Hook one or more functions to be called when the specified event is signaled.
IsHookedEvent($EventName)
Check if an event is registered and is hooked to.
static GetFreeMemory()
Get current amount of free memory.
AddImageDirectories($Dir, $SearchLast=FALSE, $SkipSlashCheck=FALSE)
Add additional directory(s) to be searched for image files.
HtmlCharset($NewSetting=NULL)
Get/set HTTP character encoding value.
const ORDER_FIRST
Run hooked function first (i.e.
GetUncleanUrlForPath($Path)
Get the unclean URL for mapped for a path.
TemplateLocationCacheExpirationInterval($NewInterval=DB_NOVALUE)
Get/set UI template location cache expiration period in minutes.
GetUncleanUrl()
Get the unclean URL for the current page.
RecordContextInCaseOfCrash($BacktraceOptions=0, $BacktraceLimit=0)
Record the current execution context in case of crash.
JumpToPageIsSet()
Report whether a page to autoload has been set.
const ORDER_LAST
Run hooked function last (i.e.
UseBaseTag($NewValue=NULL)
Get/set whether or not to use the "base" tag to ensure relative URL paths are correct.
static HtaccessSupport()
Determine if .htaccess files are enabled.
UpdateSetting($FieldName, $NewValue=DB_NOVALUE)
Convenience function for getting/setting our settings.
GetTask($TaskId)
Retrieve task info from queue (either running or queued tasks).
TaskExecutionEnabled($NewValue=DB_NOVALUE)
Get/set whether automatic task execution is enabled.
static GetPhpMemoryLimit()
Get PHP memory limit in bytes.
LoadPage($PageName)
Load page PHP and HTML/TPL files.
AddEnvInclude($FileName)
Add file to be included to set up environment.
ReQueueOrphanedTask($TaskId, $NewPriority=NULL)
Move orphaned task back into queue.
GUIFile($FileName)
Search UI directories for specified image or CSS file and return name of correct file.
QueueTask($Callback, $Parameters=NULL, $Priority=self::PRIORITY_LOW, $Description="")
Add task to queue.
GetUserInterfaces()
Get the list of available user interfaces.
PUIFile($FileName)
Search UI directories for specified image or CSS file and print name of correct file.
LogHighMemoryUsage($NewValue=DB_NOVALUE)
Get/set whether logging of high memory usage is enabled.
ActiveUserInterface($UIName=NULL)
Get/set name of current active user interface.
GetPageLocation()
Get the URL path to the page without the base path, if present.
ObjectLocationCacheExpirationInterval($NewInterval=DB_NOVALUE)
Get/set object file location cache expiration period in minutes.
GetElapsedExecutionTime()
Get time elapsed since constructor was called.
SetBrowserDetectionFunc($DetectionFunc)
Specify function to use to detect the web browser type.
static RootUrl()
Get portion of current URL through host name, with no trailing slash (e.g.
AddCleanUrl($Pattern, $Page, $GetVars=NULL, $Template=NULL)
Add clean URL mapping.
HighMemoryUsageThreshold($NewValue=DB_NOVALUE)
Get/set what percentage of max memory (set via the memory_limit PHP configuration directive) a page l...
const PRIORITY_BACKGROUND
Lowest priority.
LogMessage($Level, $Msg)
Write status message to log.