Civi::paths() - Decouple settings from paths. Allow path vars.
authorTim Otten <totten@civicrm.org>
Tue, 15 Sep 2015 22:58:17 +0000 (15:58 -0700)
committerTim Otten <totten@civicrm.org>
Thu, 17 Sep 2015 22:49:32 +0000 (15:49 -0700)
There has been a policy underwhich paths and URLs are assumed to be relative
to different points (urls => webroot; paths => sites/*/files/civicrm).  For
some settings, this is fairly confusing.  The policy had been encoded in
`SettingsBag::getPath` and `SettingsBag::getUrl`.  It's particularly
confusing because (sometimes) the default value of a specific property isn't
really aligned with the policy.

This revision attempts to cleanup in two ways:

 1. It removes policy from `SettingsBag` and puts it in `Civi\Core\Paths`.
 2. It makes the policy less important by allowing variables in the paths.
   * `[civicrm.files]/upload` might evaluate to `/var/www/sites/default/files/civicrm/upload`
   * `[cms.root]/myuploads` might evaluate to `/var/www/myuploads`

The revision also updates MagicMerge to store various path and URL policies
in `getPropertyMap()` rather than adhoc callbacks.

CRM/Core/Config/Defaults.php
CRM/Core/Config/MagicMerge.php
CRM/Core/Config/Runtime.php
CRM/Utils/File.php
CRM/Utils/Rule.php
Civi.php
Civi/Core/Paths.php [new file with mode: 0644]
Civi/Core/SettingsBag.php
tests/phpunit/Civi/Core/SettingsManagerTest.php

index a89db3a5d9409bc024d16f66bf8ac69b35313c2e..af90e5794300430054503e72b6c73227251146c4 100644 (file)
  */
 class CRM_Core_Config_Defaults {
 
-  /**
-   * Set the default values.
-   * in an empty db, also called when setting component using GUI
-   *
-   * @param array $defaults
-   *   Associated array of form elements.
-   * @param bool $formMode
-   *   this variable is set true for GUI
-   *   mode (eg: Global setting >> Components)
-   *
-   */
-  public static function setValues(&$defaults, $formMode = FALSE) {
-  }
-
-  public static function getCustomCssUrl($k = NULL) {
-    return Civi::settings()->getUrl('customCSSURL', 'absolute');
-  }
-
-  public static function getCustomFileUploadDir($k = NULL) {
-    $settings = Civi::settings();
-    $value = $settings->getPath('customFileUploadDir');
-    if (empty($value)) {
-      $defaultFileStorage = CRM_Core_Config::singleton()->userSystem->getDefaultFileStorage();
-      $value = $settings->filterPath($defaultFileStorage['path'] . "custom/");
-    }
-    $value = CRM_Utils_File::addTrailingSlash($value);
-    CRM_Utils_File::createDir($value);
-    CRM_Utils_File::restrictAccess($value);
-    return $value;
-  }
-
-
-  public static function getCustomPhpPathDir($k = NULL) {
-    return Civi::settings()->getPath('customPHPPathDir');
-  }
-
-  public static function getCustomTemplateDir($k = NULL) {
-    return Civi::settings()->getPath('customTemplateDir');
-  }
-
-  public static function getExtensionsUrl($k = NULL) {
-    return Civi::settings()->getUrl('extensionsURL', 'absolute');
-  }
-
-  public static function getExtensionsDir($k = NULL) {
-    return Civi::settings()->getPath('extensionsDir');
-  }
-
-  public static function getImageUploadDir($k = NULL) {
-    $settings = Civi::settings();
-    $value = $settings->getPath('imageUploadDir');
-    if (empty($value)) {
-      $defaultFileStorage = CRM_Core_Config::singleton()->userSystem->getDefaultFileStorage();
-      $value = $settings->filterPath($defaultFileStorage['path'] . "persist/contribute/");
-    }
-    $value = CRM_Utils_File::addTrailingSlash($value);
-    CRM_Utils_File::createDir($value);
-    return $value;
-  }
-
-  public static function getImageUploadUrl($k = NULL) {
-    $settings = Civi::settings();
-    $imageUploadURL = $settings->getUrl('imageUploadURL', 'absolute');
-    if (empty($imageUploadURL)) {
-      $defaultFileStorage = CRM_Core_Config::singleton()->userSystem->getDefaultFileStorage();
-      $imageUploadURL = $settings->filterUrl($defaultFileStorage['url'] . 'persist/contribute/', 'absolute');
-    }
-    return $imageUploadURL;
-  }
-
-  public static function getUploadDir($k = NULL) {
-    $settings = Civi::settings();
-    $value = $settings->getPath('uploadDir');
-    if (empty($value)) {
-      $defaultFileStorage = CRM_Core_Config::singleton()->userSystem->getDefaultFileStorage();
-      $value = $settings->filterPath($defaultFileStorage['path'] . "upload/");
-    }
-    $value = CRM_Utils_File::addTrailingSlash($value);
-    CRM_Utils_File::createDir($value);
-    CRM_Utils_File::restrictAccess($value);
-    return $value;
-  }
-
-  public static function getUserFrameworkResourceUrl($k = NULL) {
-    $settings = Civi::settings();
-    $url = $settings->getUrl('userFrameworkResourceURL', 'absolute');
-    if (empty($url)) {
-      $config = CRM_Core_Config::singleton();
-      $civiSource = $config->userSystem->getCiviSourceStorage();
-      $url = $settings->filterUrl($civiSource['url'], 'absolute');
-    }
-    return $url;
-  }
-
-  public static function getResourceBase($k = NULL) {
-    $settings = Civi::settings();
-    $url = $settings->getUrl('userFrameworkResourceURL', 'relative');
-    if (empty($url)) {
-      $config = CRM_Core_Config::singleton();
-      $civiSource = $config->userSystem->getCiviSourceStorage();
-      $url = $settings->filterUrl($civiSource['url'], 'relative');
-    }
-    return $url;
-  }
-
   public static function getDefaultCurrencySymbol($k = NULL) {
     $config = CRM_Core_Config::singleton();
     return $config->defaultCurrencySymbol(Civi::settings()->get('defaultCurrency'));
   }
 
-  public static function setPath($key, $value) {
-    Civi::settings()->setPath($key, $value);
-  }
-
-  public static function setUrl($key, $value) {
-    Civi::settings()->setPath($key, $value);
-  }
-
-  public static function revert($key) {
-    Civi::settings()->revert($key);
-  }
-
 }
index 927ac69c5dc88ac48a5c6737a51c612ee65f800b..377c035f213660c7874eb8bc8d2d46062d360553 100644 (file)
@@ -68,117 +68,105 @@ class CRM_Core_Config_MagicMerge {
    * @return array
    */
   public static function getPropertyMap() {
+    // Each mapping: $propertyName => Array(0 => $type, 1 => $foreignName|NULL, ...).
+    // If $foreignName is omitted/null, then it's assumed to match the $propertyName.
+    // Other parameters may be specified, depending on the type.
     return array(
-      'backtrace' => array('setting', 'backtrace'),
-      'countryLimit' => array('setting', 'countryLimit'),
-      'dashboardCacheTimeout' => array('setting', 'dashboardCacheTimeout'),
-      'dateInputFormat' => array('setting', 'dateInputFormat'),
-      'dateformatDatetime' => array('setting', 'dateformatDatetime'),
-      'dateformatFull' => array('setting', 'dateformatFull'),
-      'dateformatPartial' => array('setting', 'dateformatPartial'),
-      'dateformatTime' => array('setting', 'dateformatTime'),
-      'dateformatYear' => array('setting', 'dateformatYear'),
+      'backtrace' => array('setting'),
+      'countryLimit' => array('setting'),
+      'dashboardCacheTimeout' => array('setting'),
+      'dateInputFormat' => array('setting'),
+      'dateformatDatetime' => array('setting'),
+      'dateformatFull' => array('setting'),
+      'dateformatPartial' => array('setting'),
+      'dateformatTime' => array('setting'),
+      'dateformatYear' => array('setting'),
       'debug' => array('setting', 'debug_enabled'), // renamed.
-      'defaultContactCountry' => array('setting', 'defaultContactCountry'),
-      'defaultContactStateProvince' => array('setting', 'defaultContactStateProvince'),
-      'defaultCurrency' => array('setting', 'defaultCurrency'),
-      'defaultSearchProfileID' => array('setting', 'defaultSearchProfileID'),
-      'doNotAttachPDFReceipt' => array('setting', 'doNotAttachPDFReceipt'),
-      'empoweredBy' => array('setting', 'empoweredBy'),
+      'defaultContactCountry' => array('setting'),
+      'defaultContactStateProvince' => array('setting'),
+      'defaultCurrency' => array('setting'),
+      'defaultSearchProfileID' => array('setting'),
+      'doNotAttachPDFReceipt' => array('setting'),
+      'empoweredBy' => array('setting'),
       'enableComponents' => array('setting', 'enable_components'), // renamed.
-      'enableSSL' => array('setting', 'enableSSL'),
-      'fatalErrorHandler' => array('setting', 'fatalErrorHandler'),
-      'fieldSeparator' => array('setting', 'fieldSeparator'),
-      'fiscalYearStart' => array('setting', 'fiscalYearStart'),
-      'geoAPIKey' => array('setting', 'geoAPIKey'),
-      'geoProvider' => array('setting', 'geoProvider'),
-      'includeAlphabeticalPager' => array('setting', 'includeAlphabeticalPager'),
-      'includeEmailInName' => array('setting', 'includeEmailInName'),
-      'includeNickNameInName' => array('setting', 'includeNickNameInName'),
-      'includeOrderByClause' => array('setting', 'includeOrderByClause'),
-      'includeWildCardInName' => array('setting', 'includeWildCardInName'),
-      'inheritLocale' => array('setting', 'inheritLocale'),
-      'languageLimit' => array('setting', 'languageLimit'),
-      'lcMessages' => array('setting', 'lcMessages'),
-      'legacyEncoding' => array('setting', 'legacyEncoding'),
-      'logging' => array('setting', 'logging'),
-      'mailThrottleTime' => array('setting', 'mailThrottleTime'),
-      'mailerBatchLimit' => array('setting', 'mailerBatchLimit'),
-      'mailerJobSize' => array('setting', 'mailerJobSize'),
-      'mailerJobsMax' => array('setting', 'mailerJobsMax'),
-      'mapAPIKey' => array('setting', 'mapAPIKey'),
-      'mapProvider' => array('setting', 'mapProvider'),
-      'maxFileSize' => array('setting', 'maxFileSize'),
+      'enableSSL' => array('setting'),
+      'fatalErrorHandler' => array('setting'),
+      'fieldSeparator' => array('setting'),
+      'fiscalYearStart' => array('setting'),
+      'geoAPIKey' => array('setting'),
+      'geoProvider' => array('setting'),
+      'includeAlphabeticalPager' => array('setting'),
+      'includeEmailInName' => array('setting'),
+      'includeNickNameInName' => array('setting'),
+      'includeOrderByClause' => array('setting'),
+      'includeWildCardInName' => array('setting'),
+      'inheritLocale' => array('setting'),
+      'languageLimit' => array('setting'),
+      'lcMessages' => array('setting'),
+      'legacyEncoding' => array('setting'),
+      'logging' => array('setting'),
+      'mailThrottleTime' => array('setting'),
+      'mailerBatchLimit' => array('setting'),
+      'mailerJobSize' => array('setting'),
+      'mailerJobsMax' => array('setting'),
+      'mapAPIKey' => array('setting'),
+      'mapProvider' => array('setting'),
+      'maxFileSize' => array('setting'),
       'maxAttachments' => array('setting', 'max_attachments'), // renamed.
-      'monetaryDecimalPoint' => array('setting', 'monetaryDecimalPoint'),
-      'monetaryThousandSeparator' => array('setting', 'monetaryThousandSeparator'),
-      'moneyformat' => array('setting', 'moneyformat'),
-      'moneyvalueformat' => array('setting', 'moneyvalueformat'),
-      'provinceLimit' => array('setting', 'provinceLimit'),
-      'recaptchaOptions' => array('setting', 'recaptchaOptions'),
-      'recaptchaPublicKey' => array('setting', 'recaptchaPublicKey'),
-      'recaptchaPrivateKey' => array('setting', 'recaptchaPrivateKey'),
-      'secondDegRelPermissions' => array('setting', 'secondDegRelPermissions'),
-      'smartGroupCacheTimeout' => array('setting', 'smartGroupCacheTimeout'),
-      'timeInputFormat' => array('setting', 'timeInputFormat'),
-      'userFrameworkLogging' => array('setting', 'userFrameworkLogging'),
-      'userFrameworkUsersTableName' => array('setting', 'userFrameworkUsersTableName'),
-      'verpSeparator' => array('setting', 'verpSeparator'),
-      'wkhtmltopdfPath' => array('setting', 'wkhtmltopdfPath'),
-      'wpBasePage' => array('setting', 'wpBasePage'),
-      'wpLoadPhp' => array('setting', 'wpLoadPhp'),
-
-      'doNotResetCache' => array('local', 'doNotResetCache'),
-      'inCiviCRM' => array('local', 'inCiviCRM'),
-      'userFrameworkFrontend' => array('local', 'userFrameworkFrontend'),
-      'initialized' => array('local', 'initialized'),
-
-      'dsn' => array('runtime', 'dsn'),
-      'userFramework' => array('runtime', 'userFramework'),
-      'userFrameworkBaseURL' => array('runtime', 'userFrameworkBaseURL'),
-      'userFrameworkClass' => array('runtime', 'userFrameworkClass'),
-      'userFrameworkDSN' => array('runtime', 'userFrameworkDSN'),
+      'monetaryDecimalPoint' => array('setting'),
+      'monetaryThousandSeparator' => array('setting'),
+      'moneyformat' => array('setting'),
+      'moneyvalueformat' => array('setting'),
+      'provinceLimit' => array('setting'),
+      'recaptchaOptions' => array('setting'),
+      'recaptchaPublicKey' => array('setting'),
+      'recaptchaPrivateKey' => array('setting'),
+      'secondDegRelPermissions' => array('setting'),
+      'smartGroupCacheTimeout' => array('setting'),
+      'timeInputFormat' => array('setting'),
+      'userFrameworkLogging' => array('setting'),
+      'userFrameworkUsersTableName' => array('setting'),
+      'verpSeparator' => array('setting'),
+      'wkhtmltopdfPath' => array('setting'),
+      'wpBasePage' => array('setting'),
+      'wpLoadPhp' => array('setting'),
+
+      'doNotResetCache' => array('local'),
+      'inCiviCRM' => array('local'),
+      'userFrameworkFrontend' => array('local'),
+      'initialized' => array('local'),
+
+      'dsn' => array('runtime'),
+      'userFramework' => array('runtime'),
+      'userFrameworkBaseURL' => array('runtime'),
+      'userFrameworkClass' => array('runtime'),
+      'userFrameworkDSN' => array('runtime'),
       'useFrameworkRelativeBase' => array('runtime', 'useFrameworkRelativeBase'),
-      'userFrameworkURLVar' => array('runtime', 'userFrameworkURLVar'),
-      'userPermissionClass' => array('runtime', 'userPermissionClass'),
-      'userPermissionTemp' => array('runtime', 'userPermissionTemp'),
-      'userSystem' => array('runtime', 'userSystem'),
-      'userHookClass' => array('runtime', 'userHookClass'),
-      'cleanURL' => array('runtime', 'cleanURL'),
-      'configAndLogDir' => array('runtime', 'configAndLogDir'),
-      'templateCompileDir' => array('runtime', 'templateCompileDir'),
-      'templateDir' => array('runtime', 'templateDir'),
-
-      'customFileUploadDir' => array('callback', 'CRM_Core_Config_Defaults', 'getCustomFileUploadDir', 'setPath', 'revert'),
-      'customPHPPathDir' => array('callback', 'CRM_Core_Config_Defaults', 'getCustomPhpPathDir', 'setPath', 'revert'),
-      'customTemplateDir' => array('callback', 'CRM_Core_Config_Defaults', 'getCustomTemplateDir', 'setPath', 'revert'),
-      'extensionsDir' => array('callback', 'CRM_Core_Config_Defaults', 'getExtensionsDir', 'setPath', 'revert'),
-      'imageUploadDir' => array('callback', 'CRM_Core_Config_Defaults', 'getImageUploadDir', 'setPath', 'revert'),
-      'uploadDir' => array('callback', 'CRM_Core_Config_Defaults', 'getUploadDir', 'setPath'),
-
-      'customCSSURL' => array('callback', 'CRM_Core_Config_Defaults', 'getCustomCssUrl', 'setUrl', 'revert'),
-      'extensionsURL' => array('callback', 'CRM_Core_Config_Defaults', 'getExtensionsUrl', 'setUrl', 'revert'),
-      'imageUploadURL' => array('callback', 'CRM_Core_Config_Defaults', 'getImageUploadUrl', 'setUrl', 'revert'),
-      'resourceBase' => array('callback', 'CRM_Core_Config_Defaults', 'getResourceBase', 'setUrl', 'revert'),
-      'userFrameworkResourceURL' => array('callback', 'CRM_Core_Config_Defaults', 'getUserFrameworkResourceUrl', 'setUrl', 'revert'),
+      'userFrameworkURLVar' => array('runtime'),
+      'userPermissionClass' => array('runtime'),
+      'userPermissionTemp' => array('runtime'),
+      'userSystem' => array('runtime'),
+      'userHookClass' => array('runtime'),
+      'cleanURL' => array('runtime'),
+      'configAndLogDir' => array('runtime'),
+      'templateCompileDir' => array('runtime'),
+      'templateDir' => array('runtime'),
+
+      'customFileUploadDir' => array('setting-path', NULL, '[civicrm.files]/custom/', array('mkdir', 'restrict')),
+      'customPHPPathDir' => array('setting-path'),
+      'customTemplateDir' => array('setting-path'),
+      'extensionsDir' => array('setting-path'),
+      'imageUploadDir' => array('setting-path', NULL, '[civicrm.files]/persist/contribute/', array('mkdir')),
+      'uploadDir' => array('setting-path', NULL, '[civicrm.files]/upload/', array('mkdir', 'restrict')),
+
+      'customCSSURL' => array('setting-url-abs'),
+      'extensionsURL' => array('setting-url-abs'),
+      'imageUploadURL' => array('setting-url-abs', NULL, '[civicrm.files]/persist/contribute/'),
+      'resourceBase' => array('setting-url-rel', 'userFrameworkResourceURL', '[civicrm]/.'),
+      'userFrameworkResourceURL' => array('setting-url-abs', NULL, '[civicrm]/.'),
 
       'geocodeMethod' => array('callback', 'CRM_Utils_Geocode', 'getProviderClass'),
       'defaultCurrencySymbol' => array('callback', 'CRM_Core_Config_Defaults', 'getDefaultCurrencySymbol'),
-      //'customFileUploadDir' => array('runtime', 'customFileUploadDir'),
-      //'customPHPPathDir' => array('runtime', 'customPHPPathDir'),
-      //'customTemplateDir' => array('runtime', 'customTemplateDir'),
-      //'extensionsDir' => array('runtime', 'extensionsDir'),
-      //'imageUploadDir' => array('runtime', 'imageUploadDir'),
-      //'uploadDir' => array('runtime', 'uploadDir'),
-      //
-      //'customCSSURL' => array('runtime', 'customCSSURL'),
-      //'extensionsURL' => array('runtime', 'extensionsURL'),
-      //'imageUploadURL' => array('runtime', 'imageUploadURL'),
-      //'resourceBase' => array('runtime', 'resourceBase'),
-      //'userFrameworkResourceURL' => array('runtime', 'userFrameworkResourceURL'),
-      //
-      //'geocodeMethod' => array('runtime', 'geocodeMethod'),
-      //'defaultCurrencySymbol' => array('runtime', 'defaultCurrencySymbol'),
     );
   }
 
@@ -186,17 +174,47 @@ class CRM_Core_Config_MagicMerge {
     if (!isset($this->map[$k])) {
       throw new \CRM_Core_Exception("Cannot read unrecognized property CRM_Core_Config::\${$k}.");
     }
-    list ($type, $name) = $this->map[$k];
+
+    $type = $this->map[$k][0];
+    $name = isset($this->map[$k][1]) ? $this->map[$k][1] : $k;
 
     switch ($type) {
       case 'setting':
         return $this->getSettings()->get($name);
 
       case 'setting-path':
-        return $this->getSettings()->getPath($name);
+        // Array(0 => $type, 1 => $setting, 2 => $default, 3 => $actions).
+        $value = $this->getSettings()->get($name);
+        if (empty($value) && isset($this->map[$k][2])) {
+          $value = $this->map[$k][2];
+        }
+        $value = Civi::paths()->getPath($value);
+        if ($value) {
+          $value = CRM_Utils_File::addTrailingSlash($value);
+          if (isset($this->map[$k][3]) && in_array('mkdir', $this->map[$k][3])) {
+            CRM_Utils_File::createDir($value);
+          }
+          if (isset($this->map[$k][3]) && in_array('restrict', $this->map[$k][3])) {
+            CRM_Utils_File::restrictAccess($value);
+          }
+        }
+        return $value;
+
+      case 'setting-url-abs':
+        // Array(0 => $type, 1 => $setting, 2 => $default).
+        $value = $this->getSettings()->get($name);
+        if (empty($value) && isset($this->map[$k][2])) {
+          $value = $this->map[$k][2];
+        }
+        return Civi::paths()->getUrl($value, 'absolute');
 
-      case 'setting-url':
-        return $this->getSettings()->getUrl($name, 'absolute');
+      case 'setting-url-rel':
+        // Array(0 => $type, 1 => $setting, 2 => $default).
+        $value = $this->getSettings()->get($name);
+        if (empty($value) && isset($this->map[$k][2])) {
+          $value = $this->map[$k][2];
+        }
+        return Civi::paths()->getUrl($value, 'relative');
 
       case 'runtime':
         return $this->getRuntime()->{$name};
@@ -231,14 +249,6 @@ class CRM_Core_Config_MagicMerge {
         $this->getSettings()->set($name, $v);
         return;
 
-      case 'setting-path':
-        $this->getSettings()->setPath($name, $v);
-        return;
-
-      case 'setting-url':
-        $this->getSettings()->setUrl($name, $v);
-        return;
-
       case 'runtime':
         $this->getRuntime()->{$name} = $v;
         return;
@@ -274,7 +284,8 @@ class CRM_Core_Config_MagicMerge {
     switch ($type) {
       case 'setting':
       case 'setting-path':
-      case 'setting-url':
+      case 'setting-url-abs':
+      case 'setting-url-rel':
         $this->getSettings()->revert($k);
         return;
 
index bd68943a2c6b04c2f2b4bee30c2c07be047f789b..7382811a1cc90d31fc03e65fd85c7d2d2e02f071 100644 (file)
@@ -103,10 +103,6 @@ class CRM_Core_Config_Runtime {
    */
   public $templateDir;
 
-  //public $customFileUploadDir, $customPHPPathDir, $customTemplateDir, $extensionsDir, $imageUploadDir, $resourceBase, $uploadDir;
-  //public $userFrameworkResourceURL, $customCSSURL, $extensionsURL, $imageUploadURL;
-  //public $geocodeMethod, $defaultCurrencySymbol;
-
   /**
    * @param bool $loadFromDB
    */
@@ -138,27 +134,6 @@ class CRM_Core_Config_Runtime {
     $this->setUserFramework(CIVICRM_UF);
 
     $this->templateDir = array(dirname(dirname(dirname(__DIR__))) . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR);
-    //if ($loadFromDB) {
-    //  // //$this->enableComponents = \Civi::settings()->get('enable_components');
-    //
-    //  $this->customFileUploadDir = CRM_Core_Config_Defaults::getCustomFileUploadDir();
-    //  $this->customPHPPathDir = CRM_Core_Config_Defaults::getCustomPhpPathDir();
-    //  $this->customTemplateDir = CRM_Core_Config_Defaults::getCustomTemplateDir();
-    //  $this->extensionsDir = CRM_Core_Config_Defaults::getExtensionsDir();
-    //  $this->imageUploadDir = CRM_Core_Config_Defaults::getImageUploadDir();
-    //  $this->uploadDir = CRM_Core_Config_Defaults::getUploadDir();
-    //
-    //  $this->resourceBase = CRM_Core_Config_Defaults::getResourceBase();
-    //  $this->useFrameworkRelativeBase = CRM_Core_Config_Defaults::getUserFrameworkRelativeBase();
-    //
-    //  $this->userFrameworkResourceURL = CRM_Core_Config_Defaults::getUserFrameworkResourceUrl();
-    //  $this->customCSSURL = CRM_Core_Config_Defaults::getCustomCssUrl();
-    //  $this->extensionsURL = CRM_Core_Config_Defaults::getExtensionsUrl();
-    //  $this->imageUploadURL = CRM_Core_Config_Defaults::getImageUploadUrl();
-    //
-    //  $this->geocodeMethod = CRM_Utils_Geocode::getProviderClass();
-    //  $this->defaultCurrencySymbol = CRM_Core_Config_Defaults::getDefaultCurrencySymbol();
-    //}
 
     if (CRM_Utils_System::isSSL()) {
       $this->userSystem->mapConfigToSSL();
index 7da2868bbcb5b82e1fbac3f1a38d7d448bdd037a..73d6c46b3bbe06a0a0d0eae175d29851b21c4258 100644 (file)
@@ -569,10 +569,12 @@ HTACCESS;
 
   /**
    * @param $directory
+   * @param string|NULL $basePath
+   *   The base path when evaluating relative paths. Should include trailing slash.
    *
    * @return string
    */
-  public static function absoluteDirectory($directory) {
+  public static function absoluteDirectory($directory, $basePath = NULL) {
     // check if directory is already absolute, if so return immediately
     // Note: Windows PHP accepts any mix of "/" or "\", so "C:\htdocs" or "C:/htdocs" would be a valid absolute path
     if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' && preg_match(';^[a-zA-Z]:[/\\\\];', $directory)) {
@@ -585,7 +587,7 @@ HTACCESS;
     }
 
     // make everything absolute from the baseFilePath
-    $basePath = self::baseFilePath();
+    $basePath = ($basePath === NULL) ? self::baseFilePath() : $basePath;
 
     return $basePath . $directory;
   }
index 50a687e0caa364c3c25b1eef4255244bec659e4f..c4c0be33b69b50ec45f72a3f8583312b7546fd02 100644 (file)
@@ -169,10 +169,7 @@ class CRM_Utils_Rule {
     if (empty($url)) {
       return TRUE;
     }
-    if (!preg_match('/^([a-z]+):/', $url)) {
-      // allow relative URL's (CRM-15598)
-      $url = 'http://' . $_SERVER['HTTP_HOST'] . '/' . ltrim($url, '/');
-    }
+    $url = Civi::paths()->getUrl($url, 'absolute');
     return (bool) filter_var($url, FILTER_VALIDATE_URL);
   }
 
@@ -733,7 +730,7 @@ class CRM_Utils_Rule {
    * @return bool
    */
   public static function settingPath($path) {
-    return is_dir(\CRM_Utils_File::absoluteDirectory($path));
+    return is_dir(Civi::paths()->getPath($path));
   }
 
   /**
index c88efd0268cad606a856804373f91078a2943a41..01e1fbbc4d773db0639c0ea61cd20770d09e91df 100644 (file)
--- a/Civi.php
+++ b/Civi.php
@@ -61,6 +61,19 @@ class Civi {
     return Civi\Core\Container::singleton()->get('psr_log');
   }
 
+  /**
+   * Obtain the core file/path mapper.
+   *
+   * @return \Civi\Core\Paths
+   */
+  public static function paths() {
+    // Paths must be available before container can boot.
+    if (!isset(Civi::$statics[__CLASS__]['paths'])) {
+      Civi::$statics[__CLASS__]['paths'] = new \Civi\Core\Paths();
+    }
+    return Civi::$statics[__CLASS__]['paths'];
+  }
+
   /**
    * Fetch a service from the container.
    *
@@ -81,6 +94,13 @@ class Civi {
     self::$statics = array();
   }
 
+  /**
+   * @return CRM_Core_Resources
+   */
+  public static function resources() {
+    return CRM_Core_Resources::singleton();
+  }
+
   /**
    * Obtain the domain settings.
    *
diff --git a/Civi/Core/Paths.php b/Civi/Core/Paths.php
new file mode 100644 (file)
index 0000000..a9d4d6e
--- /dev/null
@@ -0,0 +1,155 @@
+<?php
+namespace Civi\Core;
+
+/**
+ * Class Paths
+ * @package Civi\Core
+ *
+ * This paths class translates path-expressions into local file paths and
+ * URLs. Path-expressions may take a few forms:
+ *
+ *  - Paths and URLs may use a variable prefix. For example, '[civicrm.files]/upload'
+ *  - Paths and URLS may be absolute.
+ *  - Paths may be relative (base dir: [civicrm.files]).
+ *  - URLs may be relative (base dir: [cms.root]).
+ */
+class Paths {
+
+  const DEFAULT_URL = 'cms.root';
+  const DEFAULT_PATH = 'civicrm.files';
+
+  /**
+   * @var array
+   *   Array(string $name => array(url => $, path => $)).
+   */
+  private $containers = array();
+
+  protected $containerFactory = array();
+
+  public function __construct() {
+    $this
+      ->register('civicrm', function () {
+        return \CRM_Core_Config::singleton()->userSystem->getCiviSourceStorage();
+      })
+      ->register('civicrm.root', function () {
+        return \CRM_Core_Config::singleton()->userSystem->getCiviSourceStorage();
+      })
+      ->register('civicrm.files', function () {
+        return \CRM_Core_Config::singleton()->userSystem->getDefaultFileStorage();
+      })
+      ->register('cms', function () {
+        return array(
+          'path' => \CRM_Core_Config::singleton()->userSystem->cmsRootPath(),
+          'url' => \CRM_Utils_System::baseCMSURL(),
+        );
+      })
+      ->register('cms.root', function () {
+        return array(
+          'path' => \CRM_Core_Config::singleton()->userSystem->cmsRootPath(),
+          // Misleading: this *removes* the language part of the URL, producing a pristine base URL.
+          'url' => \CRM_Utils_System::languageNegotiationURL(\CRM_Utils_System::baseCMSURL(), FALSE, TRUE),
+        );
+      });
+  }
+
+  /**
+   * Register a new URL/file path mapping.
+   *
+   * @param string $name
+   *   The name of the container.
+   * @param callable $factory
+   *   Function which returns an array with keys:
+   *    - path: string.
+   *    - url: string.
+   * @return $this
+   */
+  public function register($name, $factory) {
+    $this->containerFactory[$name] = $factory;
+    return $this;
+  }
+
+  protected function getContainerAttr($name, $attr) {
+    if (!isset($this->containers[$name])) {
+      $this->containers[$name] = call_user_func($this->containerFactory[$name]);
+    }
+    if (!isset($this->containers[$name][$attr])) {
+      throw new \RuntimeException("Cannot resolve path using \"$name.$attr\"");
+    }
+    return $this->containers[$name][$attr];
+  }
+
+  /**
+   * Determine the absolute path to a file, given that the file is most likely
+   * in a given particular container.
+   *
+   * @param string $value
+   *   The file path (which is probably relative to $container).
+   *   Use "." to reference to container root.
+   *   Values may explicitly specify the a container, e.g. "[civicrm.files]/upload".
+   * @return mixed|string
+   */
+  public function getPath($value) {
+    $defaultContainer = self::DEFAULT_PATH;
+    if ($value && $value{0} == '[' && preg_match(';^\[([a-zA-Z0-9\._]+)\]/(.*);', $value, $matches)) {
+      $defaultContainer = $matches[1];
+      $value = $matches[2];
+    }
+    if (empty($value)) {
+      return FALSE;
+    }
+    if ($value === '.') {
+      $value = '';
+    }
+    return \CRM_Utils_File::absoluteDirectory($value, $this->getContainerAttr($defaultContainer, 'path'));
+  }
+
+  /**
+   * Determine the absolute URL to a file, given that the file is most likely
+   * in a given particular container.
+   *
+   * @param string $value
+   *   The file path (which is probably relative to $container).
+   *   Values may explicitly specify the a container, e.g. "[civicrm.files]/upload".
+   * @param string $preferFormat
+   *   The preferred format ('absolute', 'relative').
+   *   The result data may not meet the preference -- if the setting
+   *   refers to an external domain, then the result will be
+   *   absolute (regardless of preference).
+   * @param bool|NULL $ssl
+   *   NULL to autodetect. TRUE to force to SSL.
+   * @return mixed|string
+   */
+  public function getUrl($value, $preferFormat = 'relative', $ssl = NULL) {
+    $defaultContainer = self::DEFAULT_URL;
+    if ($value && $value{0} == '[' && preg_match(';^\[([a-zA-Z0-9\._]+)\]/(.*);', $value, $matches)) {
+      $defaultContainer = $matches[1];
+      $value = $matches[2];
+    }
+
+    if (empty($value)) {
+      return FALSE;
+    }
+    if ($value === '.') {
+      $value = '';
+    }
+    if (substr($value, 0, 4) == 'http') {
+      return $value;
+    }
+
+    $value = $this->getContainerAttr($defaultContainer, 'url') . $value;
+
+    if ($preferFormat === 'relative') {
+      $parsed = parse_url($value);
+      if (isset($_SERVER['HTTP_HOST']) && isset($parsed['host']) && $_SERVER['HTTP_HOST'] == $parsed['host']) {
+        $value = $parsed['path'];
+      }
+    }
+
+    if ($ssl || ($ssl === NULL && \CRM_Utils_System::isSSL())) {
+      $value = str_replace('http://', 'https://', $value);
+    }
+
+    return $value;
+  }
+
+}
index e0709e3b0b289256980d7b7d38730908ecb578ee..ab54ebdebc37688b1374db7e04a62d68ab825559 100644 (file)
@@ -181,42 +181,6 @@ class SettingsBag {
     return isset($all[$key]) ? $all[$key] : NULL;
   }
 
-  /**
-   * Get the value of a setting, formatted as a path.
-   *
-   * @param string $key
-   * @return string|NULL
-   *   Absolute path.
-   */
-  public function getPath($key) {
-    if (!isset($this->filteredValues[$key])) {
-      $this->filteredValues[$key] = $this->filterPath($this->get($key));
-    }
-    return $this->filteredValues[$key];
-  }
-
-  /**
-   * Get the value of a setting, formatted as a URL.
-   *
-   * @param string $key
-   * @param bool $preferFormat
-   *   The preferred format ('absolute', 'relative').
-   *   The result data may not meet the preference -- if the setting
-   *   refers to an external domain, then the result will be
-   *   absolute (regardless of preference).
-   * @param bool|NULL $ssl
-   *   NULL to autodetect. TRUE to force to SSL.
-   * @return string|NULL
-   *   URL.
-   */
-  public function getUrl($key, $preferFormat, $ssl = NULL) {
-    if (!isset($this->filteredValues[$key][$preferFormat][$ssl])) {
-      $value = $this->filterUrl($this->get($key), $preferFormat, $ssl);
-      $this->filteredValues[$key][$preferFormat][$ssl] = $value;
-    }
-    return $this->filteredValues[$key][$preferFormat][$ssl];
-  }
-
   /**
    * Determine the default value of a setting.
    *
@@ -296,30 +260,6 @@ class SettingsBag {
     return $this;
   }
 
-  /**
-   * @param string $key
-   *   The simple name of the setting.
-   * @param string $value
-   *   Absolute path.
-   * @return $this
-   */
-  public function setPath($key, $value) {
-    $this->set($key, \CRM_Utils_File::relativeDirectory($value));
-    return $this;
-  }
-
-  /**
-   * @param string $key
-   *   The simple name of the setting.
-   * @param string $value
-   *   Absolute URL.
-   * @return $this
-   */
-  public function setUrl($key, $value) {
-    $this->set($key, \CRM_Utils_System::relativeURL($value));
-    return $this;
-  }
-
   /**
    * @return \CRM_Utils_SQL_Select
    */
@@ -428,60 +368,4 @@ class SettingsBag {
     $dao->free();
   }
 
-  /**
-   * Filter a URL, the same way that it would be if it were read from settings.
-   *
-   * This converts an expression like "persist/contribute" to an absolute path
-   * like "http://example.org/sites/default/files/civicrm/persist/contribute".
-   *
-   * @param string $value
-   * @param string $preferFormat
-   *   The preferred format ('absolute', 'relative').
-   *   The result data may not meet the preference -- if the setting
-   *   refers to an external domain, then the result will be
-   *   absolute (regardless of preference).
-   * @param bool|NULL $ssl
-   *   NULL to autodetect. TRUE to force to SSL.
-   * @return mixed|string
-   */
-  public function filterUrl($value, $preferFormat, $ssl = NULL) {
-    if ($value) {
-      $value = \CRM_Utils_System::absoluteURL($value, TRUE);
-    }
-    if ($preferFormat === 'relative' && $value) {
-      $parsed = parse_url($value);
-      if (isset($_SERVER['HTTP_HOST']) && isset($parsed['host']) && $_SERVER['HTTP_HOST'] == $parsed['host']) {
-        $value = $parsed['path'];
-      }
-    }
-
-    if ($value) {
-      if ($ssl || ($ssl === NULL && \CRM_Utils_System::isSSL())) {
-        $value = str_replace('http://', 'https://', $value);
-      }
-    }
-    return $value;
-  }
-
-  /**
-   * Filter a path, the same way that it would be it it were from the settings.
-   *
-   * This converts an expression like "persist/contribute" to an absolute path
-   * like "/var/www/drupal/sites/default/files/civicrm/persist/contribute".
-   *
-   * @param string $value
-   *   The value like "persist/contribute".
-   * @return bool|string
-   *   The value like "/var/www/drupal/sites/default/files/civicrm/persist/contribute",
-   *   or FALSE if empty.
-   */
-  public function filterPath($value) {
-    if ($value) {
-      return \CRM_Utils_File::absoluteDirectory($value);
-    }
-    else {
-      return FALSE;
-    }
-  }
-
 }
index c117b8c4e840289f3e8cae888cab52b561e1b7a7..b92f59719769d18afc658bb346bfecfbc160f6b9 100644 (file)
@@ -129,44 +129,6 @@ class SettingsManagerTest extends \CiviUnitTestCase {
     $this->assertEquals('from contact', $contactSettings->get('monkeywrench'));
   }
 
-  public function testPaths() {
-    $domain = \CRM_Core_DAO::createTestObject('CRM_Core_DAO_Domain');
-    $manager = $this->createManager()->useDefaults();
-    $settings = $manager->getBagByDomain($domain->id);
-
-    $this->assertEquals('foo', $settings->get('myrelpath'));
-    $this->assertRegExp(':/.+/foo$:', $settings->getPath('myrelpath'));
-    $settings->setPath('myrelpath', 'foo/sub');
-    $this->assertEquals('foo/sub', $settings->get('myrelpath'));
-    $this->assertRegExp(':/.+/foo/sub$:', $settings->getPath('myrelpath'));
-
-    $this->assertEquals('/tmp/bar', $settings->get('myabspath'));
-    $this->assertEquals('/tmp/bar', $settings->getPath('myabspath'));
-    $settings->setPath('myabspath', '/tmp/bar/whiz');
-    $this->assertEquals('/tmp/bar/whiz', $settings->get('myabspath'));
-  }
-
-  public function testUrl() {
-    $domain = \CRM_Core_DAO::createTestObject('CRM_Core_DAO_Domain');
-    $manager = $this->createManager()->useDefaults();
-    $settings = $manager->getBagByDomain($domain->id);
-
-    $this->assertEquals('sites/foo', $settings->get('myrelurl'));
-    $this->assertRegExp(';^http.*sites/foo$;', $settings->getUrl('myrelurl', 'absolute'));
-    $this->assertRegExp(';^https:.*sites/foo$;', $settings->getUrl('myrelurl', 'absolute', TRUE));
-    //$this->assertEquals('/sites/foo', $settings->getUrl('myrelurl', 'relative'));
-    $settings->setUrl('myrelurl', 'sites/foo/sub');
-    $this->assertEquals('sites/foo/sub', $settings->get('myrelurl'));
-    $this->assertRegExp(';^http.*sites/foo/sub$;', $settings->getUrl('myrelurl', 'absolute'));
-    //$this->assertEquals('/sites/foo/sub', $settings->getUrl('myrelurl', 'relative'));
-
-    $this->assertEquals('http://example.com/bar', $settings->get('myabsurl'));
-    $this->assertEquals('http://example.com/bar', $settings->getUrl('myabsurl', 'absolute'));
-    $settings->setUrl('myabsurl', 'http://example.com/whiz');
-    $this->assertEquals('http://example.com/whiz', $settings->get('myabsurl'));
-    $this->assertEquals('http://example.com/whiz', $settings->getUrl('myabsurl', 'absolute'));
-  }
-
   /**
    * @return SettingsManager
    */