Merge pull request #16348 from eileenmcnaughton/ev_online
[civicrm-core.git] / Civi / Core / Paths.php
CommitLineData
e3d28c74
TO
1<?php
2namespace 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 */
16class 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}