Commit | Line | Data |
---|---|---|
e3d28c74 TO |
1 | <?php |
2 | namespace Civi\Core; | |
3 | ||
4 | /** | |
5 | * Class Paths | |
6 | * @package Civi\Core | |
7 | * | |
8 | * This paths class translates path-expressions into local file paths and | |
9 | * URLs. Path-expressions may take a few forms: | |
10 | * | |
11 | * - Paths and URLs may use a variable prefix. For example, '[civicrm.files]/upload' | |
12 | * - Paths and URLS may be absolute. | |
13 | * - Paths may be relative (base dir: [civicrm.files]). | |
14 | * - URLs may be relative (base dir: [cms.root]). | |
15 | */ | |
16 | class Paths { | |
17 | ||
18 | const DEFAULT_URL = 'cms.root'; | |
19 | const DEFAULT_PATH = 'civicrm.files'; | |
20 | ||
21 | /** | |
22 | * @var array | |
23 | * Array(string $name => array(url => $, path => $)). | |
24 | */ | |
c64f69d9 | 25 | private $variables = []; |
e3d28c74 | 26 | |
c64f69d9 | 27 | private $variableFactory = []; |
e3d28c74 | 28 | |
e8e8f3ad | 29 | /** |
30 | * Class constructor. | |
31 | */ | |
e3d28c74 | 32 | public function __construct() { |
f553d1ea | 33 | $paths = $this; |
e3d28c74 | 34 | $this |
e3d28c74 TO |
35 | ->register('civicrm.root', function () { |
36 | return \CRM_Core_Config::singleton()->userSystem->getCiviSourceStorage(); | |
37 | }) | |
2f4b426c | 38 | ->register('civicrm.packages', function () { |
c64f69d9 | 39 | return [ |
2f4b426c TO |
40 | 'path' => \Civi::paths()->getPath('[civicrm.root]/packages/'), |
41 | 'url' => \Civi::paths()->getUrl('[civicrm.root]/packages/'), | |
c64f69d9 | 42 | ]; |
2f4b426c TO |
43 | }) |
44 | ->register('civicrm.vendor', function () { | |
c64f69d9 | 45 | return [ |
2f4b426c TO |
46 | 'path' => \Civi::paths()->getPath('[civicrm.root]/vendor/'), |
47 | 'url' => \Civi::paths()->getUrl('[civicrm.root]/vendor/'), | |
c64f69d9 | 48 | ]; |
2f4b426c TO |
49 | }) |
50 | ->register('civicrm.bower', function () { | |
c64f69d9 | 51 | return [ |
2f4b426c TO |
52 | 'path' => \Civi::paths()->getPath('[civicrm.root]/bower_components/'), |
53 | 'url' => \Civi::paths()->getUrl('[civicrm.root]/bower_components/'), | |
c64f69d9 | 54 | ]; |
2f4b426c | 55 | }) |
e3d28c74 TO |
56 | ->register('civicrm.files', function () { |
57 | return \CRM_Core_Config::singleton()->userSystem->getDefaultFileStorage(); | |
58 | }) | |
86a36a73 | 59 | ->register('civicrm.private', function () { |
0bd3f65c TO |
60 | return [ |
61 | // For backward compatibility with existing deployments, this | |
62 | // effectively returns `dirname(CIVICRM_TEMPLATE_COMPILEDIR)`. | |
63 | // That's confusing. Future installers should probably set `civicrm.private` | |
64 | // explicitly instead of setting `CIVICRM_TEMPLATE_COMPILEDIR`. | |
65 | 'path' => \CRM_Utils_File::baseFilePath(), | |
66 | ]; | |
67 | }) | |
68 | ->register('civicrm.log', function () { | |
69 | return [ | |
70 | 'path' => \Civi::paths()->getPath('[civicrm.private]/ConfigAndLog'), | |
71 | ]; | |
72 | }) | |
73 | ->register('civicrm.compile', function () { | |
74 | return [ | |
75 | // These two formulations are equivalent in typical deployments; however, | |
76 | // for existing systems which previously customized CIVICRM_TEMPLATE_COMPILEDIR, | |
77 | // using the constant should be more backward-compatibility. | |
78 | 'path' => defined('CIVICRM_TEMPLATE_COMPILEDIR') ? CIVICRM_TEMPLATE_COMPILEDIR : \Civi::paths()->getPath('[civicrm.private]/templates_c'), | |
79 | ]; | |
86a36a73 | 80 | }) |
f553d1ea | 81 | ->register('wp.frontend.base', function () { |
c64f69d9 | 82 | return ['url' => rtrim(CIVICRM_UF_BASEURL, '/') . '/']; |
f553d1ea KC |
83 | }) |
84 | ->register('wp.frontend', function () use ($paths) { | |
85 | $config = \CRM_Core_Config::singleton(); | |
86 | $suffix = defined('CIVICRM_UF_WP_BASEPAGE') ? CIVICRM_UF_WP_BASEPAGE : $config->wpBasePage; | |
c64f69d9 | 87 | return [ |
f553d1ea | 88 | 'url' => $paths->getVariable('wp.frontend.base', 'url') . $suffix, |
c64f69d9 | 89 | ]; |
f553d1ea KC |
90 | }) |
91 | ->register('wp.backend.base', function () { | |
c64f69d9 | 92 | return ['url' => rtrim(CIVICRM_UF_BASEURL, '/') . '/wp-admin/']; |
f553d1ea KC |
93 | }) |
94 | ->register('wp.backend', function () use ($paths) { | |
c64f69d9 | 95 | return [ |
f553d1ea | 96 | 'url' => $paths->getVariable('wp.backend.base', 'url') . 'admin.php', |
c64f69d9 | 97 | ]; |
f553d1ea | 98 | }) |
e3d28c74 | 99 | ->register('cms', function () { |
c64f69d9 | 100 | return [ |
e3d28c74 TO |
101 | 'path' => \CRM_Core_Config::singleton()->userSystem->cmsRootPath(), |
102 | 'url' => \CRM_Utils_System::baseCMSURL(), | |
c64f69d9 | 103 | ]; |
e3d28c74 TO |
104 | }) |
105 | ->register('cms.root', function () { | |
c64f69d9 | 106 | return [ |
e3d28c74 TO |
107 | 'path' => \CRM_Core_Config::singleton()->userSystem->cmsRootPath(), |
108 | // Misleading: this *removes* the language part of the URL, producing a pristine base URL. | |
109 | 'url' => \CRM_Utils_System::languageNegotiationURL(\CRM_Utils_System::baseCMSURL(), FALSE, TRUE), | |
c64f69d9 | 110 | ]; |
e3d28c74 TO |
111 | }); |
112 | } | |
113 | ||
114 | /** | |
115 | * Register a new URL/file path mapping. | |
116 | * | |
117 | * @param string $name | |
b698e2d5 | 118 | * The name of the variable. |
e3d28c74 TO |
119 | * @param callable $factory |
120 | * Function which returns an array with keys: | |
121 | * - path: string. | |
122 | * - url: string. | |
4b350175 | 123 | * @return Paths |
e3d28c74 TO |
124 | */ |
125 | public function register($name, $factory) { | |
b698e2d5 | 126 | $this->variableFactory[$name] = $factory; |
e3d28c74 TO |
127 | return $this; |
128 | } | |
129 | ||
b698e2d5 TO |
130 | /** |
131 | * @param string $name | |
132 | * Ex: 'civicrm.root'. | |
133 | * @param string $attr | |
134 | * Ex: 'url', 'path'. | |
135 | * @return mixed | |
136 | */ | |
137 | public function getVariable($name, $attr) { | |
138 | if (!isset($this->variables[$name])) { | |
139 | $this->variables[$name] = call_user_func($this->variableFactory[$name]); | |
313a57a0 TO |
140 | if (isset($GLOBALS['civicrm_paths'][$name])) { |
141 | $this->variables[$name] = array_merge($this->variables[$name], $GLOBALS['civicrm_paths'][$name]); | |
142 | } | |
e3d28c74 | 143 | } |
b698e2d5 | 144 | if (!isset($this->variables[$name][$attr])) { |
e3d28c74 TO |
145 | throw new \RuntimeException("Cannot resolve path using \"$name.$attr\""); |
146 | } | |
b698e2d5 TO |
147 | return $this->variables[$name][$attr]; |
148 | } | |
149 | ||
e8e8f3ad | 150 | /** |
151 | * Does the variable exist. | |
152 | * | |
153 | * @param string $name | |
154 | * | |
155 | * @return bool | |
156 | */ | |
b698e2d5 TO |
157 | public function hasVariable($name) { |
158 | return isset($this->variableFactory[$name]); | |
e3d28c74 TO |
159 | } |
160 | ||
161 | /** | |
162 | * Determine the absolute path to a file, given that the file is most likely | |
b698e2d5 | 163 | * in a given particular variable. |
e3d28c74 TO |
164 | * |
165 | * @param string $value | |
b698e2d5 TO |
166 | * The file path. |
167 | * Use "." to reference to default file root. | |
168 | * Values may begin with a variable, e.g. "[civicrm.files]/upload". | |
e3d28c74 TO |
169 | * @return mixed|string |
170 | */ | |
171 | public function getPath($value) { | |
172 | $defaultContainer = self::DEFAULT_PATH; | |
173 | if ($value && $value{0} == '[' && preg_match(';^\[([a-zA-Z0-9\._]+)\]/(.*);', $value, $matches)) { | |
174 | $defaultContainer = $matches[1]; | |
175 | $value = $matches[2]; | |
176 | } | |
177 | if (empty($value)) { | |
178 | return FALSE; | |
179 | } | |
180 | if ($value === '.') { | |
181 | $value = ''; | |
182 | } | |
b698e2d5 | 183 | return \CRM_Utils_File::absoluteDirectory($value, $this->getVariable($defaultContainer, 'path')); |
e3d28c74 TO |
184 | } |
185 | ||
186 | /** | |
b698e2d5 | 187 | * Determine the URL to a file. |
e3d28c74 TO |
188 | * |
189 | * @param string $value | |
b698e2d5 | 190 | * The file path. The path may begin with a variable, e.g. "[civicrm.files]/upload". |
e3d28c74 TO |
191 | * @param string $preferFormat |
192 | * The preferred format ('absolute', 'relative'). | |
193 | * The result data may not meet the preference -- if the setting | |
194 | * refers to an external domain, then the result will be | |
195 | * absolute (regardless of preference). | |
196 | * @param bool|NULL $ssl | |
197 | * NULL to autodetect. TRUE to force to SSL. | |
198 | * @return mixed|string | |
199 | */ | |
200 | public function getUrl($value, $preferFormat = 'relative', $ssl = NULL) { | |
201 | $defaultContainer = self::DEFAULT_URL; | |
ac47f7ca | 202 | if ($value && $value{0} == '[' && preg_match(';^\[([a-zA-Z0-9\._]+)\](/(.*))$;', $value, $matches)) { |
e3d28c74 | 203 | $defaultContainer = $matches[1]; |
ac47f7ca | 204 | $value = empty($matches[3]) ? '.' : $matches[3]; |
e3d28c74 TO |
205 | } |
206 | ||
207 | if (empty($value)) { | |
208 | return FALSE; | |
209 | } | |
210 | if ($value === '.') { | |
211 | $value = ''; | |
212 | } | |
213 | if (substr($value, 0, 4) == 'http') { | |
214 | return $value; | |
215 | } | |
216 | ||
b698e2d5 | 217 | $value = $this->getVariable($defaultContainer, 'url') . $value; |
e3d28c74 TO |
218 | |
219 | if ($preferFormat === 'relative') { | |
220 | $parsed = parse_url($value); | |
221 | if (isset($_SERVER['HTTP_HOST']) && isset($parsed['host']) && $_SERVER['HTTP_HOST'] == $parsed['host']) { | |
222 | $value = $parsed['path']; | |
223 | } | |
224 | } | |
225 | ||
226 | if ($ssl || ($ssl === NULL && \CRM_Utils_System::isSSL())) { | |
227 | $value = str_replace('http://', 'https://', $value); | |
228 | } | |
229 | ||
230 | return $value; | |
231 | } | |
232 | ||
233 | } |