CWIS Developer Documentation

- Home
- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

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-2016 Edward Almasy and Internet Scout Research Group
7 # http://scout.wisc.edu
8 #
9
14 class ApplicationFramework
15 {
16
17 # ---- PUBLIC INTERFACE --------------------------------------------------
18 /*@(*/
20
25 public function __construct()
26 {
27 # set up a class alias for convenience
28 class_alias("ApplicationFramework", "AF");
29
30 # adjust environment in case we are being run via CGI
31 $this->AdjustEnvironmentForCgi();
32
33 # make sure default time zone is set
34 # (using CST if nothing set because we have to use something
35 # and Scout is based in Madison, WI, which is in CST)
36 if ((ini_get("date.timezone") === NULL)
37 || !strlen(ini_get("date.timezone")))
38 {
39 ini_set("date.timezone", "America/Chicago");
40 }
41
42 # save execution start time
43 $this->ExecutionStartTime = microtime(TRUE);
44
45 # set up default object file search locations
46 self::AddObjectDirectory("local/interface/%ACTIVEUI%/objects");
47 self::AddObjectDirectory("interface/%ACTIVEUI%/objects");
48 self::AddObjectDirectory("local/interface/%DEFAULTUI%/objects");
49 self::AddObjectDirectory("interface/%DEFAULTUI%/objects");
50 self::AddObjectDirectory("local/objects");
51 self::AddObjectDirectory("objects");
52
53 # set up object file autoloader
54 spl_autoload_register(array($this, "AutoloadObjects"));
55
56 # set up function to output any buffered text in case of crash
57 register_shutdown_function(array($this, "OnCrash"));
58
59 # if we were not invoked via command line interface
60 # and session initialization has not been explicitly suppressed
61 if ((php_sapi_name() !== "cli") && (!self::$SuppressSessionInitialization))
62 {
63 # build cookie domain string
64 $SessionDomain = isset($_SERVER["SERVER_NAME"]) ? $_SERVER["SERVER_NAME"]
65 : isset($_SERVER["HTTP_HOST"]) ? $_SERVER["HTTP_HOST"]
66 : php_uname("n");
67
68 # include a leading period so that older browsers implementing
69 # rfc2109 do not reject our cookie
70 $SessionDomain = ".".$SessionDomain;
71
72 # if it appears our session storage area is writable
73 if (is_writable(session_save_path()))
74 {
75 # store our session files in a subdirectory to avoid
76 # accidentally sharing sessions with other installations
77 # on the same domain
78 $SessionStorage = session_save_path()
79 ."/".self::$AppName."_".md5($SessionDomain.dirname(__FILE__));
80
81 # create session storage subdirectory if not found
82 if (!is_dir($SessionStorage)) { mkdir($SessionStorage, 0700 ); }
83
84 # if session storage subdirectory is writable
85 if (is_writable($SessionStorage))
86 {
87 # save parameters of our session storage as instance variables
88 # for later use
89 $this->SessionGcProbability =
90 ini_get("session.gc_probability") / ini_get("session.gc_divisor");
91 # require a gc probability of at least MIN_GC_PROBABILITY
92 if ($this->SessionGcProbability < self::MIN_GC_PROBABILITY)
93 {
94 $this->SessionGcProbability = self::MIN_GC_PROBABILITY;
95 }
96
97 $this->SessionStorage = $SessionStorage;
98
99 # set the new session storage location
100 session_save_path($SessionStorage);
101
102 # disable PHP's garbage collection, as it does not handle
103 # subdirectories (instead, we'll do the cleanup as we run
104 # background tasks)
105 ini_set("session.gc_probability", 0);
106 }
107 }
108
109 # set garbage collection max period to our session lifetime
110 ini_set("session.gc_maxlifetime", self::$SessionLifetime);
111
112 # limit cookie to secure connection if we are running over same
113 $SecureCookie = isset($_SERVER["HTTPS"]) ? TRUE : FALSE;
114
115 # Cookies lacking embedded dots are... fun.
116 # rfc2109 sec 4.3.2 says to reject them
117 # rfc2965 sec 3.3.2 says to reject them
118 # rfc6265 sec 4.1.2.3 says only that "public suffixes"
119 # should be rejected. They reference Mozilla's
120 # publicsuffix.org, which does not contain 'localhost'.
121 # However, empirically in early 2017 Firefox still rejects
122 # 'localhost'.
123 # Therefore, don't set a cookie domain if we're running on
124 # localhost to avoid this problem.
125 if (preg_match('/^\.localhost(:[0-9]+)?$/', $SessionDomain))
126 {
127 $SessionDomain = "";
128 }
129 session_set_cookie_params(self::$SessionLifetime, "/",
130 $SessionDomain, $SecureCookie, TRUE);
131
132 # attempt to start session
133 $SessionStarted = @session_start();
134
135 # if session start failed
136 if (!$SessionStarted)
137 {
138 # regenerate session ID and attempt to start session again
139 session_regenerate_id(TRUE);
140 session_start();
141 }
142
143 # bump up our cookie expiry time, so that it'll die
144 # $SessionLifetime from now, rather than $SessionLifetime
145 # from whenever we created it
146 setcookie(
147 session_name(), session_id(),
148 time() + self::$SessionLifetime, "/",
149 $SessionDomain, $SecureCookie, TRUE);
150 }
151
152 # set up our internal environment
153 $this->DB = new Database();
154
155 # set up our exception handler
156 set_exception_handler(array($this, "GlobalExceptionHandler"));
157
158 # load our settings from database
159 $this->LoadSettings();
160
161 # set PHP maximum execution time
162 ini_set("max_execution_time", $this->Settings["MaxExecTime"]);
163 set_time_limit($this->Settings["MaxExecTime"]);
164
165 # set database slow query threshold for foreground execution
166 Database::SlowQueryThreshold(max(
167 self::MIN_DB_SLOW_QUERY_THRESHOLD,
168 self::DatabaseSlowQueryThresholdForForeground()));
169
170 # register events we handle internally
171 $this->RegisterEvent($this->PeriodicEvents);
172 $this->RegisterEvent($this->UIEvents);
173
174 # attempt to create SCSS cache directory if needed and it does not exist
175 if ($this->ScssSupportEnabled() && !is_dir(self::$ScssCacheDir))
176 { @mkdir(self::$ScssCacheDir, 0777, TRUE); }
177
178 # attempt to create minimized JS cache directory if needed and it does not exist
179 if ($this->UseMinimizedJavascript()
180 && $this->JavascriptMinimizationEnabled()
181 && !is_dir(self::$JSMinCacheDir))
182 {
183 @mkdir(self::$JSMinCacheDir, 0777, TRUE);
184 }
185
186 # initialize task manager
187 $this->TaskMgr = new AFTaskManager();
188 }
195 public function __destruct()
196 {
197 # if template location cache is flagged to be saved
198 if ($this->SaveTemplateLocationCache)
199 {
200 # write template location cache out and update cache expiration
201 $this->DB->Query("UPDATE ApplicationFrameworkSettings"
202 ." SET TemplateLocationCache = '"
203 .addslashes(serialize(
204 $this->TemplateLocationCache))."',"
205 ." TemplateLocationCacheExpiration = '"
206 .date("Y-m-d H:i:s",
207 $this->TemplateLocationCacheExpiration)."'");
208 }
209
210 # if object location cache is flagged to be saved
211 if (self::$SaveObjectLocationCache)
212 {
213 # write object location cache out and update cache expiration
214 $this->DB->Query("UPDATE ApplicationFrameworkSettings"
215 ." SET ObjectLocationCache = '"
216 .addslashes(serialize(
217 self::$ObjectLocationCache))."',"
218 ." ObjectLocationCacheExpiration = '"
219 .date("Y-m-d H:i:s",
220 self::$ObjectLocationCacheExpiration)."'");
221 }
222 }
230 public function GlobalExceptionHandler($Exception)
231 {
232 # display exception info
233 $Message = $Exception->getMessage();
234 $Location = str_replace(getcwd()."/", "",
235 $Exception->getFile()."[".$Exception->getLine()."]");
236 $Trace = preg_replace(":(#[0-9]+) ".getcwd()."/".":", "$1 ",
237 $Exception->getTraceAsString());
238 if (php_sapi_name() == "cli")
239 {
240 print "Uncaught Exception\n"
241 ."Message: ".$Message."\n"
242 ."Location: ".$Location."\n"
243 ."Trace: \n"
244 .$Trace."\n";
245 }
246 else
247 {
248 ?><table width="100%" cellpadding="5"
249 style="border: 2px solid #666666; background: #CCCCCC;
250 font-family: Courier New, Courier, monospace;
251 margin-top: 10px;"><tr><td>
252 <div style="color: #666666;">
253 <span style="font-size: 150%;">
254 <b>Uncaught Exception</b></span><br />
255 <b>Message:</b> <i><?= $Message ?></i><br />
256 <b>Location:</b> <i><?= $Location ?></i><br />
257 <b>Trace:</b> <blockquote><pre><?= $Trace ?></pre></blockquote>
258 </div>
259 </td></tr></table><?PHP
260 }
261
262 # log exception if not running from command line
263 if (php_sapi_name() !== "cli")
264 {
265 $TraceString = $Exception->getTraceAsString();
266 $TraceString = str_replace("\n", ", ", $TraceString);
267 $TraceString = preg_replace(":(#[0-9]+) ".getcwd()."/".":",
268 "$1 ", $TraceString);
269 $LogMsg = "Uncaught exception (".$Exception->getMessage().")"
270 ." at ".$Location."."
271 ." TRACE: ".$TraceString
272 ." URL: ".$this->FullUrl();
273 $this->LogError(self::LOGLVL_ERROR, $LogMsg);
274 }
275 }
296 public static function AddObjectDirectory(
297 $Dir, $NamespacePrefixes = array(), $Callback = NULL)
298 {
299 # check to make sure any supplied callback looks valid
300 if (!is_null($Callback) && !is_callable($Callback))
301 {
302 throw new InvalidArgumentException("Supplied callback (\""
303 .$Callback."\") is invalid.");
304 }
305
306 # make sure directory has trailing slash
307 $Dir = $Dir.((substr($Dir, -1) != "/") ? "/" : "");
308
309 # make sure namespaces is an array
310 if (!is_array($NamespacePrefixes))
311 {
312 if (is_string($NamespacePrefixes))
313 {
314 $NamespacePrefixes = [ $NamespacePrefixes ];
315 }
316 else
317 {
318 throw new InvalidArgumentException("Supplied namespace (\""
319 .$NamespacePrefixes."\") is invalid.");
320 }
321 }
322
323 # make sure namespace prefixes are in decreasing order of length
324 usort($NamespacePrefixes, function($A, $B)
325 {
326 return strlen($B) - strlen($A);
327 });
328
329 # add directory to directory list
330 self::$ObjectDirectories[$Dir] = array(
331 "NamespacePrefixes" => $NamespacePrefixes,
332 "Callback" => $Callback,
333 );
334 }
335
356 public function AddImageDirectories(
357 $Dir, $SearchLast = FALSE, $SkipSlashCheck = FALSE)
358 {
359 # add directories to existing image directory list
360 $this->ImageDirList = $this->AddToDirList(
361 $this->ImageDirList, $Dir, $SearchLast, $SkipSlashCheck);
362 }
363
385 public function AddIncludeDirectories(
386 $Dir, $SearchLast = FALSE, $SkipSlashCheck = FALSE)
387 {
388 # add directories to existing image directory list
389 $this->IncludeDirList = $this->AddToDirList(
390 $this->IncludeDirList, $Dir, $SearchLast, $SkipSlashCheck);
391 }
392
413 public function AddInterfaceDirectories(
414 $Dir, $SearchLast = FALSE, $SkipSlashCheck = FALSE)
415 {
416 # add directories to existing image directory list
417 $this->InterfaceDirList = $this->AddToDirList(
418 $this->InterfaceDirList, $Dir, $SearchLast, $SkipSlashCheck);
419
420 # cleared cached lists for user interfaces
421 self::$UserInterfaceListCache = array();
422 self::$UserInterfacePathsCache = array();
423 }
424
445 public function AddFunctionDirectories(
446 $Dir, $SearchLast = FALSE, $SkipSlashCheck = FALSE)
447 {
448 # add directories to existing image directory list
449 $this->FunctionDirList = $this->AddToDirList(
450 $this->FunctionDirList, $Dir, $SearchLast, $SkipSlashCheck);
451 }
452
458 public function SetBrowserDetectionFunc($DetectionFunc)
459 {
460 $this->BrowserDetectFunc = $DetectionFunc;
461 }
462
469 public function AddUnbufferedCallback($Callback, $Parameters = array())
470 {
471 if (is_callable($Callback))
472 {
473 $this->UnbufferedCallbacks[] = array($Callback, $Parameters);
474 }
475 }
476
486 public function TemplateLocationCacheExpirationInterval(
487 $NewInterval = DB_NOVALUE, $Persistent = FALSE)
488 {
489 return $this->UpdateSetting(
490 "TemplateLocationCacheInterval", $NewInterval, $Persistent);
491 }
492
496 public function ClearTemplateLocationCache()
497 {
498 $this->TemplateLocationCache = array();
499 $this->SaveTemplateLocationCache = TRUE;
500 }
501
511 public function ObjectLocationCacheExpirationInterval(
512 $NewValue = DB_NOVALUE, $Persistent = FALSE)
513 {
514 return $this->UpdateSetting(
515 "ObjectLocationCacheInterval", $NewValue, $Persistent);
516 }
517
521 public function ClearObjectLocationCache()
522 {
523 self::$ObjectLocationCache = array();
524 self::$SaveObjectLocationCache = TRUE;
525 }
526
536 public function UrlFingerprintingEnabled(
537 $NewValue = DB_NOVALUE, $Persistent = FALSE)
538 {
539 return $this->UpdateSetting(__FUNCTION__, $NewValue, $Persistent);
540 }
541
552 public function ScssSupportEnabled(
553 $NewValue = DB_NOVALUE, $Persistent = FALSE)
554 {
555 return $this->UpdateSetting(__FUNCTION__, $NewValue, $Persistent);
556 }
557
569 public function GenerateCompactCss(
570 $NewValue = DB_NOVALUE, $Persistent = FALSE)
571 {
572 return $this->UpdateSetting(__FUNCTION__, $NewValue, $Persistent);
573 }
574
586 public function UseMinimizedJavascript(
587 $NewValue = DB_NOVALUE, $Persistent = FALSE)
588 {
589 return $this->UpdateSetting(__FUNCTION__, $NewValue, $Persistent);
590 }
591
603 public function JavascriptMinimizationEnabled(
604 $NewValue = DB_NOVALUE, $Persistent = FALSE)
605 {
606 return $this->UpdateSetting(__FUNCTION__, $NewValue, $Persistent);
607 }
608
622 public function RecordContextInCaseOfCrash(
623 $BacktraceOptions = 0, $BacktraceLimit = 0)
624 {
625 if (version_compare(PHP_VERSION, "5.4.0", ">="))
626 {
627 $this->SavedContext = debug_backtrace(
628 $BacktraceOptions, $BacktraceLimit);
629 }
630 else
631 {
632 $this->SavedContext = debug_backtrace($BacktraceOptions);
633 }
634 array_shift($this->SavedContext);
635 }
636
641 public function LoadPage($PageName)
642 {
643 # perform any clean URL rewriting
644 $PageName = $this->RewriteCleanUrls($PageName);
645
646 # sanitize incoming page name and save local copy
647 $PageName = preg_replace("/[^a-zA-Z0-9_.-]/", "", $PageName);
648 $this->PageName = $PageName;
649
650 # if page caching is turned on
651 if ($this->PageCacheEnabled())
652 {
653 # if we have a cached page
654 $CachedPage = $this->CheckForCachedPage($PageName);
655 if ($CachedPage !== NULL)
656 {
657 # set header to indicate cache hit was found
658 header("X-ScoutAF-Cache: HIT");
659
660 # display cached page and exit
661 print $CachedPage;
662 return;
663 }
664 else
665 {
666 # set header to indicate no cache hit was found
667 header("X-ScoutAF-Cache: MISS");
668 }
669 }
670
671 # buffer any output from includes or PHP file
672 ob_start();
673
674 # include any files needed to set up execution environment
675 $IncludeFileContext = array();
676 foreach ($this->EnvIncludes as $IncludeFile)
677 {
678 $IncludeFileContext = $this->FilterContext(self::CONTEXT_ENV,
679 self::IncludeFile($IncludeFile, $IncludeFileContext));
680 }
681
682 # signal page load
683 $this->SignalEvent("EVENT_PAGE_LOAD", array("PageName" => $PageName));
684
685 # signal PHP file load
686 $SignalResult = $this->SignalEvent("EVENT_PHP_FILE_LOAD", array(
687 "PageName" => $PageName));
688
689 # if signal handler returned new page name value
690 $NewPageName = $PageName;
691 if (($SignalResult["PageName"] != $PageName)
692 && strlen($SignalResult["PageName"]))
693 {
694 # if new page name value is page file
695 if (file_exists($SignalResult["PageName"]))
696 {
697 # use new value for PHP file name
698 $PageFile = $SignalResult["PageName"];
699 }
700 else
701 {
702 # use new value for page name
703 $NewPageName = $SignalResult["PageName"];
704 }
705
706 # update local copy of page name
707 $this->PageName = $NewPageName;
708 }
709
710 # if we do not already have a PHP file
711 if (!isset($PageFile))
712 {
713 # look for PHP file for page
714 $OurPageFile = "pages/".$NewPageName.".php";
715 $LocalPageFile = "local/pages/".$NewPageName.".php";
716 $PageFile = file_exists($LocalPageFile) ? $LocalPageFile
717 : (file_exists($OurPageFile) ? $OurPageFile
718 : "pages/".$this->DefaultPage.".php");
719 }
720
721 # load PHP file
722 $IncludeFileContext = $this->FilterContext(self::CONTEXT_PAGE,
723 self::IncludeFile($PageFile, $IncludeFileContext));
724
725 # save buffered output to be displayed later after HTML file loads
726 $PageOutput = ob_get_contents();
727 ob_end_clean();
728
729 # signal PHP file load is complete
730 ob_start();
731 $Context["Variables"] = $IncludeFileContext;
732 $this->SignalEvent("EVENT_PHP_FILE_LOAD_COMPLETE",
733 array("PageName" => $PageName, "Context" => $Context));
734 $PageCompleteOutput = ob_get_contents();
735 ob_end_clean();
736
737 # set up for possible TSR (Terminate and Stay Resident :))
738 $ShouldTSR = $this->PrepForTSR();
739
740 # if PHP file indicated we should autorefresh to somewhere else
741 if (($this->JumpToPage) && ($this->JumpToPageDelay == 0))
742 {
743 # if no page output
744 if (!strlen(trim($PageOutput)))
745 {
746 # if client supports HTTP/1.1, use a 303 as it is most accurate
747 if ($_SERVER["SERVER_PROTOCOL"] == "HTTP/1.1")
748 {
749 header("HTTP/1.1 303 See Other");
750 header("Location: ".$this->JumpToPage);
751 }
752 else
753 {
754 # if the request was an HTTP/1.0 GET or HEAD, then
755 # use a 302 response code.
756
757 # NB: both RFC 2616 (HTTP/1.1) and RFC1945 (HTTP/1.0)
758 # explicitly prohibit automatic redirection via a 302
759 # if the request was not GET or HEAD.
760 if ($_SERVER["SERVER_PROTOCOL"] == "HTTP/1.0" &&
761 ($_SERVER["REQUEST_METHOD"] == "GET" ||
762 $_SERVER["REQUEST_METHOD"] == "HEAD") )
763 {
764 header("HTTP/1.0 302 Found");
765 header("Location: ".$this->JumpToPage);
766 }
767
768 # otherwise, fall back to a meta refresh
769 else
770 {
771 print '<html><head><meta http-equiv="refresh" '
772 .'content="0; URL='.$this->JumpToPage.'">'
773 .'</head><body></body></html>';
774 }
775 }
776 }
777 }
778 # else if HTML loading is not suppressed
779 elseif (!$this->SuppressHTML)
780 {
781 # set content-type to avoid diacritic errors
782 header("Content-Type: text/html; charset="
783 .$this->HtmlCharset, TRUE);
784
785 # load common HTML file (defines common functions) if available
786 $CommonHtmlFile = $this->FindFile($this->IncludeDirList,
787 "Common", array("tpl", "html"));
788 if ($CommonHtmlFile)
789 {
790 $IncludeFileContext = $this->FilterContext(self::CONTEXT_COMMON,
791 self::IncludeFile($CommonHtmlFile, $IncludeFileContext));
792 }
793
794 # load UI functions
795 $this->LoadUIFunctions();
796
797 # begin buffering content
798 ob_start();
799
800 # signal HTML file load
801 $SignalResult = $this->SignalEvent("EVENT_HTML_FILE_LOAD", array(
802 "PageName" => $PageName));
803
804 # if signal handler returned new page name value
805 $NewPageName = $PageName;
806 $PageContentFile = NULL;
807 if (($SignalResult["PageName"] != $PageName)
808 && strlen($SignalResult["PageName"]))
809 {
810 # if new page name value is HTML file
811 if (file_exists($SignalResult["PageName"]))
812 {
813 # use new value for HTML file name
814 $PageContentFile = $SignalResult["PageName"];
815 }
816 else
817 {
818 # use new value for page name
819 $NewPageName = $SignalResult["PageName"];
820 }
821 }
822
823 # load page content HTML file if available
824 if ($PageContentFile === NULL)
825 {
826 $PageContentFile = $this->FindFile(
827 $this->InterfaceDirList, $NewPageName,
828 array("tpl", "html"));
829 }
830 if ($PageContentFile)
831 {
832 $IncludeFileContext = $this->FilterContext(self::CONTEXT_INTERFACE,
833 self::IncludeFile($PageContentFile, $IncludeFileContext));
834 }
835 else
836 {
837 print "<h2>ERROR: No HTML/TPL template found"
838 ." for this page (".$NewPageName.").</h2>";
839 $this->DoNotCacheCurrentPage();
840 }
841
842 # signal HTML file load complete
843 $SignalResult = $this->SignalEvent("EVENT_HTML_FILE_LOAD_COMPLETE");
844
845 # stop buffering and save output
846 $PageContentOutput = ob_get_contents();
847 ob_end_clean();
848
849 # if standard page start/end have not been suppressed
850 $PageStartOutput = "";
851 $PageEndOutput = "";
852 if (!$this->SuppressStdPageStartAndEnd)
853 {
854 # load page start HTML file if available
855 $PageStartFile = $this->FindFile($this->IncludeDirList, "Start",
856 array("tpl", "html"), array("StdPage", "StandardPage"));
857 if ($PageStartFile)
858 {
859 ob_start();
860 $IncludeFileContext = self::IncludeFile(
861 $PageStartFile, $IncludeFileContext);
862 $PageStartOutput = ob_get_contents();
863 ob_end_clean();
864 }
865 $IncludeFileContext = $this->FilterContext(
866 self::CONTEXT_START, $IncludeFileContext);
867
868 # load page end HTML file if available
869 $PageEndFile = $this->FindFile($this->IncludeDirList, "End",
870 array("tpl", "html"), array("StdPage", "StandardPage"));
871 if ($PageEndFile)
872 {
873 ob_start();
874 self::IncludeFile($PageEndFile, $IncludeFileContext);
875 $PageEndOutput = ob_get_contents();
876 ob_end_clean();
877 }
878 }
879
880 # clear include file context because it may be large and is no longer needed
881 unset($IncludeFileContext);
882
883 # if page auto-refresh requested
884 if ($this->JumpToPage)
885 {
886 # add auto-refresh tag to page
887 $this->AddMetaTag([
888 "http-equiv" => "refresh",
889 "content" => $this->JumpToPageDelay,
890 "url" => $this->JumpToPage,
891 ]);
892 }
893
894 # assemble full page
895 $FullPageOutput = $PageStartOutput.$PageContentOutput.$PageEndOutput;
896
897 # get list of any required files not loaded
898 $RequiredFiles = $this->GetRequiredFilesNotYetLoaded($PageContentFile);
899
900 # add file loading tags to page
901 $FullPageOutput = $this->AddFileTagsToPageOutput(
902 $FullPageOutput, $RequiredFiles);
903
904 # add any requested meta tags to page
905 $FullPageOutput = $this->AddMetaTagsToPageOutput($FullPageOutput);
906
907 # perform any regular expression replacements in output
908 $NewFullPageOutput = preg_replace($this->OutputModificationPatterns,
909 $this->OutputModificationReplacements, $FullPageOutput);
910
911 # check to make sure replacements didn't fail
912 $FullPageOutput = $this->CheckOutputModification(
913 $FullPageOutput, $NewFullPageOutput,
914 "regular expression replacements");
915
916 # for each registered output modification callback
917 foreach ($this->OutputModificationCallbacks as $Info)
918 {
919 # set up data for callback
920 $this->OutputModificationCallbackInfo = $Info;
921
922 # perform output modification
923 $NewFullPageOutput = preg_replace_callback($Info["SearchPattern"],
924 array($this, "OutputModificationCallbackShell"),
925 $FullPageOutput);
926
927 # check to make sure modification didn't fail
928 $ErrorInfo = "callback info: ".print_r($Info, TRUE);
929 $FullPageOutput = $this->CheckOutputModification(
930 $FullPageOutput, $NewFullPageOutput, $ErrorInfo);
931 }
932
933 # provide the opportunity to modify full page output
934 $SignalResult = $this->SignalEvent("EVENT_PAGE_OUTPUT_FILTER", array(
935 "PageOutput" => $FullPageOutput));
936 if (isset($SignalResult["PageOutput"])
937 && strlen(trim($SignalResult["PageOutput"])))
938 {
939 $FullPageOutput = $SignalResult["PageOutput"];
940 }
941
942 # if relative paths may not work because we were invoked via clean URL
943 if ($this->CleanUrlRewritePerformed || self::WasUrlRewritten())
944 {
945 # if using the <base> tag is okay
946 $BaseUrl = $this->BaseUrl();
947 if ($this->UseBaseTag)
948 {
949 # add <base> tag to header
950 $PageStartOutput = str_replace("<head>",
951 "<head><base href=\"".$BaseUrl."\" />",
952 $PageStartOutput);
953
954 # re-assemble full page with new header
955 $FullPageOutput = $PageStartOutput.$PageContentOutput.$PageEndOutput;
956
957 # the absolute URL to the current page
958 $FullUrl = $BaseUrl . $this->GetPageLocation();
959
960 # make HREF attribute values with just a fragment ID
961 # absolute since they don't work with the <base> tag because
962 # they are relative to the current page/URL, not the site
963 # root
964 $NewFullPageOutput = preg_replace(
965 array("%href=\"(#[^:\" ]+)\"%i", "%href='(#[^:' ]+)'%i"),
966 array("href=\"".$FullUrl."$1\"", "href='".$FullUrl."$1'"),
967 $FullPageOutput);
968
969 # check to make sure HREF cleanup didn't fail
970 $FullPageOutput = $this->CheckOutputModification(
971 $FullPageOutput, $NewFullPageOutput,
972 "HREF cleanup");
973 }
974 else
975 {
976 # try to fix any relative paths throughout code
977 $SrcFileExtensions = "(js|css|gif|png|jpg|svg|ico)";
978 $RelativePathPatterns = array(
979 "%src=\"/?([^?*:;{}\\\\\" ]+)\.".$SrcFileExtensions."\"%i",
980 "%src='/?([^?*:;{}\\\\' ]+)\.".$SrcFileExtensions."'%i",
981 # don't rewrite HREF attributes that are just
982 # fragment IDs because they are relative to the
983 # current page/URL, not the site root
984 "%href=\"/?([^#][^:\" ]*)\"%i",
985 "%href='/?([^#][^:' ]*)'%i",
986 "%action=\"/?([^#][^:\" ]*)\"%i",
987 "%action='/?([^#][^:' ]*)'%i",
988 "%@import\s+url\(\"/?([^:\" ]+)\"\s*\)%i",
989 "%@import\s+url\('/?([^:\" ]+)'\s*\)%i",
990 "%src:\s+url\(\"/?([^:\" ]+)\"\s*\)%i",
991 "%src:\s+url\('/?([^:\" ]+)'\s*\)%i",
992 "%@import\s+\"/?([^:\" ]+)\"\s*%i",
993 "%@import\s+'/?([^:\" ]+)'\s*%i",
994 );
995 $RelativePathReplacements = array(
996 "src=\"".$BaseUrl."$1.$2\"",
997 "src=\"".$BaseUrl."$1.$2\"",
998 "href=\"".$BaseUrl."$1\"",
999 "href=\"".$BaseUrl."$1\"",
1000 "action=\"".$BaseUrl."$1\"",
1001 "action=\"".$BaseUrl."$1\"",
1002 "@import url(\"".$BaseUrl."$1\")",
1003 "@import url('".$BaseUrl."$1')",
1004 "src: url(\"".$BaseUrl."$1\")",
1005 "src: url('".$BaseUrl."$1')",
1006 "@import \"".$BaseUrl."$1\"",
1007 "@import '".$BaseUrl."$1'",
1008 );
1009 $NewFullPageOutput = preg_replace($RelativePathPatterns,
1010 $RelativePathReplacements, $FullPageOutput);
1011
1012 # check to make sure relative path fixes didn't fail
1013 $FullPageOutput = $this->CheckOutputModification(
1014 $FullPageOutput, $NewFullPageOutput,
1015 "relative path fixes");
1016 }
1017 }
1018
1019 # handle any necessary alternate domain rewriting
1020 $FullPageOutput = $this->RewriteAlternateDomainUrls($FullPageOutput);
1021
1022 # update page cache for this page
1023 $this->UpdatePageCache($PageName, $FullPageOutput);
1024
1025 # write out full page
1026 print $FullPageOutput;
1027 }
1028
1029 # run any post-processing routines
1030 foreach ($this->PostProcessingFuncs as $Func)
1031 {
1032 call_user_func_array($Func["FunctionName"], $Func["Arguments"]);
1033 }
1034
1035 # write out any output buffered from page code execution
1036 if (strlen($PageOutput))
1037 {
1038 if (!$this->SuppressHTML)
1039 {
1040 ?><table width="100%" cellpadding="5"
1041 style="border: 2px solid #666666; background: #CCCCCC;
1042 font-family: Courier New, Courier, monospace;
1043 margin-top: 10px;"><tr><td><?PHP
1044 }
1045 if ($this->JumpToPage)
1046 {
1047 ?><div style="color: #666666;"><span style="font-size: 150%;">
1048 <b>Page Jump Aborted</b></span>
1049 (because of error or other unexpected output)<br />
1050 <b>Jump Target:</b>
1051 <i><?PHP print($this->JumpToPage); ?></i></div><?PHP
1052 }
1053 print $PageOutput;
1054 if (!$this->SuppressHTML)
1055 {
1056 ?></td></tr></table><?PHP
1057 }
1058 }
1059
1060 # write out any output buffered from the page code execution complete signal
1061 if (!$this->JumpToPage && !$this->SuppressHTML && strlen($PageCompleteOutput))
1062 {
1063 print $PageCompleteOutput;
1064 }
1065
1066 # log slow page loads
1067 if ($this->LogSlowPageLoads()
1068 && !$this->DoNotLogSlowPageLoad
1069 && ($this->GetElapsedExecutionTime()
1070 >= ($this->SlowPageLoadThreshold())))
1071 {
1072 $RemoteHost = gethostbyaddr($_SERVER["REMOTE_ADDR"]);
1073 if ($RemoteHost === FALSE)
1074 {
1075 $RemoteHost = $_SERVER["REMOTE_ADDR"];
1076 }
1077 elseif ($RemoteHost != $_SERVER["REMOTE_ADDR"])
1078 {
1079 $RemoteHost .= " (".$_SERVER["REMOTE_ADDR"].")";
1080 }
1081 $SlowPageLoadMsg = "Slow page load ("
1082 .intval($this->GetElapsedExecutionTime())."s) for "
1083 .$this->FullUrl()." from ".$RemoteHost;
1084 $this->LogMessage(self::LOGLVL_INFO, $SlowPageLoadMsg);
1085 }
1086
1087 # execute callbacks that should not have their output buffered
1088 foreach ($this->UnbufferedCallbacks as $Callback)
1089 {
1090 call_user_func_array($Callback[0], $Callback[1]);
1091 }
1092
1093 # log high memory usage
1094 if (function_exists("memory_get_peak_usage"))
1095 {
1096 $MemoryThreshold = ($this->HighMemoryUsageThreshold()
1097 * $this->GetPhpMemoryLimit()) / 100;
1098 if ($this->LogHighMemoryUsage()
1099 && (memory_get_peak_usage(TRUE) >= $MemoryThreshold))
1100 {
1101 $HighMemUsageMsg = "High peak memory usage ("
1102 .number_format(memory_get_peak_usage(TRUE)).") for "
1103 .$this->FullUrl()." from "
1104 .$_SERVER["REMOTE_ADDR"];
1105 $this->LogMessage(self::LOGLVL_INFO, $HighMemUsageMsg);
1106 }
1107 }
1108
1109 $this->UpdateLastUsedTimeForActiveSessions();
1110
1111 # terminate and stay resident (TSR!) if indicated and HTML has been output
1112 # (only TSR if HTML has been output because otherwise browsers will misbehave)
1113 if ($ShouldTSR) { $this->LaunchTSR(); }
1114 }
1115
1120 public function IsRunningInBackground()
1121 {
1122 return $this->RunningInBackground;
1123 }
1124
1130 public function GetPageName()
1131 {
1132 return $this->PageName;
1133 }
1134
1140 public function GetPageLocation()
1141 {
1142 # retrieve current URL
1143 $Url = self::GetScriptUrl();
1144
1145 # remove the base path if present
1146 $BasePath = $this->Settings["BasePath"];
1147 if (stripos($Url, $BasePath) === 0)
1148 {
1149 $Url = substr($Url, strlen($BasePath));
1150 }
1151
1152 # if we're being accessed via an alternate domain,
1153 # add the appropriate prefix in
1154 if ($this->HtaccessSupport() &&
1155 self::$RootUrlOverride !== NULL)
1156 {
1157 $VHost = $_SERVER["SERVER_NAME"];
1158 if (isset($this->AlternateDomainPrefixes[$VHost]))
1159 {
1160 $ThisPrefix = $this->AlternateDomainPrefixes[$VHost];
1161 $Url = $ThisPrefix."/".$Url;
1162 }
1163 }
1164
1165 return $Url;
1166 }
1167
1173 public function GetPageUrl()
1174 {
1175 return self::BaseUrl().$this->GetPageLocation();
1176 }
1177
1190 public function SetJumpToPage($Page, $Delay = 0, $IsLiteral = FALSE)
1191 {
1192 if (!is_null($Page)
1193 && (!$IsLiteral)
1194 && (strpos($Page, "?") === FALSE)
1195 && ((strpos($Page, "=") !== FALSE)
1196 || ((stripos($Page, ".php") === FALSE)
1197 && (stripos($Page, ".htm") === FALSE)
1198 && (strpos($Page, "/") === FALSE)))
1199 && (stripos($Page, "http://") !== 0)
1200 && (stripos($Page, "https://") !== 0))
1201 {
1202 $this->JumpToPage = self::BaseUrl() . "index.php?P=".$Page;
1203 }
1204 else
1205 {
1206 $this->JumpToPage = $Page;
1207 }
1208 $this->JumpToPageDelay = $Delay;
1209 }
1210
1215 public function JumpToPageIsSet()
1216 {
1217 return ($this->JumpToPage === NULL) ? FALSE : TRUE;
1218 }
1219
1229 public function HtmlCharset($NewSetting = NULL)
1230 {
1231 if ($NewSetting !== NULL) { $this->HtmlCharset = $NewSetting; }
1232 return $this->HtmlCharset;
1233 }
1234
1244 public function DoNotMinimizeFile($File)
1245 {
1246 if (!is_array($File)) { $File = array($File); }
1247 $this->DoNotMinimizeList = array_merge($this->DoNotMinimizeList, $File);
1248 }
1249
1260 public function UseBaseTag($NewValue = NULL)
1261 {
1262 if ($NewValue !== NULL) { $this->UseBaseTag = $NewValue ? TRUE : FALSE; }
1263 return $this->UseBaseTag;
1264 }
1265
1273 public function SuppressHTMLOutput($NewSetting = TRUE)
1274 {
1275 $this->SuppressHTML = $NewSetting;
1276 }
1277
1285 public function SuppressStandardPageStartAndEnd($NewSetting = TRUE)
1286 {
1287 $this->SuppressStdPageStartAndEnd = $NewSetting;
1288 }
1289
1295 public static function SuppressSessionInitialization($NewValue = NULL)
1296 {
1297 if ($NewValue !== NULL)
1298 {
1299 self::$SuppressSessionInitialization = $NewValue;
1300 }
1301 return self::$SuppressSessionInitialization;
1302 }
1303
1309 public static function DefaultUserInterface($UIName = NULL)
1310 {
1311 if ($UIName !== NULL)
1312 {
1313 self::$DefaultUI = $UIName;
1314 }
1315 return self::$DefaultUI;
1316 }
1317
1324 public static function ActiveUserInterface($UIName = NULL)
1325 {
1326 if ($UIName !== NULL)
1327 {
1328 self::$ActiveUI = preg_replace("/^SPTUI--/", "", $UIName);
1329 }
1330 return self::$ActiveUI;
1331 }
1332
1343 public function GetUserInterfaces($FilterExp = NULL)
1344 {
1345 if (!isset(self::$UserInterfaceListCache[$FilterExp]))
1346 {
1347 # retrieve paths to user interface directories
1348 $Paths = $this->GetUserInterfacePaths($FilterExp);
1349
1350 # start out with an empty list
1351 self::$UserInterfaceListCache[$FilterExp] = array();
1352
1353 # for each possible UI directory
1354 foreach ($Paths as $CanonicalName => $Path)
1355 {
1356 # if name file available
1357 $LabelFile = $Path."/NAME";
1358 if (is_readable($LabelFile))
1359 {
1360 # read the UI name
1361 $Label = file_get_contents($LabelFile);
1362
1363 # if the UI name looks reasonable
1364 if (strlen(trim($Label)))
1365 {
1366 # use read name
1367 self::$UserInterfaceListCache[$FilterExp][$CanonicalName] =
1368 $Label;
1369 }
1370 }
1371
1372 # if we do not have a name yet
1373 if (!isset(self::$UserInterfaceListCache[$FilterExp][$CanonicalName]))
1374 {
1375 # use base directory for name
1376 self::$UserInterfaceListCache[$FilterExp][$CanonicalName] =
1377 basename($Path);
1378 }
1379 }
1380 }
1381
1382 # return list to caller
1383 return self::$UserInterfaceListCache[$FilterExp];
1384 }
1385
1394 public function GetUserInterfacePaths($FilterExp = NULL)
1395 {
1396 if (!isset(self::$UserInterfacePathsCache[$FilterExp]))
1397 {
1398 # extract possible UI directories from interface directory list
1399 $InterfaceDirs = array();
1400 foreach ($this->ExpandDirectoryList($this->InterfaceDirList) as $Dir)
1401 {
1402 $Matches = array();
1403 if (preg_match("#([a-zA-Z0-9/]*interface)/[a-zA-Z0-9%/]*#",
1404 $Dir, $Matches))
1405 {
1406 $Dir = $Matches[1];
1407 if (!in_array($Dir, $InterfaceDirs))
1408 {
1409 $InterfaceDirs[] = $Dir;
1410 }
1411 }
1412 }
1413
1414 # reverse order of interface directories so that the directory
1415 # returned is the base directory for the interface
1416 $InterfaceDirs = array_reverse($InterfaceDirs);
1417
1418 # start out with an empty list
1419 self::$UserInterfacePathsCache[$FilterExp] = array();
1420 $InterfacesFound = array();
1421
1422 # for each possible UI directory
1423 foreach ($InterfaceDirs as $InterfaceDir)
1424 {
1425 # check if the dir exists
1426 if (!is_dir($InterfaceDir))
1427 {
1428 continue;
1429 }
1430
1431 $Dir = dir($InterfaceDir);
1432
1433 # for each file in current directory
1434 while (($DirEntry = $Dir->read()) !== FALSE)
1435 {
1436 $InterfacePath = $InterfaceDir."/".$DirEntry;
1437
1438 # skip anything we have already found
1439 # or that doesn't have a name in the required format
1440 # or that isn't a directory
1441 # or that doesn't match the filter regex (if supplied)
1442 if (in_array($DirEntry, $InterfacesFound)
1443 || !preg_match('/^[a-zA-Z0-9]+$/', $DirEntry)
1444 || !is_dir($InterfacePath)
1445 || (($FilterExp !== NULL)
1446 && !preg_match($FilterExp, $InterfacePath)))
1447 {
1448 continue;
1449 }
1450
1451 # add interface to list
1452 self::$UserInterfacePathsCache[$FilterExp][$DirEntry] =
1453 $InterfacePath;
1454 $InterfacesFound[] = $DirEntry;
1455 }
1456
1457 $Dir->close();
1458 }
1459 }
1460
1461 # return list to caller
1462 return self::$UserInterfacePathsCache[$FilterExp];
1463 }
1464
1489 public function AddPostProcessingCall($FunctionName,
1490 &$Arg1 = self::NOVALUE, &$Arg2 = self::NOVALUE, &$Arg3 = self::NOVALUE,
1491 &$Arg4 = self::NOVALUE, &$Arg5 = self::NOVALUE, &$Arg6 = self::NOVALUE,
1492 &$Arg7 = self::NOVALUE, &$Arg8 = self::NOVALUE, &$Arg9 = self::NOVALUE)
1493 {
1494 $FuncIndex = count($this->PostProcessingFuncs);
1495 $this->PostProcessingFuncs[$FuncIndex]["FunctionName"] = $FunctionName;
1496 $this->PostProcessingFuncs[$FuncIndex]["Arguments"] = array();
1497 $Index = 1;
1498 while (isset(${"Arg".$Index}) && (${"Arg".$Index} !== self::NOVALUE))
1499 {
1500 $this->PostProcessingFuncs[$FuncIndex]["Arguments"][$Index]
1501 =& ${"Arg".$Index};
1502 $Index++;
1503 }
1504 }
1505
1511 public function AddEnvInclude($FileName)
1512 {
1513 $this->EnvIncludes[] = $FileName;
1514 }
1515
1532 public function SetContextFilter($Context, $NewSetting)
1533 {
1534 if (($NewSetting === TRUE)
1535 || ($NewSetting === FALSE)
1536 || is_array($NewSetting))
1537 {
1538 $this->ContextFilters[$Context] = $NewSetting;
1539 }
1540 elseif (is_string($NewSetting))
1541 {
1542 $this->ContextFilters[$Context] = array($NewSetting);
1543 }
1544 else
1545 {
1546 throw new InvalidArgumentException(
1547 "Invalid setting (".$NewSetting.").");
1548 }
1549 }
1551 const CONTEXT_ENV = 1;
1553 const CONTEXT_PAGE = 2;
1555 const CONTEXT_COMMON = 3;
1557 const CONTEXT_INTERFACE = 4;
1559 const CONTEXT_START = 5;
1561 const CONTEXT_END = 6;
1562
1569 public function GUIFile($FileName)
1570 {
1571 # determine which location to search based on file suffix
1572 $FileType = $this->GetFileType($FileName);
1573 $DirList = ($FileType == self::FT_IMAGE)
1574 ? $this->ImageDirList : $this->IncludeDirList;
1575
1576 # if directed to use minimized JavaScript file
1577 if (($FileType == self::FT_JAVASCRIPT) && $this->UseMinimizedJavascript())
1578 {
1579 # look for minimized version of file
1580 $MinimizedFileName = substr_replace($FileName, ".min", -3, 0);
1581 $FoundFileName = $this->FindFile($DirList, $MinimizedFileName);
1582
1583 # if minimized file was not found
1584 if (is_null($FoundFileName))
1585 {
1586 # look for unminimized file
1587 $FoundFileName = $this->FindFile($DirList, $FileName);
1588
1589 # if unminimized file found
1590 if (!is_null($FoundFileName))
1591 {
1592 # if minimization enabled and supported
1593 if ($this->JavascriptMinimizationEnabled()
1594 && self::JsMinRewriteSupport())
1595 {
1596 # attempt to create minimized file
1597 $MinFileName = $this->MinimizeJavascriptFile(
1598 $FoundFileName);
1599
1600 # if minimization succeeded
1601 if ($MinFileName !== NULL)
1602 {
1603 # use minimized version
1604 $FoundFileName = $MinFileName;
1605
1606 # save file modification time if needed for fingerprinting
1607 if ($this->UrlFingerprintingEnabled())
1608 {
1609 $FileMTime = filemtime($FoundFileName);
1610 }
1611
1612 # strip off the cache location, allowing .htaccess
1613 # to handle that for us
1614 $FoundFileName = str_replace(
1615 self::$JSMinCacheDir."/", "", $FoundFileName);
1616 }
1617 }
1618 }
1619 }
1620 }
1621 # else if directed to use SCSS files
1622 elseif (($FileType == self::FT_CSS) && $this->ScssSupportEnabled())
1623 {
1624 # look for SCSS version of file
1625 $SourceFileName = preg_replace("/.css$/", ".scss", $FileName);
1626 $FoundSourceFileName = $this->FindFile($DirList, $SourceFileName);
1627
1628 # if SCSS file not found
1629 if ($FoundSourceFileName === NULL)
1630 {
1631 # look for CSS file
1632 $FoundFileName = $this->FindFile($DirList, $FileName);
1633 }
1634 else
1635 {
1636 # compile SCSS file (if updated) and return resulting CSS file
1637 $FoundFileName = $this->CompileScssFile($FoundSourceFileName);
1638
1639 # save file modification time if needed for fingerprinting
1640 if ($this->UrlFingerprintingEnabled())
1641 {
1642 $FileMTime = filemtime($FoundFileName);
1643 }
1644
1645 # strip off the cache location, allowing .htaccess to handle that for us
1646 if (self::ScssRewriteSupport())
1647 {
1648 $FoundFileName = str_replace(
1649 self::$ScssCacheDir."/", "", $FoundFileName);
1650 }
1651 }
1652 }
1653 # otherwise just search for the file
1654 else
1655 {
1656 $FoundFileName = $this->FindFile($DirList, $FileName);
1657 }
1658
1659 # add non-image files to list of found files (used for required files loading)
1660 if ($FileType != self::FT_IMAGE)
1661 { $this->FoundUIFiles[] = basename($FoundFileName); }
1662
1663 # if UI file fingerprinting is enabled and supported
1664 if ($this->UrlFingerprintingEnabled()
1665 && self::UrlFingerprintingRewriteSupport()
1666 && (isset($FileMTime) || file_exists($FoundFileName)))
1667 {
1668 # if file does not appear to be a server-side inclusion
1669 if (!preg_match('/\.(html|php)$/i', $FoundFileName))
1670 {
1671 # for each URL fingerprinting blacklist entry
1672 $OnBlacklist = FALSE;
1673 foreach ($this->UrlFingerprintBlacklist as $BlacklistEntry)
1674 {
1675 # if entry looks like a regular expression pattern
1676 if ($BlacklistEntry[0] == substr($BlacklistEntry, -1))
1677 {
1678 # check file name against regular expression
1679 if (preg_match($BlacklistEntry, $FoundFileName))
1680 {
1681 $OnBlacklist = TRUE;
1682 break;
1683 }
1684 }
1685 else
1686 {
1687 # check file name directly against entry
1688 if (basename($FoundFileName) == $BlacklistEntry)
1689 {
1690 $OnBlacklist = TRUE;
1691 break;
1692 }
1693 }
1694 }
1695
1696 # if file was not on blacklist
1697 if (!$OnBlacklist)
1698 {
1699 # get file modification time if not already retrieved
1700 if (!isset($FileMTime))
1701 {
1702 $FileMTime = filemtime($FoundFileName);
1703 }
1704
1705 # add timestamp fingerprint to file name
1706 $Fingerprint = sprintf("%06X",
1707 ($FileMTime % 0xFFFFFF));
1708 $FoundFileName = preg_replace("/^(.+)\.([a-z]+)$/",
1709 "$1.".$Fingerprint.".$2",
1710 $FoundFileName);
1711 }
1712 }
1713 }
1714
1715 # return file name to caller
1716 return $FoundFileName;
1717 }
1718
1727 public function PUIFile($FileName)
1728 {
1729 $FullFileName = $this->GUIFile($FileName);
1730 if ($FullFileName) { print($FullFileName); }
1731 }
1732
1747 public function IncludeUIFile($FileNames, $AdditionalAttributes = NULL)
1748 {
1749 # convert file name to array if necessary
1750 if (!is_array($FileNames)) { $FileNames = [ $FileNames ]; }
1751
1752 # pad additional attributes if supplied
1753 $AddAttribs = $AdditionalAttributes ? " ".$AdditionalAttributes : "";
1754
1755 # for each file
1756 foreach ($FileNames as $BaseFileName)
1757 {
1758 # retrieve full file name
1759 $FileName = $this->GUIFile($BaseFileName);
1760
1761 # if file was found
1762 if ($FileName)
1763 {
1764 # print appropriate tag
1765 print $this->GetUIFileLoadingTag($FileName, $AdditionalAttributes);
1766 }
1767
1768 # if we are not already loading an override file
1769 if (!preg_match("/-Override.(css|scss|js)$/", $BaseFileName))
1770 {
1771 # attempt to load override file if available
1772 $FileType = $this->GetFileType($BaseFileName);
1773 switch ($FileType)
1774 {
1775 case self::FT_CSS:
1776 $OverrideFileName = preg_replace(
1777 "/\.(css|scss)$/", "-Override.$1",
1778 $BaseFileName);
1779 $this->IncludeUIFile($OverrideFileName,
1780 $AdditionalAttributes);
1781 break;
1782
1783 case self::FT_JAVASCRIPT:
1784 $OverrideFileName = preg_replace(
1785 "/\.js$/", "-Override.js",
1786 $BaseFileName);
1787 $this->IncludeUIFile($OverrideFileName,
1788 $AdditionalAttributes);
1789 break;
1790 }
1791 }
1792 }
1793 }
1794
1801 public function DoNotUrlFingerprint($Pattern)
1802 {
1803 $this->UrlFingerprintBlacklist[] = $Pattern;
1804 }
1805
1817 public function RequireUIFile($FileNames, $Order = self::ORDER_MIDDLE)
1818 {
1819 # convert file names to array if necessary
1820 if (!is_array($FileNames)) { $FileNames = [ $FileNames ]; }
1821
1822 # add file names to list of required files
1823 foreach ($FileNames as $FileName)
1824 {
1825 $this->AdditionalRequiredUIFiles[$FileName] = $Order;
1826 }
1827 }
1828
1834 public static function GetFileType($FileName)
1835 {
1836 static $FileTypeCache;
1837 if (isset($FileTypeCache[$FileName]))
1838 {
1839 return $FileTypeCache[$FileName];
1840 }
1841
1842 $FileSuffix = strtolower(substr($FileName, -3));
1843 if ($FileSuffix == "css")
1844 {
1845 $FileTypeCache[$FileName] = self::FT_CSS;
1846 }
1847 elseif ($FileSuffix == ".js")
1848 {
1849 $FileTypeCache[$FileName] = self::FT_JAVASCRIPT;
1850 }
1851 elseif (($FileSuffix == "gif")
1852 || ($FileSuffix == "jpg")
1853 || ($FileSuffix == "png")
1854 || ($FileSuffix == "svg")
1855 || ($FileSuffix == "ico"))
1856 {
1857 $FileTypeCache[$FileName] = self::FT_IMAGE;
1858 }
1859 else
1860 {
1861 $FileTypeCache[$FileName] = self::FT_OTHER;
1862 }
1863
1864 return $FileTypeCache[$FileName];
1865 }
1867 const FT_OTHER = 0;
1869 const FT_CSS = 1;
1871 const FT_IMAGE = 2;
1873 const FT_JAVASCRIPT = 3;
1874
1883 public function LoadFunction($Callback)
1884 {
1885 # if specified function is not currently available
1886 if (!is_callable($Callback))
1887 {
1888 # if function info looks legal
1889 if (is_string($Callback) && strlen($Callback))
1890 {
1891 # start with function directory list
1892 $Locations = $this->FunctionDirList;
1893
1894 # add object directories to list
1895 $Locations = array_merge(
1896 $Locations, array_keys(self::$ObjectDirectories));
1897
1898 # look for function file
1899 $FunctionFileName = $this->FindFile($Locations, "F-".$Callback,
1900 array("php", "html"));
1901
1902 # if function file was found
1903 if ($FunctionFileName)
1904 {
1905 # load function file
1906 include_once($FunctionFileName);
1907 }
1908 else
1909 {
1910 # log error indicating function load failed
1911 $this->LogError(self::LOGLVL_ERROR, "Unable to load function"
1912 ." for callback \"".$Callback."\".");
1913 }
1914 }
1915 else
1916 {
1917 # log error indicating specified function info was bad
1918 $this->LogError(self::LOGLVL_ERROR, "Unloadable callback value"
1919 ." (".$Callback.")"
1920 ." passed to AF::LoadFunction() by "
1921 .StdLib::GetMyCaller().".");
1922 }
1923 }
1924
1925 # report to caller whether function load succeeded
1926 return is_callable($Callback);
1927 }
1928
1933 public function GetElapsedExecutionTime()
1934 {
1935 return microtime(TRUE) - $this->ExecutionStartTime;
1936 }
1937
1942 public function GetSecondsBeforeTimeout()
1943 {
1944 return $this->MaxExecutionTime() - $this->GetElapsedExecutionTime();
1945 }
1946
1951 public function AddMetaTag($Attribs)
1952 {
1953 # add new meta tag to list
1954 $this->MetaTags[] = $Attribs;
1955 }
1956
1965 public function AddMetaTagOnce($Attribs, $UniqueAttribs = NULL)
1966 {
1967 # add new meta tag to list
1968 $this->UniqueMetaTags[] = [
1969 "Attribs" => $Attribs,
1970 "UniqueAttribs" => $UniqueAttribs,
1971 ];
1972 }
1973
1974 /*@)*/ /* Application Framework */
1975
1976
1977 # ---- Page Caching ------------------------------------------------------
1978 /*@(*/
1980
1990 public function PageCacheEnabled(
1991 $NewValue = DB_NOVALUE, $Persistent = FALSE)
1992 {
1993 return $this->UpdateSetting(__FUNCTION__, $NewValue, $Persistent);
1994 }
1995
2005 public function PageCacheExpirationPeriod(
2006 $NewValue = DB_NOVALUE, $Persistent = FALSE)
2007 {
2008 return $this->UpdateSetting(__FUNCTION__, $NewValue, $Persistent);
2009 }
2010
2015 public function DoNotCacheCurrentPage()
2016 {
2017 $this->CacheCurrentPage = FALSE;
2018 }
2019
2026 public function AddPageCacheTag($Tag, $Pages = NULL)
2027 {
2028 # normalize tag
2029 $Tag = strtolower($Tag);
2030
2031 # if pages were supplied
2032 if ($Pages !== NULL)
2033 {
2034 # add pages to list for this tag
2035 if (isset($this->PageCacheTags[$Tag]))
2036 {
2037 $this->PageCacheTags[$Tag] = array_merge(
2038 $this->PageCacheTags[$Tag], $Pages);
2039 }
2040 else
2041 {
2042 $this->PageCacheTags[$Tag] = $Pages;
2043 }
2044 }
2045 else
2046 {
2047 # add current page to list for this tag
2048 $this->PageCacheTags[$Tag][] = "CURRENT";
2049 }
2050 }
2051
2057 public function ClearPageCacheForTag($Tag)
2058 {
2059 # get tag ID
2060 $TagId = $this->GetPageCacheTagId($Tag);
2061
2062 # delete pages and tag/page connections for specified tag
2063 $this->DB->Query("DELETE CP, CPTI"
2064 ." FROM AF_CachedPages CP, AF_CachedPageTagInts CPTI"
2065 ." WHERE CPTI.TagId = ".intval($TagId)
2066 ." AND CP.CacheId = CPTI.CacheId");
2067 }
2068
2072 public function ClearPageCache()
2073 {
2074 # clear all page cache tables
2075 $this->DB->Query("TRUNCATE TABLE AF_CachedPages");
2076 $this->DB->Query("TRUNCATE TABLE AF_CachedPageTags");
2077 $this->DB->Query("TRUNCATE TABLE AF_CachedPageTagInts");
2078 }
2079
2086 public function GetPageCacheInfo()
2087 {
2088 $Length = $this->DB->Query("SELECT COUNT(*) AS CacheLen"
2089 ." FROM AF_CachedPages", "CacheLen");
2090 $Oldest = $this->DB->Query("SELECT CachedAt FROM AF_CachedPages"
2091 ." ORDER BY CachedAt ASC LIMIT 1", "CachedAt");
2092 return array(
2093 "NumberOfEntries" => $Length,
2094 "OldestTimestamp" => strtotime($Oldest),
2095 );
2096 }
2097
2104 public function GetPageCachePageList()
2105 {
2106 $this->DB->Query("SELECT SUBSTRING_INDEX(Fingerprint, '-', 1) AS PageName,"
2107 ." COUNT(*) AS Number FROM AF_CachedPages"
2108 ." GROUP BY PageName ORDER BY Number DESC");
2109 return $this->DB->FetchColumn("Number", "PageName");
2110 }
2111
2112 /*@)*/ /* Page Caching */
2113
2114
2115 # ---- Logging -----------------------------------------------------------
2116 /*@(*/
2118
2132 public function LogSlowPageLoads(
2133 $NewValue = DB_NOVALUE, $Persistent = FALSE)
2134 {
2135 return $this->UpdateSetting(__FUNCTION__, $NewValue, $Persistent);
2136 }
2137
2148 public function SlowPageLoadThreshold(
2149 $NewValue = DB_NOVALUE, $Persistent = FALSE)
2150 {
2151 return $this->UpdateSetting(__FUNCTION__, $NewValue, $Persistent);
2152 }
2153
2167 public function LogHighMemoryUsage(
2168 $NewValue = DB_NOVALUE, $Persistent = FALSE)
2169 {
2170 return $this->UpdateSetting(__FUNCTION__, $NewValue, $Persistent);
2171 }
2172
2184 public function HighMemoryUsageThreshold(
2185 $NewValue = DB_NOVALUE, $Persistent = FALSE)
2186 {
2187 return $this->UpdateSetting(__FUNCTION__, $NewValue, $Persistent);
2188 }
2189
2203 public function LogError($Level, $Msg)
2204 {
2205 # if error level is at or below current logging level
2206 if ($this->Settings["LoggingLevel"] >= $Level)
2207 {
2208 # attempt to log error message
2209 $Result = $this->LogMessage($Level, $Msg);
2210
2211 # if logging attempt failed and level indicated significant error
2212 if (($Result === FALSE) && ($Level <= self::LOGLVL_ERROR))
2213 {
2214 # throw exception about inability to log error
2215 static $AlreadyThrewException = FALSE;
2216 if (!$AlreadyThrewException)
2217 {
2218 $AlreadyThrewException = TRUE;
2219 throw new Exception("Unable to log error (".$Level.": ".$Msg
2220 .") to ".$this->LogFileName);
2221 }
2222 }
2223
2224 # report to caller whether message was logged
2225 return $Result;
2226 }
2227 else
2228 {
2229 # report to caller that message was not logged
2230 return FALSE;
2231 }
2232 }
2233
2245 public function LogMessage($Level, $Msg)
2246 {
2247 # if message level is at or below current logging level
2248 if ($this->Settings["LoggingLevel"] >= $Level)
2249 {
2250 # attempt to open log file
2251 $FHndl = @fopen($this->LogFileName, "a");
2252
2253 # if log file could not be open
2254 if ($FHndl === FALSE)
2255 {
2256 # report to caller that message was not logged
2257 return FALSE;
2258 }
2259 else
2260 {
2261 # format log entry
2262 $ErrorAbbrevs = array(
2263 self::LOGLVL_FATAL => "FTL",
2264 self::LOGLVL_ERROR => "ERR",
2265 self::LOGLVL_WARNING => "WRN",
2266 self::LOGLVL_INFO => "INF",
2267 self::LOGLVL_DEBUG => "DBG",
2268 self::LOGLVL_TRACE => "TRC",
2269 );
2270 $Msg = str_replace(array("\n", "\t", "\r"), " ", $Msg);
2271 $Msg = substr(trim($Msg), 0, self::LOGFILE_MAX_LINE_LENGTH);
2272 $LogEntry = date("Y-m-d H:i:s")
2273 ." ".($this->IsRunningInBackground() ? "B" : "F")
2274 ." ".$ErrorAbbrevs[$Level]
2275 ." ".$Msg;
2276
2277 # write entry to log
2278 $Success = fwrite($FHndl, $LogEntry."\n");
2279
2280 # close log file
2281 fclose($FHndl);
2282
2283 # report to caller whether message was logged
2284 return ($Success === FALSE) ? FALSE : TRUE;
2285 }
2286 }
2287 else
2288 {
2289 # report to caller that message was not logged
2290 return FALSE;
2291 }
2292 }
2293
2318 public function LoggingLevel($NewValue = DB_NOVALUE, $Persistent = FALSE)
2319 {
2320 # constrain new level (if supplied) to within legal bounds
2321 if ($NewValue !== DB_NOVALUE)
2322 {
2323 $NewValue = max(min($NewValue, 6), 1);
2324 }
2325
2326 # set new logging level (if supplied) and return current level to caller
2327 return $this->UpdateSetting(__FUNCTION__, $NewValue, $Persistent);
2328 }
2329
2336 public function LogFile($NewValue = NULL)
2337 {
2338 if ($NewValue !== NULL) { $this->LogFileName = $NewValue; }
2339 return $this->LogFileName;
2340 }
2341
2351 public function GetLogEntries($Limit = 0)
2352 {
2353 # return no entries if there isn't a log file
2354 # or we can't read it or it's empty
2355 $LogFile = $this->LogFile();
2356 if (!is_readable($LogFile) || !filesize($LogFile))
2357 {
2358 return array();
2359 }
2360
2361 # if max number of entries specified
2362 if ($Limit > 0)
2363 {
2364 # load lines from file
2365 $FHandle = fopen($LogFile, "r");
2366 $FileSize = filesize($LogFile);
2367 $SeekPosition = max(0,
2368 ($FileSize - (self::LOGFILE_MAX_LINE_LENGTH
2369 * ($Limit + 1))));
2370 fseek($FHandle, $SeekPosition);
2371 $Block = fread($FHandle, ($FileSize - $SeekPosition));
2372 fclose($FHandle);
2373 $Lines = explode(PHP_EOL, $Block);
2374 array_pop($Lines);
2375
2376 # prune array back to requested number of entries
2377 $Lines = array_slice($Lines, (0 - $Limit));
2378 }
2379 else
2380 {
2381 # load all lines from log file
2382 $Lines = file($LogFile, FILE_IGNORE_NEW_LINES);
2383 if ($Lines === FALSE)
2384 {
2385 return array();
2386 }
2387 }
2388
2389 # reverse line order
2390 $Lines = array_reverse($Lines);
2391
2392 # for each log file line
2393 $Entries = array();
2394 foreach ($Lines as $Line)
2395 {
2396 # attempt to parse line into component parts
2397 $Pieces = explode(" ", $Line, 5);
2398 $Date = isset($Pieces[0]) ? $Pieces[0] : "";
2399 $Time = isset($Pieces[1]) ? $Pieces[1] : "";
2400 $Back = isset($Pieces[2]) ? $Pieces[2] : "";
2401 $Level = isset($Pieces[3]) ? $Pieces[3] : "";
2402 $Msg = isset($Pieces[4]) ? $Pieces[4] : "";
2403
2404 # skip line if it looks invalid
2405 $ErrorAbbrevs = array(
2406 "FTL" => self::LOGLVL_FATAL,
2407 "ERR" => self::LOGLVL_ERROR,
2408 "WRN" => self::LOGLVL_WARNING,
2409 "INF" => self::LOGLVL_INFO,
2410 "DBG" => self::LOGLVL_DEBUG,
2411 "TRC" => self::LOGLVL_TRACE,
2412 );
2413 if ((($Back != "F") && ($Back != "B"))
2414 || !array_key_exists($Level, $ErrorAbbrevs)
2415 || !strlen($Msg))
2416 {
2417 continue;
2418 }
2419
2420 # convert parts into appropriate values and add to entries
2421 $Entries[] = array(
2422 "Time" => strtotime($Date." ".$Time),
2423 "Background" => ($Back == "B") ? TRUE : FALSE,
2424 "Level" => $ErrorAbbrevs[$Level],
2425 "Message" => $Msg,
2426 );
2427 }
2428
2429 # return entries to caller
2430 return $Entries;
2431 }
2432
2437 const LOGLVL_TRACE = 6;
2442 const LOGLVL_DEBUG = 5;
2448 const LOGLVL_INFO = 4;
2453 const LOGLVL_WARNING = 3;
2459 const LOGLVL_ERROR = 2;
2464 const LOGLVL_FATAL = 1;
2465
2469 const LOGFILE_MAX_LINE_LENGTH = 2048;
2470
2471 /*@)*/ /* Logging */
2472
2473
2474 # ---- Event Handling ----------------------------------------------------
2475 /*@(*/
2477
2481 const EVENTTYPE_DEFAULT = 1;
2487 const EVENTTYPE_CHAIN = 2;
2493 const EVENTTYPE_FIRST = 3;
2501 const EVENTTYPE_NAMED = 4;
2502
2504 const ORDER_FIRST = 1;
2506 const ORDER_MIDDLE = 2;
2508 const ORDER_LAST = 3;
2509
2518 public function RegisterEvent($EventsOrEventName, $EventType = NULL)
2519 {
2520 # convert parameters to array if not already in that form
2521 $Events = is_array($EventsOrEventName) ? $EventsOrEventName
2522 : array($EventsOrEventName => $EventType);
2523
2524 # for each event
2525 foreach ($Events as $Name => $Type)
2526 {
2527 # store event information
2528 $this->RegisteredEvents[$Name]["Type"] = $Type;
2529 $this->RegisteredEvents[$Name]["Hooks"] = array();
2530 }
2531 }
2532
2539 public function IsRegisteredEvent($EventName)
2540 {
2541 return array_key_exists($EventName, $this->RegisteredEvents)
2542 ? TRUE : FALSE;
2543 }
2544
2551 public function IsHookedEvent($EventName)
2552 {
2553 # the event isn't hooked to if it isn't even registered
2554 if (!$this->IsRegisteredEvent($EventName))
2555 {
2556 return FALSE;
2557 }
2558
2559 # return TRUE if there is at least one callback hooked to the event
2560 return count($this->RegisteredEvents[$EventName]["Hooks"]) > 0;
2561 }
2562
2576 public function HookEvent(
2577 $EventsOrEventName, $Callback = NULL, $Order = self::ORDER_MIDDLE)
2578 {
2579 # convert parameters to array if not already in that form
2580 $Events = is_array($EventsOrEventName) ? $EventsOrEventName
2581 : array($EventsOrEventName => $Callback);
2582
2583 # for each event
2584 $Success = TRUE;
2585 foreach ($Events as $EventName => $EventCallback)
2586 {
2587 # if callback is valid
2588 if (is_callable($EventCallback))
2589 {
2590 # if this is a periodic event we process internally
2591 if (isset($this->PeriodicEvents[$EventName]))
2592 {
2593 # process event now
2594 $this->ProcessPeriodicEvent($EventName, $EventCallback);
2595 }
2596 # if specified event has been registered
2597 elseif (isset($this->RegisteredEvents[$EventName]))
2598 {
2599 # add callback for event
2600 $this->RegisteredEvents[$EventName]["Hooks"][]
2601 = array("Callback" => $EventCallback, "Order" => $Order);
2602
2603 # sort callbacks by order
2604 if (count($this->RegisteredEvents[$EventName]["Hooks"]) > 1)
2605 {
2606 usort($this->RegisteredEvents[$EventName]["Hooks"],
2607 function ($A, $B) {
2608 return StdLib::SortCompare(
2609 $A["Order"], $B["Order"]);
2610 });
2611 }
2612 }
2613 else
2614 {
2615 $Success = FALSE;
2616 }
2617 }
2618 else
2619 {
2620 $Success = FALSE;
2621 }
2622 }
2623
2624 # report to caller whether all callbacks were hooked
2625 return $Success;
2626 }
2627
2641 public function UnhookEvent(
2642 $EventsOrEventName, $Callback = NULL, $Order = self::ORDER_MIDDLE)
2643 {
2644 # convert parameters to array if not already in that form
2645 $Events = is_array($EventsOrEventName) ? $EventsOrEventName
2646 : array($EventsOrEventName => $Callback);
2647
2648 # for each event
2649 $UnhookCount = 0;
2650 foreach ($Events as $EventName => $EventCallback)
2651 {
2652 # if this event has been registered and hooked
2653 if (isset($this->RegisteredEvents[$EventName])
2654 && count($this->RegisteredEvents[$EventName]))
2655 {
2656 # if this callback has been hooked for this event
2657 $CallbackData = array("Callback" => $EventCallback, "Order" => $Order);
2658 if (in_array($CallbackData,
2659 $this->RegisteredEvents[$EventName]["Hooks"]))
2660 {
2661 # unhook callback
2662 $HookIndex = array_search($CallbackData,
2663 $this->RegisteredEvents[$EventName]["Hooks"]);
2664 unset($this->RegisteredEvents[$EventName]["Hooks"][$HookIndex]);
2665 $UnhookCount++;
2666 }
2667 }
2668 }
2669
2670 # report number of callbacks unhooked to caller
2671 return $UnhookCount;
2672 }
2673
2684 public function SignalEvent($EventName, $Parameters = NULL)
2685 {
2686 $ReturnValue = NULL;
2687
2688 # if event has been registered
2689 if (isset($this->RegisteredEvents[$EventName]))
2690 {
2691 # set up default return value (if not NULL)
2692 switch ($this->RegisteredEvents[$EventName]["Type"])
2693 {
2694 case self::EVENTTYPE_CHAIN:
2695 $ReturnValue = $Parameters;
2696 break;
2697
2698 case self::EVENTTYPE_NAMED:
2699 $ReturnValue = array();
2700 break;
2701 }
2702
2703 # for each callback for this event
2704 foreach ($this->RegisteredEvents[$EventName]["Hooks"] as $Hook)
2705 {
2706 # invoke callback
2707 $Callback = $Hook["Callback"];
2708 $Result = ($Parameters !== NULL)
2709 ? call_user_func_array($Callback, $Parameters)
2710 : call_user_func($Callback);
2711
2712 # process return value based on event type
2713 switch ($this->RegisteredEvents[$EventName]["Type"])
2714 {
2715 case self::EVENTTYPE_CHAIN:
2716 if ($Result !== NULL)
2717 {
2718 foreach ($Parameters as $Index => $Value)
2719 {
2720 if (array_key_exists($Index, $Result))
2721 {
2722 $Parameters[$Index] = $Result[$Index];
2723 }
2724 }
2725 $ReturnValue = $Parameters;
2726 }
2727 break;
2728
2729 case self::EVENTTYPE_FIRST:
2730 if ($Result !== NULL)
2731 {
2732 $ReturnValue = $Result;
2733 break 2;
2734 }
2735 break;
2736
2737 case self::EVENTTYPE_NAMED:
2738 $CallbackName = is_array($Callback)
2739 ? (is_object($Callback[0])
2740 ? get_class($Callback[0])
2741 : $Callback[0])."::".$Callback[1]
2742 : $Callback;
2743 $ReturnValue[$CallbackName] = $Result;
2744 break;
2745
2746 default:
2747 break;
2748 }
2749 }
2750 }
2751 else
2752 {
2753 $this->LogError(self::LOGLVL_WARNING,
2754 "Unregistered event (".$EventName.") signaled by "
2755 .StdLib::GetMyCaller().".");
2756 }
2757
2758 # return value if any to caller
2759 return $ReturnValue;
2760 }
2761
2767 public function IsStaticOnlyEvent($EventName)
2768 {
2769 return isset($this->PeriodicEvents[$EventName]) ? TRUE : FALSE;
2770 }
2771
2782 public function EventWillNextRunAt($EventName, $Callback)
2783 {
2784 # if event is not a periodic event report failure to caller
2785 if (!array_key_exists($EventName, $this->EventPeriods)) { return FALSE; }
2786
2787 # retrieve last execution time for event if available
2788 $Signature = self::GetCallbackSignature($Callback);
2789 $LastRunTime = $this->DB->Query("SELECT LastRunAt FROM PeriodicEvents"
2790 ." WHERE Signature = '".addslashes($Signature)."'", "LastRunAt");
2791
2792 # if event was not found report failure to caller
2793 if ($LastRunTime === NULL) { return FALSE; }
2794
2795 # calculate next run time based on event period
2796 $NextRunTime = strtotime($LastRunTime) + $this->EventPeriods[$EventName];
2797
2798 # report next run time to caller
2799 return $NextRunTime;
2800 }
2801
2817 public function GetKnownPeriodicEvents()
2818 {
2819 # retrieve last execution times
2820 $this->DB->Query("SELECT * FROM PeriodicEvents");
2821 $LastRunTimes = $this->DB->FetchColumn("LastRunAt", "Signature");
2822
2823 # for each known event
2824 $Events = array();
2825 foreach ($this->KnownPeriodicEvents as $Signature => $Info)
2826 {
2827 # if last run time for event is available
2828 if (array_key_exists($Signature, $LastRunTimes))
2829 {
2830 # calculate next run time for event
2831 $LastRun = strtotime($LastRunTimes[$Signature]);
2832 $NextRun = $LastRun + $this->EventPeriods[$Info["Period"]];
2833 if ($Info["Period"] == "EVENT_PERIODIC") { $LastRun = FALSE; }
2834 }
2835 else
2836 {
2837 # set info to indicate run times are not known
2838 $LastRun = FALSE;
2839 $NextRun = FALSE;
2840 }
2841
2842 # add event info to list
2843 $Events[$Signature] = $Info;
2844 $Events[$Signature]["LastRun"] = $LastRun;
2845 $Events[$Signature]["NextRun"] = $NextRun;
2846 $Events[$Signature]["Parameters"] = NULL;
2847 }
2848
2849 # return list of known events to caller
2850 return $Events;
2851 }
2852
2859 public static function RunPeriodicEvent(
2860 $EventName, $Callback, $Parameters)
2861 {
2862 static $DB;
2863 if (!isset($DB)) { $DB = new Database(); }
2864
2865 # run event
2866 $ReturnVal = call_user_func_array($Callback, $Parameters);
2867
2868 # if event is already in database
2869 $Signature = self::GetCallbackSignature($Callback);
2870 if ($DB->Query("SELECT COUNT(*) AS EventCount FROM PeriodicEvents"
2871 ." WHERE Signature = '".addslashes($Signature)."'", "EventCount"))
2872 {
2873 # update last run time for event
2874 $DB->Query("UPDATE PeriodicEvents SET LastRunAt = "
2875 .(($EventName == "EVENT_PERIODIC")
2876 ? "'".date("Y-m-d H:i:s", time() + ($ReturnVal * 60))."'"
2877 : "NOW()")
2878 ." WHERE Signature = '".addslashes($Signature)."'");
2879 }
2880 else
2881 {
2882 # add last run time for event to database
2883 $DB->Query("INSERT INTO PeriodicEvents (Signature, LastRunAt) VALUES "
2884 ."('".addslashes($Signature)."', "
2885 .(($EventName == "EVENT_PERIODIC")
2886 ? "'".date("Y-m-d H:i:s", time() + ($ReturnVal * 60))."'"
2887 : "NOW()").")");
2888 }
2889 }
2890
2891 /*@)*/ /* Event Handling */
2892
2893
2894 # ---- Task Management ---------------------------------------------------
2895 /*@(*/
2897
2899 const PRIORITY_HIGH = 1;
2901 const PRIORITY_MEDIUM = 2;
2903 const PRIORITY_LOW = 3;
2905 const PRIORITY_BACKGROUND = 4;
2906
2911 public function &TaskMgr()
2912 {
2913 return $this->TaskMgr;
2914 }
2915
2929 public function QueueTask($Callback, $Parameters = NULL,
2930 $Priority = self::PRIORITY_LOW, $Description = "")
2931 {
2932 call_user_func_array([$this->TaskMgr, __FUNCTION__], func_get_args());
2933 }
2934
2953 public function QueueUniqueTask($Callback, $Parameters = NULL,
2954 $Priority = self::PRIORITY_LOW, $Description = "")
2955 {
2956 return call_user_func_array([$this->TaskMgr, __FUNCTION__], func_get_args());
2957 }
2958
2968 public function TaskIsInQueue($Callback, $Parameters = NULL)
2969 {
2970 return call_user_func_array([$this->TaskMgr, __FUNCTION__], func_get_args());
2971 }
2972
2978 public function GetTaskQueueSize($Priority = NULL)
2979 {
2980 return call_user_func_array([$this->TaskMgr, __FUNCTION__], func_get_args());
2981 }
2982
2990 public function GetQueuedTaskList($Count = 100, $Offset = 0)
2991 {
2992 return call_user_func_array([$this->TaskMgr, __FUNCTION__], func_get_args());
2993 }
2994
3008 public function GetQueuedTaskCount($Callback = NULL,
3009 $Parameters = NULL, $Priority = NULL, $Description = NULL)
3010 {
3011 return call_user_func_array([$this->TaskMgr, __FUNCTION__], func_get_args());
3012 }
3013
3021 public function GetRunningTaskList($Count = 100, $Offset = 0)
3022 {
3023 return call_user_func_array([$this->TaskMgr, __FUNCTION__], func_get_args());
3024 }
3025
3033 public function GetOrphanedTaskList($Count = 100, $Offset = 0)
3034 {
3035 return call_user_func_array([$this->TaskMgr, __FUNCTION__], func_get_args());
3036 }
3037
3042 public function GetOrphanedTaskCount()
3043 {
3044 return call_user_func_array([$this->TaskMgr, __FUNCTION__], func_get_args());
3045 }
3046
3052 public function ReQueueOrphanedTask($TaskId, $NewPriority = NULL)
3053 {
3054 call_user_func_array([$this->TaskMgr, __FUNCTION__], func_get_args());
3055 }
3056
3063 public function RequeueCurrentTask($NewValue = TRUE)
3064 {
3065 call_user_func_array([$this->TaskMgr, __FUNCTION__], func_get_args());
3066 }
3067
3073 public function DeleteTask($TaskId)
3074 {
3075 return call_user_func_array([$this->TaskMgr, __FUNCTION__], func_get_args());
3076 }
3077
3085 public function GetTask($TaskId)
3086 {
3087 return call_user_func_array([$this->TaskMgr, __FUNCTION__], func_get_args());
3088 }
3089
3100 public function TaskExecutionEnabled(
3101 $NewValue = DB_NOVALUE, $Persistent = FALSE)
3102 {
3103 return call_user_func_array([$this->TaskMgr, __FUNCTION__], func_get_args());
3104 }
3105
3114 public function MaxTasks($NewValue = DB_NOVALUE, $Persistent = FALSE)
3115 {
3116 return call_user_func_array([$this->TaskMgr, __FUNCTION__], func_get_args());
3117 }
3118
3126 public static function GetTaskCallbackSynopsis($TaskInfo)
3127 {
3128 return call_user_func_array(["AFTaskManager", __FUNCTION__], func_get_args());
3129 }
3130
3136 public function GetCurrentBackgroundPriority()
3137 {
3138 return call_user_func_array([$this->TaskMgr, __FUNCTION__], func_get_args());
3139 }
3140
3150 public function GetNextHigherBackgroundPriority($Priority = NULL)
3151 {
3152 return call_user_func_array([$this->TaskMgr, __FUNCTION__], func_get_args());
3153 }
3154
3164 public function GetNextLowerBackgroundPriority($Priority = NULL)
3165 {
3166 return call_user_func_array([$this->TaskMgr, __FUNCTION__], func_get_args());
3167 }
3168
3169 /*@)*/ /* Task Management */
3170
3171
3172 # ---- Clean URL Support -------------------------------------------------
3173 /*@(*/
3175
3202 public function AddCleanUrl($Pattern, $Page,
3203 $GetVars = NULL, $Template = NULL)
3204 {
3205 # save clean URL mapping parameters
3206 $this->CleanUrlMappings[] = array(
3207 "Pattern" => $Pattern,
3208 "Page" => $Page,
3209 "GetVars" => $GetVars,
3210 "AddedBy" => StdLib::GetCallerInfo(),
3211 );
3212
3213 # if replacement template specified
3214 if ($Template !== NULL)
3215 {
3216 # if GET parameters specified
3217 if (count($GetVars))
3218 {
3219 # retrieve all possible permutations of GET parameters
3220 $GetPerms = StdLib::ArrayPermutations(array_keys($GetVars));
3221
3222 # for each permutation of GET parameters
3223 foreach ($GetPerms as $VarPermutation)
3224 {
3225 # construct search pattern for permutation
3226 $SearchPattern = "/href=([\"'])index\\.php\\?P=".$Page;
3227 $GetVarSegment = "";
3228 foreach ($VarPermutation as $GetVar)
3229 {
3230 if (preg_match("%\\\$[0-9]+%", $GetVars[$GetVar]))
3231 {
3232 $GetVarSegment .= "&amp;".$GetVar."=((?:(?!\\1)[^&])+)";
3233 }
3234 else
3235 {
3236 $GetVarSegment .= "&amp;".$GetVar."=".$GetVars[$GetVar];
3237 }
3238 }
3239 $SearchPattern .= $GetVarSegment."\\1/i";
3240
3241 # if template is actually a callback
3242 if (is_callable($Template))
3243 {
3244 # add pattern to HTML output mod callbacks list
3245 $this->OutputModificationCallbacks[] = array(
3246 "Pattern" => $Pattern,
3247 "Page" => $Page,
3248 "SearchPattern" => $SearchPattern,
3249 "Callback" => $Template,
3250 );
3251 }
3252 else
3253 {
3254 # construct replacement string for permutation
3255 $Replacement = $Template;
3256 $Index = 2;
3257 foreach ($VarPermutation as $GetVar)
3258 {
3259 $Replacement = str_replace(
3260 "\$".$GetVar, "\$".$Index, $Replacement);
3261 $Index++;
3262 }
3263 $Replacement = "href=\"".$Replacement."\"";
3264
3265 # add pattern to HTML output modifications list
3266 $this->OutputModificationPatterns[] = $SearchPattern;
3267 $this->OutputModificationReplacements[] = $Replacement;
3268 }
3269 }
3270 }
3271 else
3272 {
3273 # construct search pattern
3274 $SearchPattern = "/href=\"index\\.php\\?P=".$Page."\"/i";
3275
3276 # if template is actually a callback
3277 if (is_callable($Template))
3278 {
3279 # add pattern to HTML output mod callbacks list
3280 $this->OutputModificationCallbacks[] = array(
3281 "Pattern" => $Pattern,
3282 "Page" => $Page,
3283 "SearchPattern" => $SearchPattern,
3284 "Callback" => $Template,
3285 );
3286 }
3287 else
3288 {
3289 # add simple pattern to HTML output modifications list
3290 $this->OutputModificationPatterns[] = $SearchPattern;
3291 $this->OutputModificationReplacements[] = "href=\"".$Template."\"";
3292 }
3293 }
3294 }
3295 }
3296
3302 public function CleanUrlIsMapped($Path)
3303 {
3304 foreach ($this->CleanUrlMappings as $Info)
3305 {
3306 if (preg_match($Info["Pattern"], $Path))
3307 {
3308 return TRUE;
3309 }
3310 }
3311 return FALSE;
3312 }
3313
3323 public function GetCleanUrlForPath($Path)
3324 {
3325 # the search patterns and callbacks require a specific format
3326 $Format = "href=\"".str_replace("&", "&amp;", $Path)."\"";
3327 $Search = $Format;
3328
3329 # perform any regular expression replacements on the search string
3330 $Search = preg_replace($this->OutputModificationPatterns,
3331 $this->OutputModificationReplacements, $Search);
3332
3333 # only run the callbacks if a replacement hasn't already been performed
3334 if ($Search == $Format)
3335 {
3336 # perform any callback replacements on the search string
3337 foreach ($this->OutputModificationCallbacks as $Info)
3338 {
3339 # make the information available to the callback
3340 $this->OutputModificationCallbackInfo = $Info;
3341
3342 # execute the callback
3343 $Search = preg_replace_callback($Info["SearchPattern"],
3344 array($this, "OutputModificationCallbackShell"),
3345 $Search);
3346 }
3347 }
3348
3349 # return the path untouched if no replacements were performed
3350 if ($Search == $Format)
3351 {
3352 return $Path;
3353 }
3354
3355 # remove the bits added to the search string to get it recognized by
3356 # the replacement expressions and callbacks
3357 $Result = substr($Search, 6, -1);
3358
3359 return $Result;
3360 }
3361
3368 public function GetUncleanUrlForPath($Path)
3369 {
3370 # for each clean URL mapping
3371 foreach ($this->CleanUrlMappings as $Info)
3372 {
3373 # if current path matches the clean URL pattern
3374 if (preg_match($Info["Pattern"], $Path, $Matches))
3375 {
3376 # the GET parameters for the URL, starting with the page name
3377 $GetVars = array("P" => $Info["Page"]);
3378
3379 # if additional $_GET variables specified for clean URL
3380 if ($Info["GetVars"] !== NULL)
3381 {
3382 # for each $_GET variable specified for clean URL
3383 foreach ($Info["GetVars"] as $VarName => $VarTemplate)
3384 {
3385 # start with template for variable value
3386 $Value = $VarTemplate;
3387
3388 # for each subpattern matched in current URL
3389 foreach ($Matches as $Index => $Match)
3390 {
3391 # if not first (whole) match
3392 if ($Index > 0)
3393 {
3394 # make any substitutions in template
3395 $Value = str_replace("$".$Index, $Match, $Value);
3396 }
3397 }
3398
3399 # add the GET variable
3400 $GetVars[$VarName] = $Value;
3401 }
3402 }
3403
3404 # return the unclean URL
3405 return "index.php?" . http_build_query($GetVars);
3406 }
3407 }
3408
3409 # return the path unchanged
3410 return $Path;
3411 }
3412
3418 public function GetCleanUrl()
3419 {
3420 return $this->GetCleanUrlForPath($this->GetUncleanUrl());
3421 }
3422
3427 public function GetUncleanUrl()
3428 {
3429 $GetVars = array("P" => $this->GetPageName()) + $_GET;
3430 return "index.php?" . http_build_query($GetVars);
3431 }
3432
3440 public function GetCleanUrlList()
3441 {
3442 return $this->CleanUrlMappings;
3443 }
3444
3457 public function AddPrefixForAlternateDomain($Domain, $Prefix)
3458 {
3459 $this->AlternateDomainPrefixes[$Domain] = $Prefix;
3460 }
3461
3462
3467 public function GetAlternateDomains()
3468 {
3469 return array_keys($this->AlternateDomainPrefixes);
3470 }
3471
3478 public function GetPrefixForAlternateDomain($Domain)
3479 {
3480 return isset($this->AlternateDomainPrefixes[$Domain]) ?
3481 $this->AlternateDomainPrefixes[$Domain] : NULL;
3482 }
3483
3484 /*@)*/ /* Clean URL Support */
3485
3486 # ---- Server Environment ------------------------------------------------
3487 /*@(*/
3489
3495 public static function SessionLifetime($NewValue = NULL)
3496 {
3497 if ($NewValue !== NULL)
3498 {
3499 self::$SessionLifetime = $NewValue;
3500 }
3501 return self::$SessionLifetime;
3502 }
3503
3509 public static function HtaccessSupport()
3510 {
3511 return isset($_SERVER["HTACCESS_SUPPORT"])
3512 || isset($_SERVER["REDIRECT_HTACCESS_SUPPORT"]);
3513 }
3514
3521 public static function UrlFingerprintingRewriteSupport()
3522 {
3523 return isset($_SERVER["URL_FINGERPRINTING_SUPPORT"])
3524 || isset($_SERVER["REDIRECT_URL_FINGERPRINTING_SUPPORT"]);
3525 }
3526
3533 public static function ScssRewriteSupport()
3534 {
3535 return isset($_SERVER["SCSS_REWRITE_SUPPORT"])
3536 || isset($_SERVER["REDIRECT_SCSS_REWRITE_SUPPORT"]);
3537 }
3538
3545 public static function JsMinRewriteSupport()
3546 {
3547 return isset($_SERVER["JSMIN_REWRITE_SUPPORT"])
3548 || isset($_SERVER["REDIRECT_JSMIN_REWRITE_SUPPORT"]);
3549 }
3550
3558 public static function RootUrl()
3559 {
3560 # return override value if one is set
3561 if (self::$RootUrlOverride !== NULL)
3562 {
3563 return self::$RootUrlOverride;
3564 }
3565
3566 # determine scheme name
3567 $Protocol = (isset($_SERVER["HTTPS"]) ? "https" : "http");
3568
3569 # if HTTP_HOST is preferred or SERVER_NAME points to localhost
3570 # and HTTP_HOST is set
3571 if ((self::$PreferHttpHost || ($_SERVER["SERVER_NAME"] == "127.0.0.1"))
3572 && isset($_SERVER["HTTP_HOST"]))
3573 {
3574 # use HTTP_HOST for domain name
3575 $DomainName = $_SERVER["HTTP_HOST"];
3576 }
3577 else
3578 {
3579 # use SERVER_NAME for domain name
3580 $DomainName = $_SERVER["SERVER_NAME"];
3581 }
3582
3583 # build URL root and return to caller
3584 return $Protocol."://".$DomainName;
3585 }
3586
3601 public static function RootUrlOverride($NewValue = self::NOVALUE)
3602 {
3603 if ($NewValue !== self::NOVALUE)
3604 {
3605 self::$RootUrlOverride = strlen(trim($NewValue)) ? $NewValue : NULL;
3606 }
3607 return self::$RootUrlOverride;
3608 }
3609
3619 public static function BaseUrl()
3620 {
3621 $BaseUrl = self::RootUrl().dirname($_SERVER["SCRIPT_NAME"]);
3622 if (substr($BaseUrl, -1) != "/") { $BaseUrl .= "/"; }
3623 return $BaseUrl;
3624 }
3625
3633 public static function FullUrl()
3634 {
3635 return self::RootUrl().$_SERVER["REQUEST_URI"];
3636 }
3637
3648 public static function PreferHttpHost($NewValue = NULL)
3649 {
3650 if ($NewValue !== NULL)
3651 {
3652 self::$PreferHttpHost = ($NewValue ? TRUE : FALSE);
3653 }
3654 return self::$PreferHttpHost;
3655 }
3656
3661 public static function BasePath()
3662 {
3663 $BasePath = dirname($_SERVER["SCRIPT_NAME"]);
3664
3665 if (substr($BasePath, -1) != "/")
3666 {
3667 $BasePath .= "/";
3668 }
3669
3670 return $BasePath;
3671 }
3672
3678 public static function GetScriptUrl()
3679 {
3680 if (array_key_exists("SCRIPT_URL", $_SERVER))
3681 {
3682 return $_SERVER["SCRIPT_URL"];
3683 }
3684 elseif (array_key_exists("REQUEST_URI", $_SERVER))
3685 {
3686 $Pieces = parse_url($_SERVER["REQUEST_URI"]);
3687 return isset($Pieces["path"]) ? $Pieces["path"] : NULL;
3688 }
3689 elseif (array_key_exists("REDIRECT_URL", $_SERVER))
3690 {
3691 return $_SERVER["REDIRECT_URL"];
3692 }
3693 else
3694 {
3695 return NULL;
3696 }
3697 }
3698
3707 public static function WasUrlRewritten($ScriptName="index.php")
3708 {
3709 # needed to get the path of the URL minus the query and fragment pieces
3710 $Components = parse_url(self::GetScriptUrl());
3711
3712 # if parsing was successful and a path is set
3713 if (is_array($Components) && isset($Components["path"]))
3714 {
3715 $BasePath = self::BasePath();
3716 $Path = $Components["path"];
3717
3718 # the URL was rewritten if the path isn't the base path, i.e., the
3719 # home page, and the file in the URL isn't the script generating the
3720 # page
3721 if ($BasePath != $Path && basename($Path) != $ScriptName)
3722 {
3723 return TRUE;
3724 }
3725 }
3726
3727 # the URL wasn't rewritten
3728 return FALSE;
3729 }
3730
3740 public static function ReachedViaAjax($NewSetting = NULL)
3741 {
3742 if ($NewSetting !== NULL)
3743 {
3744 self::$IsAjaxPageLoad = $NewSetting;
3745 }
3746
3747 if (isset(self::$IsAjaxPageLoad))
3748 {
3749 return self::$IsAjaxPageLoad;
3750 }
3751 elseif (isset($_SERVER["HTTP_X_REQUESTED_WITH"])
3752 && (strtolower($_SERVER["HTTP_X_REQUESTED_WITH"])
3753 == "xmlhttprequest"))
3754 {
3755 return TRUE;
3756 }
3757 else
3758 {
3759 return FALSE;
3760 }
3761 }
3762
3768 public static function GetFreeMemory()
3769 {
3770 return self::GetPhpMemoryLimit() - memory_get_usage(TRUE);
3771 }
3772
3778 public static function GetPercentFreeMemory()
3779 {
3780 return (self::GetFreeMemory() / self::GetPhpMemoryLimit()) * 100;
3781 }
3782
3788 public static function GetPhpMemoryLimit()
3789 {
3790 return self::ConvertPhpIniSizeToBytes(
3791 ini_get("memory_limit"));
3792 }
3793
3800 public static function GetPhpMaxUploadSize()
3801 {
3802 return min(
3803 self::ConvertPhpIniSizeToBytes(
3804 ini_get("post_max_size")),
3805 self::ConvertPhpIniSizeToBytes(
3806 ini_get("upload_max_filesize")));
3807 }
3808
3821 public function MaxExecutionTime($NewValue = NULL, $Persistent = FALSE)
3822 {
3823 if ($NewValue !== NULL)
3824 {
3825 $NewValue = max($NewValue, 5);
3826 ini_set("max_execution_time", $NewValue);
3827 set_time_limit($NewValue - $this->GetElapsedExecutionTime());
3828 $this->UpdateSetting("MaxExecTime", $NewValue, $Persistent);
3829 }
3830 return ini_get("max_execution_time");
3831 }
3832
3842 public function DatabaseSlowQueryThresholdForForeground(
3843 $NewValue = DB_NOVALUE, $Persistent = FALSE)
3844 {
3845 if (($NewValue !== DB_NOVALUE) && !$this->IsRunningInBackground())
3846 {
3847 Database::SlowQueryThreshold($NewValue);
3848 }
3849 return $this->UpdateSetting("DbSlowQueryThresholdForeground", $NewValue);
3850 }
3851
3861 public function DatabaseSlowQueryThresholdForBackground(
3862 $NewValue = DB_NOVALUE, $Persistent = FALSE)
3863 {
3864 if (($NewValue !== DB_NOVALUE) && !$this->IsRunningInBackground())
3865 {
3866 Database::SlowQueryThreshold($NewValue);
3867 }
3868 return $this->UpdateSetting("DbSlowQueryThresholdBackground", $NewValue);
3869 }
3870
3872 const MIN_DB_SLOW_QUERY_THRESHOLD = 2;
3873
3874 /*@)*/ /* Server Environment */
3875
3876
3877 # ---- Utility -----------------------------------------------------------
3878 /*@(*/
3880
3892 public function DownloadFile($FilePath, $FileName = NULL,
3893 $MimeType = NULL)
3894 {
3895 # check that file is readable
3896 if (!is_readable($FilePath))
3897 {
3898 return FALSE;
3899 }
3900
3901 # if file name was not supplied
3902 if ($FileName === NULL)
3903 {
3904 # extract file name from path
3905 $FileName = basename($FilePath);
3906 }
3907
3908 # if MIME type was not supplied
3909 if ($MimeType === NULL)
3910 {
3911 # attempt to determine MIME type
3912 $FInfoHandle = finfo_open(FILEINFO_MIME);
3913 if ($FInfoHandle)
3914 {
3915 $FInfoMime = finfo_file($FInfoHandle, $FilePath);
3916 finfo_close($FInfoHandle);
3917 if ($FInfoMime)
3918 {
3919 $MimeType = $FInfoMime;
3920 }
3921 }
3922
3923 # use default if unable to determine MIME type
3924 if ($MimeType === NULL)
3925 {
3926 $MimeType = "application/octet-stream";
3927 }
3928 }
3929
3930 # list of mime types where we allow the browser to decide on
3931 # how to display the item by omitting the Content-Disposition
3932 # header
3933 $InlineTypes = [
3934 "image/gif",
3935 "image/jpeg",
3936 "image/png",
3937 "application/pdf",
3938 ];
3939
3940 # set headers to download file
3941 header("Content-Type: ".$MimeType);
3942 header("Content-Length: ".filesize($FilePath));
3943 if ($this->CleanUrlRewritePerformed &&
3944 !in_array($MimeType, $InlineTypes))
3945 {
3946 header('Content-Disposition: attachment; filename="'.$FileName.'"');
3947 }
3948
3949 # make sure that apache does not attempt to compress file
3950 apache_setenv('no-gzip', '1');
3951
3952 # send file to user, but unbuffered to avoid memory issues
3953 $this->AddUnbufferedCallback(function ($File)
3954 {
3955 $BlockSize = 512000;
3956
3957 $Handle = @fopen($File, "rb");
3958 if ($Handle === FALSE)
3959 {
3960 return;
3961 }
3962
3963 # (close out session, making it read-only, so that session file
3964 # lock is released and others are not potentially hanging
3965 # waiting for it while the download completes)
3966 session_write_close();
3967
3968 while (!feof($Handle))
3969 {
3970 print fread($Handle, $BlockSize);
3971 flush();
3972 }
3973
3974 fclose($Handle);
3975 }, array($FilePath));
3976
3977 # prevent HTML output that might interfere with download
3978 $this->SuppressHTMLOutput();
3979
3980 # set flag to indicate not to log a slow page load in case client
3981 # connection delays PHP execution because of header
3982 $this->DoNotLogSlowPageLoad = TRUE;
3983
3984 # report no errors found to caller
3985 return TRUE;
3986 }
3987
4000 public function GetLock($LockName, $Wait = TRUE)
4001 {
4002 # assume we will not get a lock
4003 $GotLock = FALSE;
4004
4005 # clear out any stale locks
4006 static $CleanupHasBeenDone = FALSE;
4007 if (!$CleanupHasBeenDone)
4008 {
4009 # (margin for clearing stale locks is twice the known
4010 # maximum PHP execution time, because the max time
4011 # techinically does not include external operations
4012 # like database queries)
4013 $ClearLocksObtainedBefore = date(StdLib::SQL_DATE_FORMAT,
4014 (time() - ($this->MaxExecutionTime() * 2)));
4015 $this->DB->Query("DELETE FROM AF_Locks WHERE"
4016 ." ObtainedAt < '".$ClearLocksObtainedBefore."' AND"
4017 ." LockName = '".addslashes($LockName)."'");
4018 }
4019
4020 do
4021 {
4022 # lock database table so nobody else can try to get a lock
4023 $this->DB->Query("LOCK TABLES AF_Locks WRITE");
4024
4025 # look for lock with specified name
4026 $FoundCount = $this->DB->Query("SELECT COUNT(*) AS FoundCount"
4027 ." FROM AF_Locks WHERE LockName = '"
4028 .addslashes($LockName)."'", "FoundCount");
4029 $LockFound = ($FoundCount > 0) ? TRUE : FALSE;
4030
4031 # if lock found
4032 if ($LockFound)
4033 {
4034 # unlock database tables
4035 $this->DB->Query("UNLOCK TABLES");
4036
4037 # if blocking was requested
4038 if ($Wait)
4039 {
4040 # wait to give someone else a chance to release lock
4041 sleep(2);
4042 }
4043 }
4044 // @codingStandardsIgnoreStart
4045 // (because phpcs does not correctly handle do-while loops)
4046 # while lock was found and blocking was requested
4047 } while ($LockFound && $Wait);
4048 // @codingStandardsIgnoreEnd
4049
4050 # if lock was not found
4051 if (!$LockFound)
4052 {
4053 # get our lock
4054 $this->DB->Query("INSERT INTO AF_Locks (LockName) VALUES ('"
4055 .addslashes($LockName)."')");
4056 $GotLock = TRUE;
4057
4058 # unlock database tables
4059 $this->DB->Query("UNLOCK TABLES");
4060 }
4061
4062 # report to caller whether lock was obtained
4063 return $GotLock;
4064 }
4065
4073 public function ReleaseLock($LockName)
4074 {
4075 # release any existing locks
4076 $this->DB->Query("DELETE FROM AF_Locks WHERE LockName = '"
4077 .addslashes($LockName)."'");
4078
4079 # report to caller whether existing lock was released
4080 return $this->DB->NumRowsAffected() ? TRUE : FALSE;
4081 }
4082
4101 public function BeginAjaxResponse(
4102 $ResponseType = "JSON",
4103 $CloseSession = TRUE)
4104 {
4105 switch ($ResponseType)
4106 {
4107 case "JSON":
4108 $this->SuppressHTMLOutput();
4109 header("Content-Type: application/json; charset="
4110 .$GLOBALS["G_SysConfig"]->DefaultCharacterSet(), TRUE);
4111 break;
4112 case "XML":
4113 $this->SuppressHTMLOutput();
4114 header("Content-Type: application/xml; charset="
4115 .$GLOBALS["G_SysConfig"]->DefaultCharacterSet(), TRUE);
4116 break;
4117 case "HTML":
4118 break;
4119 default:
4120 throw new Exception(
4121 "Unsupported response type: ".$ResponseType);
4122 }
4123
4124 $this->SetBrowserCacheExpirationTime(
4125 self::$DefaultBrowserCacheExpiration);
4126
4127 if ($CloseSession)
4128 {
4129 session_write_close();
4130 }
4131
4132 }
4133
4139 public function SetBrowserCacheExpirationTime($MaxAge)
4140 {
4141 # set headers to control caching
4142 header("Expires: ".gmdate("D, d M Y H:i:s \G\M\T", time()+$MaxAge));
4143 header("Cache-Control: private, max-age=".$MaxAge);
4144 header("Pragma:");
4145 }
4146
4153 public static function ConvertPhpIniSizeToBytes($Size)
4154 {
4155 $Str = strtoupper($Size);
4156
4157 # trim off 'B' suffix for KB/MB/GB
4158 if (substr($Str, -1) == "B")
4159 {
4160 $Str = substr($Str, 0, strlen($Str) - 1);
4161 }
4162
4163 # pull out the numeric part of our setting
4164 $Val = intval($Str);
4165
4166 # adjust it based on the units
4167 switch (substr($Str, -1))
4168 {
4169 case "G":
4170 $Val *= 1073741824;
4171 break;
4172
4173 case "M":
4174 $Val *= 1048576;
4175 break;
4176
4177 case "K":
4178 $Val *= 1024;
4179 break;
4180
4181 default:
4182 break;
4183 }
4184
4185 return $Val;
4186 }
4187
4194 public function SessionInUse($InUse = NULL)
4195 {
4196 if ($InUse !== NULL)
4197 {
4198 $this->SessionInUse = $InUse;
4199 }
4200
4201 return $this->SessionInUse;
4202 }
4203
4204 /*@)*/ /* Utility */
4205
4206
4207 # ---- Backward Compatibility --------------------------------------------
4208 /*@(*/
4210
4217 public function FindCommonTemplate($BaseName)
4218 {
4219 return $this->FindFile(
4220 $this->IncludeDirList, $BaseName, array("tpl", "html"));
4221 }
4222
4223 /*@)*/ /* Backward Compatibility */
4224
4225
4226 # ---- PRIVATE INTERFACE -------------------------------------------------
4227
4228 private $AdditionalRequiredUIFiles = array();
4229 private $AlternateDomainPrefixes = array();
4230 private $BrowserDetectFunc;
4231 private $CacheCurrentPage = TRUE;
4232 private $CleanUrlMappings = array();
4233 private $CleanUrlRewritePerformed = FALSE;
4234 private $ContextFilters = array(
4235 self::CONTEXT_START => TRUE,
4236 self::CONTEXT_PAGE => array("H_"),
4237 self::CONTEXT_COMMON => array("H_"),
4238 );
4239 private $CssUrlFingerprintPath;
4240 private $DB;
4241 private $DefaultPage = "Home";
4242 private $DoNotMinimizeList = array();
4243 private $DoNotLogSlowPageLoad = FALSE;
4244 private $EnvIncludes = array();
4245 private $ExecutionStartTime;
4246 private $FoundUIFiles = array();
4247 private $HtmlCharset = "UTF-8";
4248 private $InterfaceSettings = array();
4249 private $JSMinimizerJavaScriptPackerAvailable = FALSE;
4250 private $JSMinimizerJShrinkAvailable = TRUE;
4251 private $JumpToPage = NULL;
4252 private $JumpToPageDelay = 0;
4253 private $LogFileName = "local/logs/site.log";
4254 private $MetaTags = array();
4255 private $OutputModificationCallbackInfo;
4256 private $OutputModificationCallbacks = array();
4257 private $OutputModificationPatterns = array();
4258 private $OutputModificationReplacements = array();
4259 private $PageCacheTags = array();
4260 private $PageName;
4261 private $PostProcessingFuncs = array();
4262 private $RunningInBackground = FALSE;
4263 private $SavedContext;
4264 private $SaveTemplateLocationCache = FALSE;
4265 private $SessionStorage;
4266 private $SessionGcProbability;
4267 private $Settings;
4268 private $SuppressHTML = FALSE;
4269 private $SuppressStdPageStartAndEnd = FALSE;
4270 private $TaskMgr;
4271 private $TemplateLocationCache;
4272 private $TemplateLocationCacheInterval = 60; # in minutes
4273 private $TemplateLocationCacheExpiration;
4274 private $UnbufferedCallbacks = array();
4275 private $UniqueMetaTags = array();
4276 private $UrlFingerprintBlacklist = array();
4277 private $UseBaseTag = FALSE;
4278 private $SessionInUse = FALSE;
4279
4280 private static $ActiveUI = "default";
4281 private static $AppName = "ScoutAF";
4282 private static $DefaultBrowserCacheExpiration = 30; # in seconds
4283 private static $DefaultUI = "default";
4284 private static $IsAjaxPageLoad;
4285 private static $JSMinCacheDir = "local/data/caches/JSMin";
4286 private static $NamespaceDirectories = array();
4287 private static $ObjectDirectories = array();
4288 private static $ObjectLocationCache;
4289 private static $ObjectLocationCacheInterval = 60;
4290 private static $ObjectLocationCacheExpiration;
4291 private static $PreferHttpHost = FALSE;
4292 private static $RootUrlOverride = NULL;
4293 private static $SaveObjectLocationCache = FALSE;
4294 private static $ScssCacheDir = "local/data/caches/SCSS";
4295 private static $SessionLifetime = 1440; # in seconds
4296 private static $SuppressSessionInitialization = FALSE;
4297 private static $UserInterfaceListCache = array();
4298 private static $UserInterfacePathsCache = array();
4299
4300 # offset used to generate page cache tag IDs from numeric tags
4301 const PAGECACHETAGIDOFFSET = 100000;
4302
4303 # minimum expired session garbage collection probability
4304 const MIN_GC_PROBABILITY = 0.01;
4305
4310 private $NoTSR = FALSE;
4311
4312 private $RegisteredEvents = array();
4313 private $KnownPeriodicEvents = array();
4314 private $PeriodicEvents = array(
4315 "EVENT_HOURLY" => self::EVENTTYPE_DEFAULT,
4316 "EVENT_DAILY" => self::EVENTTYPE_DEFAULT,
4317 "EVENT_WEEKLY" => self::EVENTTYPE_DEFAULT,
4318 "EVENT_MONTHLY" => self::EVENTTYPE_DEFAULT,
4319 "EVENT_PERIODIC" => self::EVENTTYPE_NAMED,
4320 );
4321 private $EventPeriods = array(
4322 "EVENT_HOURLY" => 3600,
4323 "EVENT_DAILY" => 86400,
4324 "EVENT_WEEKLY" => 604800,
4325 "EVENT_MONTHLY" => 2592000,
4326 "EVENT_PERIODIC" => 0,
4327 );
4328 private $UIEvents = array(
4329 "EVENT_PAGE_LOAD" => self::EVENTTYPE_DEFAULT,
4330 "EVENT_PHP_FILE_LOAD" => self::EVENTTYPE_CHAIN,
4331 "EVENT_PHP_FILE_LOAD_COMPLETE" => self::EVENTTYPE_DEFAULT,
4332 "EVENT_HTML_FILE_LOAD" => self::EVENTTYPE_CHAIN,
4333 "EVENT_HTML_FILE_LOAD_COMPLETE" => self::EVENTTYPE_DEFAULT,
4334 "EVENT_PAGE_OUTPUT_FILTER" => self::EVENTTYPE_CHAIN,
4335 );
4336
4341 private function LoadSettings()
4342 {
4343 # read settings in from database
4344 $this->DB->Query("SELECT * FROM ApplicationFrameworkSettings");
4345 $this->Settings = $this->DB->FetchRow();
4346
4347 # if settings were not previously initialized
4348 if ($this->Settings === FALSE)
4349 {
4350 # initialize settings in database
4351 $this->DB->Query("INSERT INTO ApplicationFrameworkSettings"
4352 ." (LastTaskRunAt) VALUES ('2000-01-02 03:04:05')");
4353
4354 # read new settings in from database
4355 $this->DB->Query("SELECT * FROM ApplicationFrameworkSettings");
4356 $this->Settings = $this->DB->FetchRow();
4357
4358 # bail out if reloading new settings failed
4359 if ($this->Settings === FALSE)
4360 {
4361 throw new Exception(
4362 "Unable to load application framework settings.");
4363 }
4364 }
4365
4366 # if base path was not previously set or we appear to have moved
4367 if (!array_key_exists("BasePath", $this->Settings)
4368 || (!strlen($this->Settings["BasePath"]))
4369 || (!array_key_exists("BasePathCheck", $this->Settings))
4370 || (__FILE__ != $this->Settings["BasePathCheck"]))
4371 {
4372 # attempt to extract base path from Apache .htaccess file
4373 if (is_readable(".htaccess"))
4374 {
4375 $Lines = file(".htaccess");
4376 foreach ($Lines as $Line)
4377 {
4378 if (preg_match("/\\s*RewriteBase\\s+/", $Line))
4379 {
4380 $Pieces = preg_split(
4381 "/\\s+/", $Line, NULL, PREG_SPLIT_NO_EMPTY);
4382 $BasePath = $Pieces[1];
4383 }
4384 }
4385 }
4386
4387 # if base path was found
4388 if (isset($BasePath))
4389 {
4390 # save base path locally
4391 $this->Settings["BasePath"] = $BasePath;
4392
4393 # save base path to database
4394 $this->DB->Query("UPDATE ApplicationFrameworkSettings"
4395 ." SET BasePath = '".addslashes($BasePath)."'"
4396 .", BasePathCheck = '".addslashes(__FILE__)."'");
4397 }
4398 }
4399
4400 # retrieve template location cache
4401 $this->TemplateLocationCache = unserialize(
4402 $this->Settings["TemplateLocationCache"]);
4403 $this->TemplateLocationCacheInterval =
4404 $this->Settings["TemplateLocationCacheInterval"];
4405 $this->TemplateLocationCacheExpiration =
4406 strtotime($this->Settings["TemplateLocationCacheExpiration"]);
4407
4408 # if template location cache looks invalid or has expired
4409 $CurrentTime = time();
4410 if (!is_array($this->TemplateLocationCache)
4411 || !count($this->TemplateLocationCache)
4412 || ($CurrentTime >= $this->TemplateLocationCacheExpiration))
4413 {
4414 # clear cache and reset cache expiration
4415 $this->TemplateLocationCache = array();
4416 $this->TemplateLocationCacheExpiration =
4417 $CurrentTime + ($this->TemplateLocationCacheInterval * 60);
4418 $this->SaveTemplateLocationCache = TRUE;
4419 }
4420
4421 # retrieve object location cache
4422 self::$ObjectLocationCache =
4423 unserialize($this->Settings["ObjectLocationCache"]);
4424 self::$ObjectLocationCacheInterval =
4425 $this->Settings["ObjectLocationCacheInterval"];
4426 self::$ObjectLocationCacheExpiration =
4427 strtotime($this->Settings["ObjectLocationCacheExpiration"]);
4428
4429 # if object location cache looks invalid or has expired
4430 if (!is_array(self::$ObjectLocationCache)
4431 || !count(self::$ObjectLocationCache)
4432 || ($CurrentTime >= self::$ObjectLocationCacheExpiration))
4433 {
4434 # clear cache and reset cache expiration
4435 self::$ObjectLocationCache = array();
4436 self::$ObjectLocationCacheExpiration =
4437 $CurrentTime + (self::$ObjectLocationCacheInterval * 60);
4438 self::$SaveObjectLocationCache = TRUE;
4439 }
4440 }
4441
4448 private function RewriteCleanUrls($PageName)
4449 {
4450 # if URL rewriting is supported by the server
4451 if ($this->HtaccessSupport())
4452 {
4453 # retrieve current URL and remove base path if present
4454 $Url = $this->GetPageLocation();
4455
4456 # for each clean URL mapping
4457 foreach ($this->CleanUrlMappings as $Info)
4458 {
4459 # if current URL matches clean URL pattern
4460 if (preg_match($Info["Pattern"], $Url, $Matches))
4461 {
4462 # set new page
4463 $PageName = $Info["Page"];
4464
4465 # if $_GET variables specified for clean URL
4466 if ($Info["GetVars"] !== NULL)
4467 {
4468 # for each $_GET variable specified for clean URL
4469 foreach ($Info["GetVars"] as $VarName => $VarTemplate)
4470 {
4471 # start with template for variable value
4472 $Value = $VarTemplate;
4473
4474 # for each subpattern matched in current URL
4475 foreach ($Matches as $Index => $Match)
4476 {
4477 # if not first (whole) match
4478 if ($Index > 0)
4479 {
4480 # make any substitutions in template
4481 $Value = str_replace("$".$Index, $Match, $Value);
4482 }
4483 }
4484
4485 # set $_GET variable
4486 $_GET[$VarName] = $Value;
4487 }
4488 }
4489
4490 # set flag indicating clean URL mapped
4491 $this->CleanUrlRewritePerformed = TRUE;
4492
4493 # stop looking for a mapping
4494 break;
4495 }
4496 }
4497 }
4498
4499 # return (possibly) updated page name to caller
4500 return $PageName;
4501 }
4502
4515 private function RewriteAlternateDomainUrls($Html)
4516 {
4517 # if we were loaded via an alternate domain, and we have a
4518 # RootUrlOverride configured to tell us which domain is the
4519 # primary, and if rewriting support is enabled, then we can
4520 # handle URL Rewriting
4521 if ($this->LoadedViaAlternateDomain() &&
4522 self::$RootUrlOverride !== NULL &&
4523 $this->HtaccessSupport())
4524 {
4525 # pull out the configured prefix for this domain
4526 $VHost = $_SERVER["SERVER_NAME"];
4527 $ThisPrefix = $this->AlternateDomainPrefixes[$VHost];
4528
4529 # get the URL for the primary domain, including the base path
4530 # (usually the part between the host name and the PHP file name)
4531 $RootUrl = $this->RootUrl().self::BasePath();
4532
4533 # and figure out what protcol we were using
4534 $Protocol = (isset($_SERVER["HTTPS"]) ? "https" : "http");
4535
4536 # NB: preg_replace iterates through the configured
4537 # search/replacement pairs, such that the second one
4538 # runs after the first and so on
4539
4540 # the first n-1 patterns below convert any relative
4541 # links in the generated HTML to absolute links using
4542 # our primary domain (e.g., for stylesheets, javascript,
4543 # images, etc)
4544
4545 # the nth pattern looks for links that live within the
4546 # path subtree specified by our configured prefix on
4547 # our primary domain, then replaces them with equivalent
4548 # links on our secondary domain
4549
4550 # for example, if our primary domain is
4551 # example.com/MySite and our secondary domain is
4552 # things.example.org/MySite with 'things' as the
4553 # configured prefix, then this last pattern will look
4554 # for example.com/MySite/things and replace it with
4555 # things.example.org/MySite
4556 $RelativePathPatterns = array(
4557 "%src=\"(?!http://|https://)%i",
4558 "%src='(?!http://|https://)%i",
4559 "%href=\"(?!http://|https://)%i",
4560 "%href='(?!http://|https://)%i",
4561 "%action=\"(?!http://|https://)%i",
4562 "%action='(?!http://|https://)%i",
4563 "%@import\s+url\(\"(?!http://|https://)%i",
4564 "%@import\s+url\('(?!http://|https://)%i",
4565 "%src:\s+url\(\"(?!http://|https://)%i",
4566 "%src:\s+url\('(?!http://|https://)%i",
4567 "%@import\s+\"(?!http://|https://)%i",
4568 "%@import\s+'(?!http://|https://)%i",
4569 "%".preg_quote($RootUrl.$ThisPrefix."/", "%")."%",
4570 );
4571 $RelativePathReplacements = array(
4572 "src=\"".$RootUrl,
4573 "src='".$RootUrl,
4574 "href=\"".$RootUrl,
4575 "href='".$RootUrl,
4576 "action=\"".$RootUrl,
4577 "action='".$RootUrl,
4578 "@import url(\"".$RootUrl,
4579 "@import url('".$RootUrl,
4580 "src: url(\"".$RootUrl,
4581 "src: url('".$RootUrl,
4582 "@import \"".$RootUrl,
4583 "@import '".$RootUrl,
4584 $Protocol."://".$VHost.self::BasePath(),
4585 );
4586
4587 $NewHtml = preg_replace(
4588 $RelativePathPatterns,
4589 $RelativePathReplacements,
4590 $Html);
4591
4592 # check to make sure relative path fixes didn't fail
4593 $Html = $this->CheckOutputModification(
4594 $Html, $NewHtml,
4595 "alternate domain substitutions");
4596 }
4597
4598 return $Html;
4599 }
4600
4605 private function LoadedViaAlternateDomain()
4606 {
4607 return (isset($_SERVER["SERVER_NAME"]) &&
4608 isset($this->AlternateDomainPrefixes[$_SERVER["SERVER_NAME"]])) ?
4609 TRUE : FALSE ;
4610 }
4611
4630 private function FindFile($DirectoryList, $BaseName,
4631 $PossibleSuffixes = NULL, $PossiblePrefixes = NULL)
4632 {
4633 # generate template cache index for this page
4634 $CacheIndex = md5(serialize($DirectoryList))
4635 .self::$DefaultUI.self::$ActiveUI.$BaseName;
4636
4637 # if caching is enabled and we have cached location
4638 if (($this->TemplateLocationCacheInterval > 0)
4639 && array_key_exists($CacheIndex,
4640 $this->TemplateLocationCache))
4641 {
4642 # use template location from cache
4643 $FoundFileName = $this->TemplateLocationCache[$CacheIndex];
4644 }
4645 else
4646 {
4647 # if suffixes specified and base name does not include suffix
4648 if ($PossibleSuffixes !== NULL
4649 && count($PossibleSuffixes)
4650 && !preg_match("/\.[a-zA-Z0-9]+$/", $BaseName))
4651 {
4652 # add versions of file names with suffixes to file name list
4653 $FileNames = array();
4654 foreach ($PossibleSuffixes as $Suffix)
4655 {
4656 $FileNames[] = $BaseName.".".$Suffix;
4657 }
4658 }
4659 else
4660 {
4661 # use base name as file name
4662 $FileNames = array($BaseName);
4663 }
4664
4665 # if prefixes specified
4666 if ($PossiblePrefixes !== NULL && count($PossiblePrefixes))
4667 {
4668 # add versions of file names with prefixes to file name list
4669 $NewFileNames = array();
4670 foreach ($FileNames as $FileName)
4671 {
4672 foreach ($PossiblePrefixes as $Prefix)
4673 {
4674 $NewFileNames[] = $Prefix.$FileName;
4675 }
4676 }
4677 $FileNames = $NewFileNames;
4678 }
4679
4680 # expand directory list to include variants
4681 $DirectoryList = $this->ExpandDirectoryList($DirectoryList);
4682
4683 # for each possible location
4684 $FoundFileName = NULL;
4685 foreach ($DirectoryList as $Dir)
4686 {
4687 # for each possible file name
4688 foreach ($FileNames as $File)
4689 {
4690 # if template is found at location
4691 if (file_exists($Dir.$File))
4692 {
4693 # save full template file name and stop looking
4694 $FoundFileName = $Dir.$File;
4695 break 2;
4696 }
4697 }
4698 }
4699
4700 # save location in cache
4701 $this->TemplateLocationCache[$CacheIndex]
4702 = $FoundFileName;
4703
4704 # set flag indicating that cache should be saved
4705 $this->SaveTemplateLocationCache = TRUE;
4706 }
4707
4708 # return full template file name to caller
4709 return $FoundFileName;
4710 }
4711
4718 private function ExpandDirectoryList($DirList)
4719 {
4720 # generate lookup for supplied list
4721 $ExpandedListKey = md5(serialize($DirList)
4722 .self::$DefaultUI.self::$ActiveUI);
4723
4724 # if we already have expanded version of supplied list
4725 if (isset($this->ExpandedDirectoryLists[$ExpandedListKey]))
4726 {
4727 # return expanded version to caller
4728 return $this->ExpandedDirectoryLists[$ExpandedListKey];
4729 }
4730
4731 # for each directory in list
4732 $ExpDirList = array();
4733 foreach ($DirList as $Dir)
4734 {
4735 # if directory includes substitution keyword
4736 if ((strpos($Dir, "%DEFAULTUI%") !== FALSE)
4737 || (strpos($Dir, "%ACTIVEUI%") !== FALSE))
4738 {
4739 # start with empty new list segment
4740 $ExpDirListSegment = array();
4741
4742 # use default values for initial parent
4743 $ParentInterface = array(self::$ActiveUI, self::$DefaultUI);
4744
4745 do
4746 {
4747 # substitute in for keyword on parent
4748 $CurrDir = str_replace(array("%ACTIVEUI%", "%DEFAULTUI%"),
4749 $ParentInterface, $Dir);
4750
4751 # add local version of parent directory to new list segment
4752 $ExpDirListSegment[] = "local/".$CurrDir;
4753
4754 # add parent directory to new list segment
4755 $ExpDirListSegment[] = $CurrDir;
4756
4757 # look for new parent interface
4758 $ParentInterface = $this->GetInterfaceSetting(
4759 $CurrDir, "ParentInterface");
4760
4761 # repeat if parent is available
4762 } while (strlen($ParentInterface));
4763
4764 # add new list segment to expanded list
4765 $ExpDirList = array_merge($ExpDirList, $ExpDirListSegment);
4766 }
4767 else
4768 {
4769 # add local version of directory to expanded list
4770 $ExpDirList[] = "local/".$Dir;
4771
4772 # add directory to expanded list
4773 $ExpDirList[] = $Dir;
4774 }
4775 }
4776
4777 # return expanded version to caller
4778 $this->ExpandedDirectoryLists[$ExpandedListKey] = $ExpDirList;
4779 return $this->ExpandedDirectoryLists[$ExpandedListKey];
4780 }
4781
4790 private function GetInterfaceSetting(
4791 $InterfaceDir, $SettingName = NULL)
4792 {
4793 # extract canonical interface name and base interface directory
4794 preg_match("%(.*interface/)([^/]+)%", $InterfaceDir, $Matches);
4795 $InterfaceDir = (count($Matches) > 2)
4796 ? $Matches[1].$Matches[2] : $InterfaceDir;
4797 $InterfaceName = (count($Matches) > 2)
4798 ? $Matches[2] : "UNKNOWN";
4799
4800 # if we do not have settings for interface
4801 if (!isset($this->InterfaceSettings[$InterfaceName]))
4802 {
4803 # load default values for settings
4804 $this->InterfaceSettings[$InterfaceName] = array(
4805 "Source" => "",
4806 );
4807 }
4808
4809 # if directory takes precedence over existing settings source
4810 # ("takes precendence" == is more local == longer directory length)
4811 if (strlen($InterfaceDir)
4812 > strlen($this->InterfaceSettings[$InterfaceName]["Source"]))
4813 {
4814 # if settings file exists in directory
4815 $SettingsFile = $InterfaceDir."/interface.ini";
4816 if (is_readable($SettingsFile))
4817 {
4818 # read in values from file
4819 $NewSettings = parse_ini_file($SettingsFile);
4820
4821 # merge in values with existing settings
4822 $this->InterfaceSettings[$InterfaceName] = array_merge(
4823 $this->InterfaceSettings[$InterfaceName], $NewSettings);
4824
4825 # save new source of settings
4826 $this->InterfaceSettings[$InterfaceName]["Source"] = $InterfaceDir;
4827 }
4828 }
4829
4830 # return interface settings to caller
4831 return $SettingName
4832 ? (isset($this->InterfaceSettings[$InterfaceName][$SettingName])
4833 ? $this->InterfaceSettings[$InterfaceName][$SettingName]
4834 : NULL)
4835 : $this->InterfaceSettings[$InterfaceName];
4836 }
4837
4846 private function CompileScssFile($SrcFile)
4847 {
4848 # build path to CSS file
4849 $DstFile = self::$ScssCacheDir."/".dirname($SrcFile)
4850 ."/".basename($SrcFile);
4851 $DstFile = substr_replace($DstFile, "css", -4);
4852
4853 # if SCSS file is newer than CSS file
4854 if (!file_exists($DstFile)
4855 || (filemtime($SrcFile) > filemtime($DstFile)))
4856 {
4857 # attempt to create CSS cache subdirectory if not present
4858 if (!is_dir(dirname($DstFile)))
4859 {
4860 @mkdir(dirname($DstFile), 0777, TRUE);
4861 }
4862
4863 # if CSS cache directory and CSS file path appear writable
4864 static $CacheDirIsWritable;
4865 if (!isset($CacheDirIsWritable))
4866 { $CacheDirIsWritable = is_writable(self::$ScssCacheDir); }
4867 if (is_writable($DstFile)
4868 || (!file_exists($DstFile) && $CacheDirIsWritable))
4869 {
4870 # load SCSS and compile to CSS
4871 $ScssCode = file_get_contents($SrcFile);
4872 $ScssCompiler = new scssc();
4873 $ScssCompiler->setFormatter($this->GenerateCompactCss()
4874 ? "scss_formatter_compressed" : "scss_formatter");
4875 try
4876 {
4877 $CssCode = $ScssCompiler->compile($ScssCode);
4878
4879 # add fingerprinting for URLs in CSS
4880 $this->CssUrlFingerprintPath = dirname($SrcFile);
4881 $CssCode = preg_replace_callback(
4882 "/url\((['\"]*)(.+)\.([a-z]+)(['\"]*)\)/",
4883 array($this, "CssUrlFingerprintInsertion"),
4884 $CssCode);
4885
4886 # strip out comments from CSS (if requested)
4887 if ($this->GenerateCompactCss())
4888 {
4889 $CssCode = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!',
4890 '', $CssCode);
4891 }
4892
4893 # write out CSS file
4894 file_put_contents($DstFile, $CssCode);
4895 }
4896 catch (Exception $Ex)
4897 {
4898 $this->LogError(self::LOGLVL_ERROR,
4899 "Error compiling SCSS file ".$SrcFile.": "
4900 .$Ex->getMessage());
4901 $DstFile = NULL;
4902 }
4903 }
4904 else
4905 {
4906 # log error and set CSS file path to indicate failure
4907 $this->LogError(self::LOGLVL_ERROR,
4908 "Unable to write out CSS file (compiled from SCSS) to "
4909 .$DstFile);
4910 $DstFile = NULL;
4911 }
4912 }
4913
4914 # return CSS file path to caller
4915 return $DstFile;
4916 }
4917
4925 private function MinimizeJavascriptFile($SrcFile)
4926 {
4927 # bail out if file is on exclusion list
4928 foreach ($this->DoNotMinimizeList as $DNMFile)
4929 {
4930 if (($SrcFile == $DNMFile) || (basename($SrcFile) == $DNMFile))
4931 {
4932 return NULL;
4933 }
4934 }
4935
4936 # build path to minimized file
4937 $DstFile = self::$JSMinCacheDir."/".dirname($SrcFile)
4938 ."/".basename($SrcFile);
4939 $DstFile = substr_replace($DstFile, ".min", -3, 0);
4940
4941 # if original file is newer than minimized file
4942 if (!file_exists($DstFile)
4943 || (filemtime($SrcFile) > filemtime($DstFile)))
4944 {
4945 # attempt to create cache subdirectory if not present
4946 if (!is_dir(dirname($DstFile)))
4947 {
4948 @mkdir(dirname($DstFile), 0777, TRUE);
4949 }
4950
4951 # if cache directory and minimized file path appear writable
4952 static $CacheDirIsWritable;
4953 if (!isset($CacheDirIsWritable))
4954 { $CacheDirIsWritable = is_writable(self::$JSMinCacheDir); }
4955 if (is_writable($DstFile)
4956 || (!file_exists($DstFile) && $CacheDirIsWritable))
4957 {
4958 # load JavaScript code
4959 $Code = file_get_contents($SrcFile);
4960
4961 # decide which minimizer to use
4962 if ($this->JSMinimizerJavaScriptPackerAvailable
4963 && $this->JSMinimizerJShrinkAvailable)
4964 {
4965 $Minimizer = (strlen($Code) < 5000)
4966 ? "JShrink" : "JavaScriptPacker";
4967 }
4968 elseif ($this->JSMinimizerJShrinkAvailable)
4969 {
4970 $Minimizer = "JShrink";
4971 }
4972 else
4973 {
4974 $Minimizer = "NONE";
4975 }
4976
4977 # minimize code
4978 switch ($Minimizer)
4979 {
4980 case "JavaScriptMinimizer":
4981 $Packer = new JavaScriptPacker($Code, "Normal");
4982 $MinimizedCode = $Packer->pack();
4983 break;
4984
4985 case "JShrink":
4986 try
4987 {
4988 $MinimizedCode = \JShrink\Minifier::minify($Code);
4989 }
4990 catch (Exception $Exception)
4991 {
4992 unset($MinimizedCode);
4993 $MinimizeError = $Exception->getMessage();
4994 }
4995 break;
4996 }
4997
4998 # if minimization succeeded
4999 if (isset($MinimizedCode))
5000 {
5001 # write out minimized file
5002 file_put_contents($DstFile, $MinimizedCode);
5003 }
5004 else
5005 {
5006 # log error and set destination file path to indicate failure
5007 $ErrMsg = "Unable to minimize JavaScript file ".$SrcFile;
5008 if (isset($MinimizeError))
5009 {
5010 $ErrMsg .= " (".$MinimizeError.")";
5011 }
5012 $this->LogError(self::LOGLVL_ERROR, $ErrMsg);
5013 $DstFile = NULL;
5014 }
5015 }
5016 else
5017 {
5018 # log error and set destination file path to indicate failure
5019 $this->LogError(self::LOGLVL_ERROR,
5020 "Unable to write out minimized JavaScript to file ".$DstFile);
5021 $DstFile = NULL;
5022 }
5023 }
5024
5025 # return CSS file path to caller
5026 return $DstFile;
5027 }
5028
5036 private function CssUrlFingerprintInsertion($Matches)
5037 {
5038 # generate fingerprint string from CSS file modification time
5039 $FileName = realpath($this->CssUrlFingerprintPath."/".
5040 $Matches[2].".".$Matches[3]);
5041 $MTime = filemtime($FileName);
5042 $Fingerprint = sprintf("%06X", ($MTime % 0xFFFFFF));
5043
5044 # build URL string with fingerprint and return it to caller
5045 return "url(".$Matches[1].$Matches[2].".".$Fingerprint
5046 .".".$Matches[3].$Matches[4].")";
5047 }
5048
5056 private function GetRequiredFilesNotYetLoaded($PageContentFile)
5057 {
5058 # start out assuming no files required
5059 $RequiredFiles = array();
5060
5061 # if page content file supplied
5062 if ($PageContentFile)
5063 {
5064 # if file containing list of required files is available
5065 $Path = dirname($PageContentFile);
5066 $RequireListFile = $Path."/REQUIRES";
5067 if (file_exists($RequireListFile))
5068 {
5069 # read in list of required files
5070 $RequestedFiles = file($RequireListFile);
5071
5072 # for each line in required file list
5073 foreach ($RequestedFiles as $Line)
5074 {
5075 # if line is not a comment
5076 $Line = trim($Line);
5077 if (!preg_match("/^#/", $Line))
5078 {
5079 # if file has not already been loaded
5080 if (!in_array($Line, $this->FoundUIFiles))
5081 {
5082 # add to list of required files
5083 $RequiredFiles[$Line] = self::ORDER_MIDDLE;
5084 }
5085 }
5086 }
5087 }
5088 }
5089
5090 # add in additional required files if any
5091 if (count($this->AdditionalRequiredUIFiles))
5092 {
5093 # make sure there are no duplicates
5094 $AdditionalRequiredUIFiles = array_unique(
5095 $this->AdditionalRequiredUIFiles);
5096
5097 $RequiredFiles = array_merge(
5098 $RequiredFiles, $this->AdditionalRequiredUIFiles);
5099 }
5100
5101 # return list of required files to caller
5102 return $RequiredFiles;
5103 }
5104
5113 private function SubBrowserIntoFileNames($FileNames)
5114 {
5115 # if a browser detection function has been made available
5116 $UpdatedFileNames = array();
5117 if (is_callable($this->BrowserDetectFunc))
5118 {
5119 # call function to get browser list
5120 $Browsers = call_user_func($this->BrowserDetectFunc);
5121
5122 # for each required file
5123 foreach ($FileNames as $FileName => $Value)
5124 {
5125 # if file name includes browser keyword
5126 if (preg_match("/%BROWSER%/", $FileName))
5127 {
5128 # for each browser
5129 foreach ($Browsers as $Browser)
5130 {
5131 # substitute in browser name and add to new file list
5132 $NewFileName = preg_replace(
5133 "/%BROWSER%/", $Browser, $FileName);
5134 $UpdatedFileNames[$NewFileName] = $Value;
5135 }
5136 }
5137 else
5138 {
5139 # add to new file list
5140 $UpdatedFileNames[$FileName] = $Value;
5141 }
5142 }
5143 }
5144 else
5145 {
5146 # filter out any files with browser keyword in their name
5147 foreach ($FileNames as $FileName => $Value)
5148 {
5149 if (!preg_match("/%BROWSER%/", $FileName))
5150 {
5151 $UpdatedFileNames[$FileName] = $Value;
5152 }
5153 }
5154 }
5155
5156 return $UpdatedFileNames;
5157 }
5158
5164 private function AddMetaTagsToPageOutput($PageOutput)
5165 {
5166 # start with unconditional (non-unique) tags
5167 $TagsToAdd = $this->MetaTags;
5168
5169 # for each unique tag
5170 foreach ($this->UniqueMetaTags as $UniqueMetaTag)
5171 {
5172 $Attribs = $UniqueMetaTag["Attribs"];
5173 $UniqueAttribs = $UniqueMetaTag["UniqueAttribs"];
5174
5175 # if no unique attributes specified
5176 if ($UniqueAttribs === NULL)
5177 {
5178 # use first attribute as unique attribute
5179 $UniqueAttribs = array_slice($Attribs, 0, 1);
5180 }
5181
5182 # for each already-queued tag
5183 # (look for meta tags that match all attributes in
5184 # the current unique tag)
5185 foreach ($TagsToAdd as $TagAttribs)
5186 {
5187 # for each attribute in current unique tag
5188 # (look for attributes in the current unique tag that do
5189 # not match attributes in the this queued tag)
5190 foreach ($UniqueAttribs as $UniqueName => $UniqueValue)
5191 {
5192 # if unique attribute is not found in queued tag
5193 # or queued tag attribute has a different value
5194 if (!isset($TagAttribs[$UniqueName])
5195 || ($TagAttribs[$UniqueName] != $UniqueValue))
5196 {
5197 # skip to next queued tag
5198 # (some attribute in the current unique tag
5199 # was not found in the queued tag)
5200 continue 2;
5201 }
5202 }
5203
5204 # skip to next unique tag
5205 # (all attributes in the current unique tag were found
5206 # in the queued tag, so do not queue this unique tag)
5207 continue 2;
5208 }
5209
5210 # generate potential combinations of unique attributes
5211 $UniqueAttribNameCombos = StdLib::ArrayPermutations(
5212 array_keys($UniqueAttribs));
5213
5214 # for each combination of unique attributes
5215 foreach ($UniqueAttribNameCombos as $UniqueNameCombo)
5216 {
5217 # for each attribute in combination
5218 $AttribStrings = array();
5219 foreach ($UniqueNameCombo as $UniqueName)
5220 {
5221 # add attrib/value string to list
5222 $AttribStrings[] = $UniqueName."=\""
5223 .htmlspecialchars($UniqueAttribs[$UniqueName])."\"";
5224 }
5225
5226 # build search string from list of attribute pairs
5227 $SearchString = "<meta ".implode(" ", $AttribStrings);
5228
5229 # if search string appears in page output
5230 if (strpos($PageOutput, $SearchString) !== FALSE)
5231 {
5232 # skip to next unique tag
5233 continue 2;
5234 }
5235
5236 # repeat search with single quotes instead of double quotes
5237 $SearchString = strtr($SearchString, '"', "'");
5238 if (strpos($PageOutput, $SearchString) !== FALSE)
5239 {
5240 # skip to next unique tag
5241 continue 2;
5242 }
5243 }
5244
5245 # unique tag was not found in page output, so add it to inserted tags
5246 $TagsToAdd[] = $Attribs;
5247 }
5248
5249 # if there are meta tags to be added
5250 if (count($TagsToAdd))
5251 {
5252 # start with an empty segment
5253 $Section = "";
5254
5255 # for each meta tag
5256 foreach ($TagsToAdd as $Attribs)
5257 {
5258 # assemble tag and add it to the segment
5259 $Section .= "<meta";
5260 foreach ($Attribs as $AttribName => $AttribValue)
5261 {
5262 $Section .= " ".$AttribName."=\""
5263 .htmlspecialchars(trim($AttribValue))."\"";
5264 }
5265 $Section .= " />\n";
5266 }
5267
5268 # if standard page start and end have been disabled
5269 if ($this->SuppressStdPageStartAndEnd)
5270 {
5271 # add segment to beginning of page output
5272 $PageOutput = $Section.$PageOutput;
5273 }
5274 else
5275 {
5276 # insert segment at beginning of HTML head section in page output
5277 $PageOutput = preg_replace("#<head>#i",
5278 "<head>\n".$Section, $PageOutput, 1);
5279 }
5280 }
5281
5282 # return (potentially modified) page output to caller
5283 return $PageOutput;
5284 }
5285
5293 private function AddFileTagsToPageOutput($PageOutput, $Files)
5294 {
5295 # substitute browser name into names of required files as appropriate
5296 $Files = $this->SubBrowserIntoFileNames($Files);
5297
5298 # initialize content sections
5299 $HeadContent = [
5300 self::ORDER_FIRST => "",
5301 self::ORDER_MIDDLE => "",
5302 self::ORDER_LAST => "",
5303 ];
5304 $BodyContent = [
5305 self::ORDER_FIRST => "",
5306 self::ORDER_MIDDLE => "",
5307 self::ORDER_LAST => "",
5308 ];
5309
5310 # for each required file
5311 foreach ($Files as $File => $Order)
5312 {
5313 # locate specific file to use
5314 $FilePath = $this->GUIFile($File);
5315
5316 # if file was found
5317 if ($FilePath)
5318 {
5319 # generate tag for file
5320 $Tag = $this->GetUIFileLoadingTag($FilePath);
5321
5322 # add file to HTML output based on file type
5323 $FileType = $this->GetFileType($FilePath);
5324 switch ($FileType)
5325 {
5326 case self::FT_CSS:
5327 $HeadContent[$Order] .= $Tag."\n";
5328 break;
5329
5330 case self::FT_JAVASCRIPT:
5331 $BodyContent[$Order] .= $Tag."\n";
5332 break;
5333 }
5334 }
5335 }
5336
5337 # add content to head
5338 $Replacement = $HeadContent[self::ORDER_MIDDLE]
5339 .$HeadContent[self::ORDER_LAST];
5340 $UpdatedPageOutput = str_ireplace("</head>",
5341 $Replacement."</head>",
5342 $PageOutput, $ReplacementCount);
5343 # (if no </head> tag was found, just prepend tags to page content)
5344 if ($ReplacementCount == 0)
5345 {
5346 $PageOutput = $Replacement.$PageOutput;
5347 }
5348 # (else if multiple </head> tags found, only prepend tags to the first)
5349 elseif ($ReplacementCount > 1)
5350 {
5351 $PageOutput = preg_replace("#</head>#i",
5352 $Replacement."</head>",
5353 $PageOutput, 1);
5354 }
5355 else
5356 {
5357 $PageOutput = $UpdatedPageOutput;
5358 }
5359 $Replacement = $HeadContent[self::ORDER_FIRST];
5360 $UpdatedPageOutput = str_ireplace("<head>",
5361 "<head>\n".$Replacement,
5362 $PageOutput, $ReplacementCount);
5363 # (if no <head> tag was found, just prepend tags to page content)
5364 if ($ReplacementCount == 0)
5365 {
5366 $PageOutput = $Replacement.$PageOutput;
5367 }
5368 # (else if multiple <head> tags found, only append tags to the first)
5369 elseif ($ReplacementCount > 1)
5370 {
5371 $PageOutput = preg_replace("#<head>#i",
5372 "<head>\n".$Replacement,
5373 $PageOutput, 1);
5374 }
5375 else
5376 {
5377 $PageOutput = $UpdatedPageOutput;
5378 }
5379
5380 # add content to body
5381 $Replacement = $BodyContent[self::ORDER_FIRST];
5382 $PageOutput = preg_replace("#<body([^>]*)>#i",
5383 "<body\\1>\n".$Replacement,
5384 $PageOutput, 1, $ReplacementCount);
5385 # (if no <body> tag was found, just append tags to page content)
5386 if ($ReplacementCount == 0)
5387 {
5388 $PageOutput = $PageOutput.$Replacement;
5389 }
5390 $Replacement = $BodyContent[self::ORDER_MIDDLE]
5391 .$BodyContent[self::ORDER_LAST];
5392 $UpdatedPageOutput = str_ireplace("</body>",
5393 $Replacement."\n</body>",
5394 $PageOutput, $ReplacementCount);
5395 # (if no </body> tag was found, just append tags to page content)
5396 if ($ReplacementCount == 0)
5397 {
5398 $PageOutput = $PageOutput.$Replacement;
5399 }
5400 # (else if multiple </body> tags found, only prepend tag to the first)
5401 elseif ($ReplacementCount > 1)
5402 {
5403 $PageOutput = preg_replace("#</body>#i",
5404 $Replacement."\n</body>",
5405 $PageOutput, 1);
5406 }
5407 else
5408 {
5409 $PageOutput = $UpdatedPageOutput;
5410 }
5411
5412 return $PageOutput;
5413 }
5414
5425 private function GetUIFileLoadingTag(
5426 $FileName, $AdditionalAttributes = NULL)
5427 {
5428 # pad additional attributes if supplied
5429 $AddAttribs = $AdditionalAttributes ? " ".$AdditionalAttributes : "";
5430
5431 # retrieve type of UI file
5432 $FileType = $this->GetFileType($FileName);
5433
5434 # construct tag based on file type
5435 switch ($FileType)
5436 {
5437 case self::FT_CSS:
5438 $Tag = " <link rel=\"stylesheet\" type=\"text/css\""
5439 ." media=\"all\" href=\"".$FileName."\""
5440 .$AddAttribs." />\n";
5441 break;
5442
5443 case self::FT_JAVASCRIPT:
5444 $Tag = " <script type=\"text/javascript\""
5445 ." src=\"".$FileName."\""
5446 .$AddAttribs."></script>\n";
5447 break;
5448
5449 case self::FT_IMAGE:
5450 $Tag = "<img src=\"".$FileName."\"".$AddAttribs.">";
5451 break;
5452
5453 default:
5454 $Tag = "";
5455 break;
5456 }
5457
5458 # return constructed tag to caller
5459 return $Tag;
5460 }
5461
5466 private function AutoloadObjects($ClassName)
5467 {
5468 # if caching is not turned off
5469 # and we have a cached location for class
5470 # and file at cached location is readable
5471 if ((self::$ObjectLocationCacheInterval > 0)
5472 && array_key_exists($ClassName,
5473 self::$ObjectLocationCache)
5474 && is_readable(self::$ObjectLocationCache[$ClassName]))
5475 {
5476 # use object location from cache
5477 require_once(self::$ObjectLocationCache[$ClassName]);
5478 }
5479 else
5480 {
5481 # for each possible object file directory
5482 static $FileLists;
5483 foreach (self::$ObjectDirectories as $Location => $Info)
5484 {
5485 # make any needed replacements in directory path
5486 $Location = str_replace(array("%ACTIVEUI%", "%DEFAULTUI%"),
5487 array(self::$ActiveUI, self::$DefaultUI), $Location);
5488
5489 # if directory looks valid
5490 if (is_dir($Location))
5491 {
5492 # pass class name through callback (if supplied)
5493 $ClassFileName = $ClassName;
5494 if (is_callable($Info["Callback"]))
5495 {
5496 $ClassFileName = $Info["Callback"]($ClassFileName);
5497 }
5498
5499 # strip off any namespace prefix
5500 foreach ($Info["NamespacePrefixes"] as $Prefix)
5501 {
5502 if (strpos($ClassFileName, $Prefix) === 0)
5503 {
5504 $ClassFileName = substr($ClassFileName, strlen($Prefix));
5505 break;
5506 }
5507 }
5508
5509 # strip off any leading namespace separator
5510 if (strpos($ClassFileName, "\\") === 0)
5511 {
5512 $ClassFileName = substr($ClassFileName, 1);
5513 }
5514
5515 # convert any namespace separators to directory separators
5516 $ClassFileName = str_replace("\\", "/", $ClassFileName);
5517
5518 # finish building class file name
5519 $ClassFileName = $ClassFileName.".php";
5520
5521 # read in directory contents if not already retrieved
5522 if (!isset($FileLists[$Location]))
5523 {
5524 $FileLists[$Location] = self::ReadDirectoryTree(
5525 $Location, '/^.+\.php$/i');
5526 }
5527
5528 # for each file in target directory
5529 foreach ($FileLists[$Location] as $FileName)
5530 {
5531 # if file matches our target object file name
5532 if ($FileName == $ClassFileName)
5533 {
5534 # include object file
5535 require_once($Location.$FileName);
5536
5537 # save location to cache
5538 self::$ObjectLocationCache[$ClassName]
5539 = $Location.$FileName;
5540
5541 # set flag indicating that cache should be saved
5542 self::$SaveObjectLocationCache = TRUE;
5543
5544 # stop looking
5545 break 2;
5546 }
5547 }
5548 }
5549 }
5550 }
5551 }
5552
5560 private static function ReadDirectoryTree($Directory, $Pattern)
5561 {
5562 $CurrentDir = getcwd();
5563 chdir($Directory);
5564 $DirIter = new RecursiveDirectoryIterator(".");
5565 $IterIter = new RecursiveIteratorIterator($DirIter);
5566 $RegexResults = new RegexIterator($IterIter, $Pattern,
5567 RecursiveRegexIterator::GET_MATCH);
5568 $FileList = array();
5569 foreach ($RegexResults as $Result)
5570 {
5571 $FileList[] = substr($Result[0], 2);
5572 }
5573 chdir($CurrentDir);
5574 return $FileList;
5575 }
5576
5581 private function LoadUIFunctions()
5582 {
5583 $Dirs = array(
5584 "local/interface/%ACTIVEUI%/include",
5585 "interface/%ACTIVEUI%/include",
5586 "local/interface/%DEFAULTUI%/include",
5587 "interface/%DEFAULTUI%/include",
5588 );
5589 foreach ($Dirs as $Dir)
5590 {
5591 $Dir = str_replace(array("%ACTIVEUI%", "%DEFAULTUI%"),
5592 array(self::$ActiveUI, self::$DefaultUI), $Dir);
5593 if (is_dir($Dir))
5594 {
5595 $FileNames = scandir($Dir);
5596 foreach ($FileNames as $FileName)
5597 {
5598 if (preg_match("/^F-([A-Za-z0-9_]+)\.php/",
5599 $FileName, $Matches)
5600 || preg_match("/^F-([A-Za-z0-9_]+)\.html/",
5601 $FileName, $Matches))
5602 {
5603 if (!function_exists($Matches[1]))
5604 {
5605 include_once($Dir."/".$FileName);
5606 }
5607 }
5608 }
5609 }
5610 }
5611 }
5612
5618 private function ProcessPeriodicEvent($EventName, $Callback)
5619 {
5620 # retrieve last execution time for event if available
5621 $Signature = self::GetCallbackSignature($Callback);
5622 $LastRun = $this->DB->Query("SELECT LastRunAt FROM PeriodicEvents"
5623 ." WHERE Signature = '".addslashes($Signature)."'", "LastRunAt");
5624
5625 # determine whether enough time has passed for event to execute
5626 $ShouldExecute = (($LastRun === NULL)
5627 || (time() > (strtotime($LastRun) + $this->EventPeriods[$EventName])))
5628 ? TRUE : FALSE;
5629
5630 # if event should run
5631 if ($ShouldExecute)
5632 {
5633 # add event to task queue
5634 $WrapperCallback = array("ApplicationFramework", "RunPeriodicEvent");
5635 $WrapperParameters = array(
5636 $EventName, $Callback, array("LastRunAt" => $LastRun));
5637 $this->QueueUniqueTask($WrapperCallback, $WrapperParameters);
5638 }
5639
5640 # add event to list of periodic events
5641 $this->KnownPeriodicEvents[$Signature] = array(
5642 "Period" => $EventName,
5643 "Callback" => $Callback,
5644 "Queued" => $ShouldExecute);
5645 }
5646
5652 private static function GetCallbackSignature($Callback)
5653 {
5654 return !is_array($Callback) ? $Callback
5655 : (is_object($Callback[0]) ? md5(serialize($Callback[0])) : $Callback[0])
5656 ."::".$Callback[1];
5657 }
5658
5663 private function PrepForTSR()
5664 {
5665 # if HTML has been output and it's time to launch another task
5666 # (only TSR if HTML has been output because otherwise browsers
5667 # may misbehave after connection is closed)
5668 if ((PHP_SAPI != "cli")
5669 && ($this->JumpToPage || !$this->SuppressHTML)
5670 && !$this->LoadedViaAlternateDomain()
5671 && $this->TaskMgr->TaskExecutionEnabled()
5672 && $this->TaskMgr->GetTaskQueueSize())
5673 {
5674 # begin buffering output for TSR
5675 ob_start();
5676
5677 # let caller know it is time to launch another task
5678 return TRUE;
5679 }
5680 else
5681 {
5682 # let caller know it is not time to launch another task
5683 return FALSE;
5684 }
5685 }
5686
5691 private function LaunchTSR()
5692 {
5693 # set headers to close out connection to browser
5694 if (!$this->NoTSR)
5695 {
5696 ignore_user_abort(TRUE);
5697 header("Connection: close");
5698 header("Content-Length: ".ob_get_length());
5699 }
5700
5701 # output buffered content
5702 while (ob_get_level()) { ob_end_flush(); }
5703 flush();
5704
5705 # write out any outstanding data and end HTTP session
5706 session_write_close();
5707
5708 # set flag indicating that we are now running in background
5709 $this->RunningInBackground = TRUE;
5710
5711 # set database slow query threshold for background execution
5712 Database::SlowQueryThreshold(max(
5713 self::MIN_DB_SLOW_QUERY_THRESHOLD,
5714 self::DatabaseSlowQueryThresholdForBackground()));
5715
5716 # handle garbage collection for session data
5717 if (isset($this->SessionStorage) &&
5718 (rand()/getrandmax()) <= $this->SessionGcProbability)
5719 {
5720 # determine when sessions will expire
5721 $ExpiredTime = strtotime("-". self::$SessionLifetime." seconds");
5722
5723 # iterate over files in the session directory with a DirectoryIterator
5724 # NB: we cannot use scandir() here because it reads the
5725 # entire list of files into memory and may exceed the memory
5726 # limit for directories with very many files
5727 $DI = new DirectoryIterator($this->SessionStorage);
5728 while ($DI->valid())
5729 {
5730 if ((strpos($DI->getFilename(), "sess_") === 0) &&
5731 $DI->isFile() &&
5732 $DI->getCTime() < $ExpiredTime)
5733 {
5734 unlink($DI->getPathname());
5735 }
5736 $DI->next();
5737 }
5738 unset($DI);
5739 }
5740
5741 # run qny queued tasks
5742 $this->TaskMgr->RunQueuedTasks();
5743 }
5744
5750 public function OnCrash()
5751 {
5752 # attempt to remove any memory limits
5753 $FreeMemory = $this->GetFreeMemory();
5754 ini_set("memory_limit", -1);
5755
5756 # if there is a background task currently running
5757 if (isset($this->RunningTask))
5758 {
5759 # add info about current page load
5760 $CrashInfo["ElapsedTime"] = $this->GetElapsedExecutionTime();
5761 $CrashInfo["FreeMemory"] = $FreeMemory;
5762 $CrashInfo["REMOTE_ADDR"] = $_SERVER["REMOTE_ADDR"];
5763 $CrashInfo["REQUEST_URI"] = $_SERVER["REQUEST_URI"];
5764 if (isset($_SERVER["REQUEST_TIME"]))
5765 {
5766 $CrashInfo["REQUEST_TIME"] = $_SERVER["REQUEST_TIME"];
5767 }
5768 if (isset($_SERVER["REMOTE_HOST"]))
5769 {
5770 $CrashInfo["REMOTE_HOST"] = $_SERVER["REMOTE_HOST"];
5771 }
5772
5773 # add info about error that caused crash (if available)
5774 if (function_exists("error_get_last"))
5775 {
5776 $CrashInfo["LastError"] = error_get_last();
5777 }
5778
5779 # add info about current output buffer contents (if available)
5780 if (ob_get_length() !== FALSE)
5781 {
5782 $CrashInfo["OutputBuffer"] = ob_get_contents();
5783 }
5784
5785 # if backtrace info is available for the crash
5786 $Backtrace = debug_backtrace();
5787 if (count($Backtrace) > 1)
5788 {
5789 # discard the current context from the backtrace
5790 array_shift($Backtrace);
5791
5792 # add the backtrace to the crash info
5793 $CrashInfo["Backtrace"] = $Backtrace;
5794 }
5795 # else if saved backtrace info is available
5796 elseif (isset($this->SavedContext))
5797 {
5798 # add the saved backtrace to the crash info
5799 $CrashInfo["Backtrace"] = $this->SavedContext;
5800 }
5801
5802 # save crash info for currently running task
5803 $DB = new Database();
5804 $DB->Query("UPDATE RunningTasks SET CrashInfo = '"
5805 .addslashes(serialize($CrashInfo))
5806 ."' WHERE TaskId = ".intval($this->RunningTask["TaskId"]));
5807 }
5808
5809 print("\n");
5810 return;
5811 }
5812
5830 private function AddToDirList(
5831 $DirList, $Dir, $SearchLast, $SkipSlashCheck)
5832 {
5833 # convert incoming directory to array of directories (if needed)
5834 $Dirs = is_array($Dir) ? $Dir : array($Dir);
5835
5836 # reverse array so directories are searched in specified order
5837 $Dirs = array_reverse($Dirs);
5838
5839 # for each directory
5840 foreach ($Dirs as $Location)
5841 {
5842 # make sure directory includes trailing slash
5843 if (!$SkipSlashCheck)
5844 {
5845 $Location = $Location
5846 .((substr($Location, -1) != "/") ? "/" : "");
5847 }
5848
5849 # remove directory from list if already present
5850 if (in_array($Location, $DirList))
5851 {
5852 $DirList = array_diff(
5853 $DirList, array($Location));
5854 }
5855
5856 # add directory to list of directories
5857 if ($SearchLast)
5858 {
5859 array_push($DirList, $Location);
5860 }
5861 else
5862 {
5863 array_unshift($DirList, $Location);
5864 }
5865 }
5866
5867 # return updated directory list to caller
5868 return $DirList;
5869 }
5870
5877 private function OutputModificationCallbackShell($Matches)
5878 {
5879 # call previously-stored external function
5880 return call_user_func($this->OutputModificationCallbackInfo["Callback"],
5881 $Matches,
5882 $this->OutputModificationCallbackInfo["Pattern"],
5883 $this->OutputModificationCallbackInfo["Page"],
5884 $this->OutputModificationCallbackInfo["SearchPattern"]);
5885 }
5886
5895 private function CheckOutputModification(
5896 $Original, $Modified, $ErrorInfo)
5897 {
5898 # if error was reported by regex engine
5899 if (preg_last_error() !== PREG_NO_ERROR)
5900 {
5901 # log error
5902 $this->LogError(self::LOGLVL_ERROR,
5903 "Error reported by regex engine when modifying output."
5904 ." (".$ErrorInfo.")");
5905
5906 # use unmodified version of output
5907 $OutputToUse = $Original;
5908 }
5909 # else if modification reduced output by more than threshold
5910 elseif ((strlen(trim($Modified)) / strlen(trim($Original)))
5911 < self::OUTPUT_MODIFICATION_THRESHOLD)
5912 {
5913 # log error
5914 $this->LogError(self::LOGLVL_WARNING,
5915 "Content reduced below acceptable threshold while modifying output."
5916 ." (".$ErrorInfo.")");
5917
5918 # use unmodified version of output
5919 $OutputToUse = $Original;
5920 }
5921 else
5922 {
5923 # use modified version of output
5924 $OutputToUse = $Modified;
5925 }
5926
5927 # return output to use to caller
5928 return $OutputToUse;
5929 }
5930
5932 const OUTPUT_MODIFICATION_THRESHOLD = 0.10;
5933
5943 private function UpdateSetting(
5944 $FieldName, $NewValue = DB_NOVALUE, $Persistent = TRUE)
5945 {
5946 static $LocalSettings;
5947 if ($NewValue !== DB_NOVALUE)
5948 {
5949 if ($Persistent)
5950 {
5951 $LocalSettings[$FieldName] = $this->DB->UpdateValue(
5952 "ApplicationFrameworkSettings",
5953 $FieldName, $NewValue, NULL, $this->Settings);
5954 }
5955 else
5956 {
5957 $LocalSettings[$FieldName] = $NewValue;
5958 }
5959 }
5960 elseif (!isset($LocalSettings[$FieldName]))
5961 {
5962 $LocalSettings[$FieldName] = $this->DB->UpdateValue(
5963 "ApplicationFrameworkSettings",
5964 $FieldName, $NewValue, NULL, $this->Settings);
5965 }
5966 return $LocalSettings[$FieldName];
5967 }
5968
5978 private static function IncludeFile(
5979 $_AF_File, $_AF_ContextVars = array())
5980 {
5981 # set up context
5982 foreach ($_AF_ContextVars as $_AF_VarName => $_AF_VarValue)
5983 {
5984 $$_AF_VarName = $_AF_VarValue;
5985 }
5986 unset($_AF_VarName);
5987 unset($_AF_VarValue);
5988 unset($_AF_ContextVars);
5989
5990 # add variables to context that we assume are always available
5991 $AF = $GLOBALS["AF"];
5992
5993 # load file
5994 include($_AF_File);
5995
5996 # return updated context
5997 $ContextVars = get_defined_vars();
5998 unset($ContextVars["_AF_File"]);
5999 return $ContextVars;
6000 }
6001
6008 private function FilterContext($Context, $ContextVars)
6009 {
6010 # clear all variables if no setting for context is available
6011 # or setting is FALSE
6012 if (!isset($this->ContextFilters[$Context])
6013 || ($this->ContextFilters[$Context] == FALSE))
6014 {
6015 return array();
6016 }
6017 # keep all variables if setting for context is TRUE
6018 elseif ($this->ContextFilters[$Context] == TRUE)
6019 {
6020 return $ContextVars;
6021 }
6022 else
6023 {
6024 $Prefixes = $this->ContextFilters[$Context];
6025 $FilterFunc = function($VarName) use ($Prefixes) {
6026 foreach ($Prefixes as $Prefix)
6027 {
6028 if (substr($VarName, $Prefix) === 0)
6029 {
6030 return TRUE;
6031 }
6032 }
6033 return FALSE;
6034 };
6035 return array_filter(
6036 $ContextVars, $FilterFunc, ARRAY_FILTER_USE_KEY);
6037 }
6038 }
6039
6041 private $InterfaceDirList = array(
6042 "interface/%ACTIVEUI%/",
6043 "interface/%DEFAULTUI%/",
6044 );
6049 private $IncludeDirList = array(
6050 "interface/%ACTIVEUI%/include/",
6051 "interface/%ACTIVEUI%/objects/",
6052 "interface/%DEFAULTUI%/include/",
6053 "interface/%DEFAULTUI%/objects/",
6054 );
6056 private $ImageDirList = array(
6057 "interface/%ACTIVEUI%/images/",
6058 "interface/%DEFAULTUI%/images/",
6059 );
6061 private $FunctionDirList = array(
6062 "interface/%ACTIVEUI%/include/",
6063 "interface/%DEFAULTUI%/include/",
6064 "include/",
6065 );
6066
6067 private $ExpandedDirectoryLists = array();
6068
6069 const NOVALUE = ".-+-.NO VALUE PASSED IN FOR ARGUMENT.-+-.";
6070
6071
6072 # ---- Page Caching (Internal Methods) -----------------------------------
6073
6079 private function CheckForCachedPage($PageName)
6080 {
6081 # assume no cached page will be found
6082 $CachedPage = NULL;
6083
6084 # if returning a cached page is allowed
6085 if ($this->CacheCurrentPage)
6086 {
6087 # get fingerprint for requested page
6088 $PageFingerprint = $this->GetPageFingerprint($PageName);
6089
6090 # look for matching page in cache in database
6091 $this->DB->Query("SELECT * FROM AF_CachedPages"
6092 ." WHERE Fingerprint = '".addslashes($PageFingerprint)."'");
6093
6094 # if matching page found
6095 if ($this->DB->NumRowsSelected())
6096 {
6097 # if cached page has expired
6098 $Row = $this->DB->FetchRow();
6099 $ExpirationTime = strtotime(
6100 "-".$this->PageCacheExpirationPeriod()." seconds");
6101 if (strtotime($Row["CachedAt"]) < $ExpirationTime)
6102 {
6103 # clear expired pages from cache
6104 $ExpirationTimestamp = date("Y-m-d H:i:s", $ExpirationTime);
6105 $this->DB->Query("DELETE CP, CPTI FROM AF_CachedPages CP,"
6106 ." AF_CachedPageTagInts CPTI"
6107 ." WHERE CP.CachedAt < '".$ExpirationTimestamp."'"
6108 ." AND CPTI.CacheId = CP.CacheId");
6109 $this->DB->Query("DELETE FROM AF_CachedPages "
6110 ." WHERE CachedAt < '".$ExpirationTimestamp."'");
6111 }
6112 else
6113 {
6114 # display cached page and exit
6115 $CachedPage = $Row["PageContent"];
6116 }
6117 }
6118 }
6119
6120 # return any cached page found to caller
6121 return $CachedPage;
6122 }
6123
6129 private function UpdatePageCache($PageName, $PageContent)
6130 {
6131 # if page caching is enabled and current page should be cached
6132 if ($this->PageCacheEnabled()
6133 && $this->CacheCurrentPage)
6134 {
6135 # if page content looks invalid
6136 if (strlen(trim(strip_tags($PageContent))) == 0)
6137 {
6138 # log error
6139 $LogMsg = "Page not cached because content was empty."
6140 ." (PAGE: ".$PageName.", URL: ".$this->FullUrl().")";
6141 $this->LogError(self::LOGLVL_ERROR, $LogMsg);
6142 }
6143 else
6144 {
6145 # save page to cache
6146 $PageFingerprint = $this->GetPageFingerprint($PageName);
6147 $this->DB->Query("INSERT INTO AF_CachedPages"
6148 ." (Fingerprint, PageContent) VALUES"
6149 ." ('".$this->DB->EscapeString($PageFingerprint)."', '"
6150 .$this->DB->EscapeString($PageContent)."')");
6151 $CacheId = $this->DB->LastInsertId();
6152
6153 # for each page cache tag that was added
6154 foreach ($this->PageCacheTags as $Tag => $Pages)
6155 {
6156 # if current page is in list for tag
6157 if (in_array("CURRENT", $Pages) || in_array($PageName, $Pages))
6158 {
6159 # look up tag ID
6160 $TagId = $this->GetPageCacheTagId($Tag);
6161
6162 # mark current page as associated with tag
6163 $this->DB->Query("INSERT INTO AF_CachedPageTagInts"
6164 ." (TagId, CacheId) VALUES "
6165 ." (".intval($TagId).", ".intval($CacheId).")");
6166 }
6167 }
6168 }
6169 }
6170 }
6171
6177 private function GetPageCacheTagId($Tag)
6178 {
6179 # if tag is a non-negative integer
6180 if (is_numeric($Tag) && ($Tag > 0) && (intval($Tag) == $Tag))
6181 {
6182 # generate ID
6183 $Id = self::PAGECACHETAGIDOFFSET + $Tag;
6184 }
6185 else
6186 {
6187 # look up ID in database
6188 $Id = $this->DB->Query("SELECT TagId FROM AF_CachedPageTags"
6189 ." WHERE Tag = '".addslashes($Tag)."'", "TagId");
6190
6191 # if ID was not found
6192 if ($Id === NULL)
6193 {
6194 # add tag to database
6195 $this->DB->Query("INSERT INTO AF_CachedPageTags"
6196 ." SET Tag = '".addslashes($Tag)."'");
6197 $Id = $this->DB->LastInsertId();
6198 }
6199 }
6200
6201 # return tag ID to caller
6202 return $Id;
6203 }
6204
6210 private function GetPageFingerprint($PageName)
6211 {
6212 # only get the environmental fingerprint once so that it is consistent
6213 # between page construction start and end
6214 static $EnvFingerprint;
6215 if (!isset($EnvFingerprint))
6216 {
6217 $GetVars = $_GET;
6218 ksort($GetVars);
6219 $PostVars = $_POST;
6220 ksort($PostVars);
6221 $EnvData = json_encode($GetVars)
6222 .json_encode($PostVars)
6223 .$_SERVER["SERVER_NAME"];
6224 $EnvFingerprint = md5($EnvData);
6225 }
6226
6227 # build page fingerprint and return it to caller
6228 return $PageName."-".$EnvFingerprint;
6229 }
6230
6237 private function UpdateLastUsedTimeForActiveSessions()
6238 {
6239 if ($this->SessionInUse)
6240 {
6241 $_SESSION["AF_SessionLastUsed"] = date("Y-m-d H:i:s");
6242 }
6243 elseif (isset($_SESSION["AF_SessionLastUsed"]))
6244 {
6245 unset($_SESSION["AF_SessionLastUsed"]);
6246 }
6247 }
6248
6256 private function AdjustEnvironmentForCgi()
6257 {
6258 # if it appears we are running via a CGI interpreter
6259 if (isset($_SERVER["ORIG_SCRIPT_NAME"]))
6260 {
6261 # for each server environment variable
6262 foreach ($_SERVER as $Key => $Value)
6263 {
6264 # if variable appears the result of using CGI
6265 if (strpos($Key, "REDIRECT_") === 0)
6266 {
6267 # if unmodified version of variable is not set
6268 $KeyWithoutPrefix = substr($Key, 9);
6269 if (!isset($_SERVER[$KeyWithoutPrefix]))
6270 {
6271 # set unmodified version of variable
6272 $_SERVER[$KeyWithoutPrefix] = $_SERVER[$Key];
6273 }
6274 }
6275 }
6276 }
6277 }
6278 }
ApplicationFramework\LOGLVL_ERROR
const LOGLVL_ERROR
ERROR error logging level.
Definition: ApplicationFramework.php:2459
ApplicationFramework\PageCacheEnabled
PageCacheEnabled($NewValue=DB_NOVALUE, $Persistent=FALSE)
Enable/disable page caching.
Definition: ApplicationFramework.php:1990
ApplicationFramework\UnhookEvent
UnhookEvent($EventsOrEventName, $Callback=NULL, $Order=self::ORDER_MIDDLE)
Unhook one or more functions that were previously hooked to be called when the specified event is sig...
Definition: ApplicationFramework.php:2641
ApplicationFramework\GetOrphanedTaskList
GetOrphanedTaskList($Count=100, $Offset=0)
Retrieve list of tasks currently orphaned.
Definition: ApplicationFramework.php:3033
ApplicationFramework\SuppressHTMLOutput
SuppressHTMLOutput($NewSetting=TRUE)
Suppress loading of HTML files.
Definition: ApplicationFramework.php:1273
ApplicationFramework\AddInterfaceDirectories
AddInterfaceDirectories($Dir, $SearchLast=FALSE, $SkipSlashCheck=FALSE)
Add additional directory(s) to be searched for user interface (HTML/TPL) files.
Definition: ApplicationFramework.php:413
ApplicationFramework\AddIncludeDirectories
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.
Definition: ApplicationFramework.php:385
ApplicationFramework\GetScriptUrl
static GetScriptUrl()
Retrieve SCRIPT_URL server value, pulling it from elsewhere if that variable isn&#39;t set...
Definition: ApplicationFramework.php:3678
ApplicationFramework\LOGLVL_INFO
const LOGLVL_INFO
INFO error logging level.
Definition: ApplicationFramework.php:2448
ApplicationFramework\AddUnbufferedCallback
AddUnbufferedCallback($Callback, $Parameters=array())
Add a callback that will be executed after buffered content has been output and that won&#39;t have its o...
Definition: ApplicationFramework.php:469
ApplicationFramework\DoNotUrlFingerprint
DoNotUrlFingerprint($Pattern)
Specify file or file name pattern to exclude from URL fingerprinting.
Definition: ApplicationFramework.php:1801
ApplicationFramework\QueueUniqueTask
QueueUniqueTask($Callback, $Parameters=NULL, $Priority=self::PRIORITY_LOW, $Description="")
Add task to queue if not already in queue or currently running.
Definition: ApplicationFramework.php:2953
ApplicationFramework\AddPrefixForAlternateDomain
AddPrefixForAlternateDomain($Domain, $Prefix)
Add an alternate domain for the site which should map to a path tree under the main site URL...
Definition: ApplicationFramework.php:3457
ApplicationFramework\DatabaseSlowQueryThresholdForBackground
DatabaseSlowQueryThresholdForBackground($NewValue=DB_NOVALUE, $Persistent=FALSE)
Get/set threshold for when database queries are considered "slow" when running in the background...
Definition: ApplicationFramework.php:3861
ApplicationFramework\ConvertPhpIniSizeToBytes
static ConvertPhpIniSizeToBytes($Size)
Convert an abbreviated size from php.ini (e.g., 2g) to a number of bytes.
Definition: ApplicationFramework.php:4153
ApplicationFramework\RequeueCurrentTask
RequeueCurrentTask($NewValue=TRUE)
Set whether to requeue the currently-running background task when it completes.
Definition: ApplicationFramework.php:3063
ApplicationFramework\LOGLVL_FATAL
const LOGLVL_FATAL
FATAL error logging level.
Definition: ApplicationFramework.php:2464
ApplicationFramework\AddMetaTag
AddMetaTag($Attribs)
Add meta tag to page output.
Definition: ApplicationFramework.php:1951
ApplicationFramework\MaxExecutionTime
MaxExecutionTime($NewValue=NULL, $Persistent=FALSE)
Get/set maximum PHP execution time.
Definition: ApplicationFramework.php:3821
ApplicationFramework\AddPostProcessingCall
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.
Definition: ApplicationFramework.php:1489
ApplicationFramework\GetCleanUrlForPath
GetCleanUrlForPath($Path)
Get the clean URL mapped for a path.
Definition: ApplicationFramework.php:3323
ApplicationFramework\PRIORITY_LOW
const PRIORITY_LOW
Lower priority.
Definition: ApplicationFramework.php:2903
ApplicationFramework\IsRunningInBackground
IsRunningInBackground()
Determine whether currently running inside a background task.
Definition: ApplicationFramework.php:1120
Message
Abstraction for forum messages and resource comments.
Definition: Message.php:14
ApplicationFramework\GetQueuedTaskList
GetQueuedTaskList($Count=100, $Offset=0)
Retrieve list of tasks currently in queue.
Definition: ApplicationFramework.php:2990
ApplicationFramework\LogFile
LogFile($NewValue=NULL)
Get/set log file name.
Definition: ApplicationFramework.php:2336
ApplicationFramework\GetCleanUrl
GetCleanUrl()
Get the clean URL for the current page if one is available.
Definition: ApplicationFramework.php:3418
ApplicationFramework\GetPageCachePageList
GetPageCachePageList()
Get list of cached pages.
Definition: ApplicationFramework.php:2104
ApplicationFramework\CONTEXT_END
const CONTEXT_END
File loading context: page end file.
Definition: ApplicationFramework.php:1561
ApplicationFramework\PAGECACHETAGIDOFFSET
const PAGECACHETAGIDOFFSET
Definition: ApplicationFramework.php:4301
ApplicationFramework\PreferHttpHost
static PreferHttpHost($NewValue=NULL)
Get/set whether to prefer $_SERVER["HTTP_HOST"] (if available) over $_SERVER["SERVER_NAME"] when dete...
Definition: ApplicationFramework.php:3648
ApplicationFramework\ClearObjectLocationCache
ClearObjectLocationCache()
Clear object (class) file location cache.
Definition: ApplicationFramework.php:521
ApplicationFramework\GetLock
GetLock($LockName, $Wait=TRUE)
Get an exclusive ("write") lock on the specified name.
Definition: ApplicationFramework.php:4000
ApplicationFramework\FullUrl
static FullUrl()
Get current full URL, before any clean URL remapping and with any query string (e.g.
Definition: ApplicationFramework.php:3633
ApplicationFramework
Top-level framework for web applications.
Definition: ApplicationFramework.php:14
ApplicationFramework\BaseUrl
static BaseUrl()
Get current base URL (the part before index.php) (e.g.
Definition: ApplicationFramework.php:3619
ApplicationFramework\GetOrphanedTaskCount
GetOrphanedTaskCount()
Retrieve current number of orphaned tasks.
Definition: ApplicationFramework.php:3042
Database
SQL database abstraction object with smart query caching.
Definition: Database.php:22
ApplicationFramework\ActiveUserInterface
static ActiveUserInterface($UIName=NULL)
Get/set name of current active user interface.
Definition: ApplicationFramework.php:1324
ApplicationFramework\SessionInUse
SessionInUse($InUse=NULL)
Get/Set value of SessionInUse, which indicates if the current session is currently in use...
Definition: ApplicationFramework.php:4194
ApplicationFramework\GetTaskQueueSize
GetTaskQueueSize($Priority=NULL)
Retrieve current number of tasks in queue.
Definition: ApplicationFramework.php:2978
StdLib\SortCompare
static SortCompare($A, $B)
Perform compare and return value appropriate for sort function callbacks.
Definition: StdLib.php:575
ApplicationFramework\RootUrlOverride
static RootUrlOverride($NewValue=self::NOVALUE)
Get/set root URL override.
Definition: ApplicationFramework.php:3601
ApplicationFramework\GetNextLowerBackgroundPriority
GetNextLowerBackgroundPriority($Priority=NULL)
Get next lower possible background task priority.
Definition: ApplicationFramework.php:3164
ApplicationFramework\UrlFingerprintingRewriteSupport
static UrlFingerprintingRewriteSupport()
Determine if rewrite support for URL fingerprinting is available.
Definition: ApplicationFramework.php:3521
ApplicationFramework\GetQueuedTaskCount
GetQueuedTaskCount($Callback=NULL, $Parameters=NULL, $Priority=NULL, $Description=NULL)
Get number of queued tasks that match supplied values.
Definition: ApplicationFramework.php:3008
ApplicationFramework\DeleteTask
DeleteTask($TaskId)
Remove task from task queues.
Definition: ApplicationFramework.php:3073
ApplicationFramework\LOGLVL_DEBUG
const LOGLVL_DEBUG
DEBUG error logging level.
Definition: ApplicationFramework.php:2442
ApplicationFramework\ReachedViaAjax
static ReachedViaAjax($NewSetting=NULL)
Determine if we were reached via an AJAX-based (or other automated) page load.
Definition: ApplicationFramework.php:3740
ApplicationFramework\EVENTTYPE_NAMED
const EVENTTYPE_NAMED
Named result event type.
Definition: ApplicationFramework.php:2501
ApplicationFramework\EVENTTYPE_FIRST
const EVENTTYPE_FIRST
First response event type.
Definition: ApplicationFramework.php:2493
ApplicationFramework\WasUrlRewritten
static WasUrlRewritten($ScriptName="index.php")
Determine if the URL was rewritten, i.e., the script is being accessed through a URL that isn&#39;t direc...
Definition: ApplicationFramework.php:3707
ApplicationFramework\EVENTTYPE_DEFAULT
const EVENTTYPE_DEFAULT
Default event type.
Definition: ApplicationFramework.php:2481
ApplicationFramework\GetPageUrl
GetPageUrl()
Get the full URL to the page.
Definition: ApplicationFramework.php:1173
JShrink\Minifier\minify
static minify($js, $options=array())
Takes a string containing javascript and removes unneeded characters in order to shrink the code with...
Definition: Minifier.php:103
ApplicationFramework\CONTEXT_ENV
const CONTEXT_ENV
File loading context: environmental include files.
Definition: ApplicationFramework.php:1551
ApplicationFramework\TemplateLocationCacheExpirationInterval
TemplateLocationCacheExpirationInterval($NewInterval=DB_NOVALUE, $Persistent=FALSE)
Get/set UI template location cache expiration period in minutes.
Definition: ApplicationFramework.php:486
ApplicationFramework\RequireUIFile
RequireUIFile($FileNames, $Order=self::ORDER_MIDDLE)
Add file to list of required UI files.
Definition: ApplicationFramework.php:1817
ApplicationFramework\SetBrowserCacheExpirationTime
SetBrowserCacheExpirationTime($MaxAge)
Set headers to control client-side caching of data served to the browser in this page load (usually J...
Definition: ApplicationFramework.php:4139
ApplicationFramework\IsStaticOnlyEvent
IsStaticOnlyEvent($EventName)
Report whether specified event only allows static callbacks.
Definition: ApplicationFramework.php:2767
ApplicationFramework\CONTEXT_PAGE
const CONTEXT_PAGE
File loading context: PHP page file (from "pages").
Definition: ApplicationFramework.php:1553
ApplicationFramework\PageCacheExpirationPeriod
PageCacheExpirationPeriod($NewValue=DB_NOVALUE, $Persistent=FALSE)
Get/set page cache expiration period in seconds.
Definition: ApplicationFramework.php:2005
ApplicationFramework\IsRegisteredEvent
IsRegisteredEvent($EventName)
Check if event has been registered (is available to be signaled).
Definition: ApplicationFramework.php:2539
ApplicationFramework\ReleaseLock
ReleaseLock($LockName)
Release lock with specified name.
Definition: ApplicationFramework.php:4073
ApplicationFramework\SignalEvent
SignalEvent($EventName, $Parameters=NULL)
Signal that an event has occured.
Definition: ApplicationFramework.php:2684
ApplicationFramework\BasePath
static BasePath()
Get current base path (usually the part after the host name).
Definition: ApplicationFramework.php:3661
ApplicationFramework\LOGLVL_TRACE
const LOGLVL_TRACE
TRACE error logging level.
Definition: ApplicationFramework.php:2437
ApplicationFramework\LOGLVL_WARNING
const LOGLVL_WARNING
WARNING error logging level.
Definition: ApplicationFramework.php:2453
ApplicationFramework\PRIORITY_MEDIUM
const PRIORITY_MEDIUM
Medium (default) priority.
Definition: ApplicationFramework.php:2901
ApplicationFramework\LogError
LogError($Level, $Msg)
Write error message to log.
Definition: ApplicationFramework.php:2203
ApplicationFramework\DatabaseSlowQueryThresholdForForeground
DatabaseSlowQueryThresholdForForeground($NewValue=DB_NOVALUE, $Persistent=FALSE)
Get/set threshold for when database queries are considered "slow" when running in the foreground...
Definition: ApplicationFramework.php:3842
ApplicationFramework\LogHighMemoryUsage
LogHighMemoryUsage($NewValue=DB_NOVALUE, $Persistent=FALSE)
Get/set whether logging of high memory usage is enabled.
Definition: ApplicationFramework.php:2167
Database\SlowQueryThreshold
static SlowQueryThreshold($NewValue=NULL)
Get/set current threshold for what is considered a "slow" SQL query.
Definition: Database.php:1248
ApplicationFramework\ScssRewriteSupport
static ScssRewriteSupport()
Determine if SCSS rewrite support is available.
Definition: ApplicationFramework.php:3533
ApplicationFramework\OnCrash
OnCrash()
Called automatically at program termination to ensure output is written out.
Definition: ApplicationFramework.php:5750
ApplicationFramework\GetKnownPeriodicEvents
GetKnownPeriodicEvents()
Get list of known periodic events.
Definition: ApplicationFramework.php:2817
ApplicationFramework\JavascriptMinimizationEnabled
JavascriptMinimizationEnabled($NewValue=DB_NOVALUE, $Persistent=FALSE)
Get/set whether the application framework will attempt to generate minimized JavaScript.
Definition: ApplicationFramework.php:603
ApplicationFramework\UseMinimizedJavascript
UseMinimizedJavascript($NewValue=DB_NOVALUE, $Persistent=FALSE)
Get/set whether minimized JavaScript will be searched for and used if found.
Definition: ApplicationFramework.php:586
ApplicationFramework\EVENTTYPE_CHAIN
const EVENTTYPE_CHAIN
Result chaining event type.
Definition: ApplicationFramework.php:2487
ApplicationFramework\SlowPageLoadThreshold
SlowPageLoadThreshold($NewValue=DB_NOVALUE, $Persistent=FALSE)
Get/set how long a page load can take before it should be considered "slow" and may be logged...
Definition: ApplicationFramework.php:2148
ApplicationFramework\AddMetaTagOnce
AddMetaTagOnce($Attribs, $UniqueAttribs=NULL)
Add meta tag to page output if not already present.
Definition: ApplicationFramework.php:1965
ApplicationFramework\ClearPageCacheForTag
ClearPageCacheForTag($Tag)
Clear all cached pages associated with specified tag.
Definition: ApplicationFramework.php:2057
ApplicationFramework\DoNotCacheCurrentPage
DoNotCacheCurrentPage()
Prevent the current page from being cached.
Definition: ApplicationFramework.php:2015
ApplicationFramework\SuppressStandardPageStartAndEnd
SuppressStandardPageStartAndEnd($NewSetting=TRUE)
Suppress loading of standard page start and end files.
Definition: ApplicationFramework.php:1285
scssc
SCSS compiler written in PHP.
Definition: scssc.php:45
ApplicationFramework\GetSecondsBeforeTimeout
GetSecondsBeforeTimeout()
Get remaining available (PHP) execution time.
Definition: ApplicationFramework.php:1942
ApplicationFramework\TaskIsInQueue
TaskIsInQueue($Callback, $Parameters=NULL)
Check if task is already in queue or currently running.
Definition: ApplicationFramework.php:2968
ApplicationFramework\GetCurrentBackgroundPriority
GetCurrentBackgroundPriority()
Determine current priority if running in background.
Definition: ApplicationFramework.php:3136
ApplicationFramework\AddFunctionDirectories
AddFunctionDirectories($Dir, $SearchLast=FALSE, $SkipSlashCheck=FALSE)
Add additional directory(s) to be searched for function ("F-") files.
Definition: ApplicationFramework.php:445
ApplicationFramework\GetAlternateDomains
GetAlternateDomains()
Get the list of configured alternate domains.
Definition: ApplicationFramework.php:3467
StdLib\ArrayPermutations
static ArrayPermutations($Items, $Perms=array())
Return all possible permutations of a given array.
Definition: StdLib.php:724
ApplicationFramework\AddPageCacheTag
AddPageCacheTag($Tag, $Pages=NULL)
Add caching tag for current page or specified pages.
Definition: ApplicationFramework.php:2026
ApplicationFramework\GetPageCacheInfo
GetPageCacheInfo()
Get page cache information.
Definition: ApplicationFramework.php:2086
ApplicationFramework\ORDER_MIDDLE
const ORDER_MIDDLE
Handle item after ORDER_FIRST and before ORDER_LAST items.
Definition: ApplicationFramework.php:2506
ApplicationFramework\GetPercentFreeMemory
static GetPercentFreeMemory()
Get current percentage of free memory.
Definition: ApplicationFramework.php:3778
ApplicationFramework\MIN_DB_SLOW_QUERY_THRESHOLD
const MIN_DB_SLOW_QUERY_THRESHOLD
minimum threshold for what is considered a slow database query
Definition: ApplicationFramework.php:3872
ApplicationFramework\OUTPUT_MODIFICATION_THRESHOLD
const OUTPUT_MODIFICATION_THRESHOLD
Threshold below which page output modifications are considered to have failed.
Definition: ApplicationFramework.php:5932
ApplicationFramework\RegisterEvent
RegisterEvent($EventsOrEventName, $EventType=NULL)
Register one or more events that may be signaled.
Definition: ApplicationFramework.php:2518
ApplicationFramework\UrlFingerprintingEnabled
UrlFingerprintingEnabled($NewValue=DB_NOVALUE, $Persistent=FALSE)
Get/set whether URL fingerprinting is enabled.
Definition: ApplicationFramework.php:536
PHP
PHP
Definition: RSSClient--Test.php:12
ApplicationFramework\JsMinRewriteSupport
static JsMinRewriteSupport()
Determine if rewrite support for JavaScript minification is available.
Definition: ApplicationFramework.php:3545
ApplicationFramework\DownloadFile
DownloadFile($FilePath, $FileName=NULL, $MimeType=NULL)
Send specified file for download by user.
Definition: ApplicationFramework.php:3892
ApplicationFramework\GetPageName
GetPageName()
Get name of page being loaded.
Definition: ApplicationFramework.php:1130
ApplicationFramework\LogSlowPageLoads
LogSlowPageLoads($NewValue=DB_NOVALUE, $Persistent=FALSE)
Get/set whether logging of long page load times is enabled.
Definition: ApplicationFramework.php:2132
DB_NOVALUE
const DB_NOVALUE
Definition: Database.php:1784
ApplicationFramework\CleanUrlIsMapped
CleanUrlIsMapped($Path)
Report whether clean URL has already been mapped.
Definition: ApplicationFramework.php:3302
ApplicationFramework\HighMemoryUsageThreshold
HighMemoryUsageThreshold($NewValue=DB_NOVALUE, $Persistent=FALSE)
Get/set what percentage of max memory (set via the memory_limit PHP configuration directive) a page l...
Definition: ApplicationFramework.php:2184
ApplicationFramework\SetJumpToPage
SetJumpToPage($Page, $Delay=0, $IsLiteral=FALSE)
Set URL of page to autoload after PHP page file is executed.
Definition: ApplicationFramework.php:1190
ApplicationFramework\MIN_GC_PROBABILITY
const MIN_GC_PROBABILITY
Definition: ApplicationFramework.php:4304
ApplicationFramework\CONTEXT_START
const CONTEXT_START
File loading context: page start file.
Definition: ApplicationFramework.php:1559
ApplicationFramework\CONTEXT_COMMON
const CONTEXT_COMMON
File loading context: common HTML files.
Definition: ApplicationFramework.php:1555
ApplicationFramework\PRIORITY_HIGH
const PRIORITY_HIGH
Highest priority.
Definition: ApplicationFramework.php:2899
ApplicationFramework\SuppressSessionInitialization
static SuppressSessionInitialization($NewValue=NULL)
Get/set whether session initialization is intentionally suppressed.
Definition: ApplicationFramework.php:1295
ApplicationFramework\LoadFunction
LoadFunction($Callback)
Attempt to load code for function or method if not currently available.
Definition: ApplicationFramework.php:1883
ApplicationFramework\GetNextHigherBackgroundPriority
GetNextHigherBackgroundPriority($Priority=NULL)
Get next higher possible background task priority.
Definition: ApplicationFramework.php:3150
ApplicationFramework\GetRunningTaskList
GetRunningTaskList($Count=100, $Offset=0)
Retrieve list of tasks currently running.
Definition: ApplicationFramework.php:3021
ApplicationFramework\FindCommonTemplate
FindCommonTemplate($BaseName)
Preserved for backward compatibility for use with code written prior to October 2012.
Definition: ApplicationFramework.php:4217
ApplicationFramework\EventWillNextRunAt
EventWillNextRunAt($EventName, $Callback)
Get date/time a periodic event will next run.
Definition: ApplicationFramework.php:2782
ApplicationFramework\SessionLifetime
static SessionLifetime($NewValue=NULL)
Get/set session timeout in seconds.
Definition: ApplicationFramework.php:3495
ApplicationFramework\DefaultUserInterface
static DefaultUserInterface($UIName=NULL)
Get/set name of current default user interface.
Definition: ApplicationFramework.php:1309
ApplicationFramework\HookEvent
HookEvent($EventsOrEventName, $Callback=NULL, $Order=self::ORDER_MIDDLE)
Hook one or more functions to be called when the specified event is signaled.
Definition: ApplicationFramework.php:2576
ApplicationFramework\ClearPageCache
ClearPageCache()
Clear all pages from page cache.
Definition: ApplicationFramework.php:2072
ApplicationFramework\IsHookedEvent
IsHookedEvent($EventName)
Check if an event is registered and is hooked to.
Definition: ApplicationFramework.php:2551
ApplicationFramework\GetFreeMemory
static GetFreeMemory()
Get current amount of free memory.
Definition: ApplicationFramework.php:3768
ApplicationFramework\ObjectLocationCacheExpirationInterval
ObjectLocationCacheExpirationInterval($NewValue=DB_NOVALUE, $Persistent=FALSE)
Get/set object file location cache expiration period in minutes.
Definition: ApplicationFramework.php:511
ApplicationFramework\FT_JAVASCRIPT
const FT_JAVASCRIPT
JavaScript file type.
Definition: ApplicationFramework.php:1873
ApplicationFramework\CONTEXT_INTERFACE
const CONTEXT_INTERFACE
File loading context: HTML interface file.
Definition: ApplicationFramework.php:1557
ApplicationFramework\AddImageDirectories
AddImageDirectories($Dir, $SearchLast=FALSE, $SkipSlashCheck=FALSE)
Add additional directory(s) to be searched for image files.
Definition: ApplicationFramework.php:356
ApplicationFramework\FT_CSS
const FT_CSS
CSS file type.
Definition: ApplicationFramework.php:1869
ApplicationFramework\HtmlCharset
HtmlCharset($NewSetting=NULL)
Get/set HTTP character encoding value.
Definition: ApplicationFramework.php:1229
ApplicationFramework\ORDER_FIRST
const ORDER_FIRST
Handle item first (i.e.
Definition: ApplicationFramework.php:2504
StdLib\SQL_DATE_FORMAT
const SQL_DATE_FORMAT
Format to feed to date() to get SQL-compatible date/time string.
Definition: StdLib.php:913
ApplicationFramework\GetPhpMaxUploadSize
static GetPhpMaxUploadSize()
Get the maximum size for a file upload in bytes.
Definition: ApplicationFramework.php:3800
AFTaskManager
Task manager component of top-level framework for web applications.
Definition: AFTaskManager.php:14
ApplicationFramework\IncludeUIFile
IncludeUIFile($FileNames, $AdditionalAttributes=NULL)
Search UI directories for specified JavaScript or CSS file and print HTML tag to load file...
Definition: ApplicationFramework.php:1747
ApplicationFramework\GetUncleanUrlForPath
GetUncleanUrlForPath($Path)
Get the unclean URL for mapped for a path.
Definition: ApplicationFramework.php:3368
ApplicationFramework\FT_IMAGE
const FT_IMAGE
Image (GIF/JPG/PNG) file type.
Definition: ApplicationFramework.php:1871
ApplicationFramework\ClearTemplateLocationCache
ClearTemplateLocationCache()
Clear template location cache.
Definition: ApplicationFramework.php:496
ApplicationFramework\GetPrefixForAlternateDomain
GetPrefixForAlternateDomain($Domain)
Get configured prefix for an alternate domain, if one exists.
Definition: ApplicationFramework.php:3478
ApplicationFramework\GetFileType
static GetFileType($FileName)
Determine type of specified file based on the file name.
Definition: ApplicationFramework.php:1834
ApplicationFramework\GetUncleanUrl
GetUncleanUrl()
Get the unclean URL for the current page.
Definition: ApplicationFramework.php:3427
ApplicationFramework\DoNotMinimizeFile
DoNotMinimizeFile($File)
Specify file(s) to not attempt to minimize.
Definition: ApplicationFramework.php:1244
ApplicationFramework\RecordContextInCaseOfCrash
RecordContextInCaseOfCrash($BacktraceOptions=0, $BacktraceLimit=0)
Record the current execution context in case of crash.
Definition: ApplicationFramework.php:622
ApplicationFramework\JumpToPageIsSet
JumpToPageIsSet()
Report whether a page to autoload has been set.
Definition: ApplicationFramework.php:1215
ApplicationFramework\ORDER_LAST
const ORDER_LAST
Handle item last (i.e.
Definition: ApplicationFramework.php:2508
ApplicationFramework\LoggingLevel
LoggingLevel($NewValue=DB_NOVALUE, $Persistent=FALSE)
Get/set logging level.
Definition: ApplicationFramework.php:2318
ApplicationFramework\UseBaseTag
UseBaseTag($NewValue=NULL)
Get/set whether or not to use the "base" tag to ensure relative URL paths are correct.
Definition: ApplicationFramework.php:1260
ApplicationFramework\HtaccessSupport
static HtaccessSupport()
Determine if .htaccess files are enabled.
Definition: ApplicationFramework.php:3509
ApplicationFramework\GetTask
GetTask($TaskId)
Retrieve task info from queue (either running or queued tasks).
Definition: ApplicationFramework.php:3085
ApplicationFramework\GetPhpMemoryLimit
static GetPhpMemoryLimit()
Get PHP memory limit in bytes.
Definition: ApplicationFramework.php:3788
ApplicationFramework\SetContextFilter
SetContextFilter($Context, $NewSetting)
Configure filtering of variables left in the execution environment for the next loaded file after a P...
Definition: ApplicationFramework.php:1532
ApplicationFramework\LoadPage
LoadPage($PageName)
Load page PHP and HTML/TPL files.
Definition: ApplicationFramework.php:641
ApplicationFramework\GetUserInterfacePaths
GetUserInterfacePaths($FilterExp=NULL)
Get list of available user interfaces and the relative paths to the base directory for each interface...
Definition: ApplicationFramework.php:1394
ApplicationFramework\AddEnvInclude
AddEnvInclude($FileName)
Add file to be included to set up environment.
Definition: ApplicationFramework.php:1511
ApplicationFramework\GetCleanUrlList
GetCleanUrlList()
Get list of all clean URLs currently added.
Definition: ApplicationFramework.php:3440
ApplicationFramework\NOVALUE
const NOVALUE
Definition: ApplicationFramework.php:6069
ApplicationFramework\GetUserInterfaces
GetUserInterfaces($FilterExp=NULL)
Get list of available user interfaces and their labels.
Definition: ApplicationFramework.php:1343
ApplicationFramework\ReQueueOrphanedTask
ReQueueOrphanedTask($TaskId, $NewPriority=NULL)
Move orphaned task back into queue.
Definition: ApplicationFramework.php:3052
ApplicationFramework\GetLogEntries
GetLogEntries($Limit=0)
Get log entries, in reverse chronological order.
Definition: ApplicationFramework.php:2351
ApplicationFramework\GUIFile
GUIFile($FileName)
Search UI directories for specified image or CSS file and return name of correct file.
Definition: ApplicationFramework.php:1569
ApplicationFramework\QueueTask
QueueTask($Callback, $Parameters=NULL, $Priority=self::PRIORITY_LOW, $Description="")
Add task to queue.
Definition: ApplicationFramework.php:2929
ApplicationFramework\TaskExecutionEnabled
TaskExecutionEnabled($NewValue=DB_NOVALUE, $Persistent=FALSE)
Get/set whether automatic task execution is enabled.
Definition: ApplicationFramework.php:3100
ApplicationFramework\GetTaskCallbackSynopsis
static GetTaskCallbackSynopsis($TaskInfo)
Get printable synopsis for task callback.
Definition: ApplicationFramework.php:3126
ApplicationFramework\PUIFile
PUIFile($FileName)
Search UI directories for specified interface (image, CSS, JavaScript etc) file and print name of cor...
Definition: ApplicationFramework.php:1727
ApplicationFramework\GenerateCompactCss
GenerateCompactCss($NewValue=DB_NOVALUE, $Persistent=FALSE)
Get/set whether generating compact CSS (when compiling SCSS) is enabled.
Definition: ApplicationFramework.php:569
ApplicationFramework\LOGFILE_MAX_LINE_LENGTH
const LOGFILE_MAX_LINE_LENGTH
Maximum length for a line in the log file.
Definition: ApplicationFramework.php:2469
ApplicationFramework\TaskMgr
& TaskMgr()
Access the task manager.
Definition: ApplicationFramework.php:2911
ApplicationFramework\GetPageLocation
GetPageLocation()
Get the URL path to the page without the base path, if present.
Definition: ApplicationFramework.php:1140
ApplicationFramework\ScssSupportEnabled
ScssSupportEnabled($NewValue=DB_NOVALUE, $Persistent=FALSE)
Get/set whether SCSS compilation support is enabled.
Definition: ApplicationFramework.php:552
ApplicationFramework\BeginAjaxResponse
BeginAjaxResponse($ResponseType="JSON", $CloseSession=TRUE)
Begin an AJAX response, setting the necessary HTTP headers and optionally closing the PHP session...
Definition: ApplicationFramework.php:4101
ApplicationFramework\MaxTasks
MaxTasks($NewValue=DB_NOVALUE, $Persistent=FALSE)
Get/set maximum number of tasks to have running simultaneously.
Definition: ApplicationFramework.php:3114
ApplicationFramework\GetElapsedExecutionTime
GetElapsedExecutionTime()
Get time elapsed since constructor was called.
Definition: ApplicationFramework.php:1933
ApplicationFramework\RunPeriodicEvent
static RunPeriodicEvent($EventName, $Callback, $Parameters)
Run periodic event and then save info needed to know when to run it again.
Definition: ApplicationFramework.php:2859
ApplicationFramework\SetBrowserDetectionFunc
SetBrowserDetectionFunc($DetectionFunc)
Specify function to use to detect the web browser type.
Definition: ApplicationFramework.php:458
ApplicationFramework\RootUrl
static RootUrl()
Get portion of current URL through host name, with no trailing slash (e.g.
Definition: ApplicationFramework.php:3558
ApplicationFramework\AddCleanUrl
AddCleanUrl($Pattern, $Page, $GetVars=NULL, $Template=NULL)
Add clean URL mapping.
Definition: ApplicationFramework.php:3202
JavaScriptPacker
Definition: JavaScriptPacker.php:69
StdLib\GetCallerInfo
static GetCallerInfo($Element=NULL)
Get info about call to current function.
Definition: StdLib.php:26
ApplicationFramework\PRIORITY_BACKGROUND
const PRIORITY_BACKGROUND
Lowest priority.
Definition: ApplicationFramework.php:2905
ApplicationFramework\LogMessage
LogMessage($Level, $Msg)
Write status message to log.
Definition: ApplicationFramework.php:2245
ApplicationFramework\FT_OTHER
const FT_OTHER
File type other than CSS, image, or JavaScript.
Definition: ApplicationFramework.php:1867
ApplicationFramework\AddObjectDirectory
static AddObjectDirectory($Dir, $NamespacePrefixes=array(), $Callback=NULL)
Add directory to be searched for object files when autoloading.
Definition: ApplicationFramework.php:296