<?php
/*
 +--------------------------------------------------------------------+
 | CiviCRM version 4.7                                                |
 +--------------------------------------------------------------------+
 | Copyright CiviCRM LLC (c) 2004-2015                                |
 +--------------------------------------------------------------------+
 | This file is a part of CiviCRM.                                    |
 |                                                                    |
 | CiviCRM is free software; you can copy, modify, and distribute it  |
 | under the terms of the GNU Affero General Public License           |
 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception.   |
 |                                                                    |
 | CiviCRM is distributed in the hope that it will be useful, but     |
 | WITHOUT ANY WARRANTY; without even the implied warranty of         |
 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.               |
 | See the GNU Affero General Public License for more details.        |
 |                                                                    |
 | You should have received a copy of the GNU Affero General Public   |
 | License and the CiviCRM Licensing Exception along                  |
 | with this program; if not, contact CiviCRM LLC                     |
 | at info[AT]civicrm[DOT]org. If you have questions about the        |
 | GNU Affero General Public License or the licensing of CiviCRM,     |
 | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
 +--------------------------------------------------------------------+
 */

/**
 *
 * @package CRM
 * @copyright CiviCRM LLC (c) 2004-2015
 */

/**
 * Page for configuring CKEditor options.
 *
 * Note that while this is implemented as a CRM_Core_Page, it is actually a form.
 * Because the form needs to be submitted and refreshed via javascript, it seemed like
 * Quickform and CRM_Core_Form/Controller might get in the way.
 */
class CRM_Admin_Page_CKEditorConfig extends CRM_Core_Page {

  const CONFIG_FILENAME = '[civicrm.files]/persist/crm-ckeditor-config.js';

  /**
   * Default settings if config file has not been initialized
   *
   * @var array
   */
  public $defaultSettings = array(
    'skin' => 'moono',
    'extraPlugins' => '',
  );

  /**
   * Run page.
   *
   * @return string
   */
  public function run() {
    // If the form was submitted, take appropriate action.
    if (!empty($_POST['revert'])) {
      self::deleteConfigFile();
    }
    elseif (!empty($_POST['config'])) {
      $this->save($_POST);
    }

    CRM_Core_Resources::singleton()
      ->addScriptFile('civicrm', 'bower_components/ckeditor/ckeditor.js', 0, 'html-header')
      ->addScriptFile('civicrm', 'bower_components/ckeditor/samples/toolbarconfigurator/js/fulltoolbareditor.js', 1)
      ->addScriptFile('civicrm', 'bower_components/ckeditor/samples/toolbarconfigurator/js/abstracttoolbarmodifier.js', 2)
      ->addScriptFile('civicrm', 'bower_components/ckeditor/samples/toolbarconfigurator/js/toolbarmodifier.js', 3)
      ->addScriptFile('civicrm', 'js/wysiwyg/admin.ckeditor-configurator.js', 10)
      ->addStyleFile('civicrm', 'bower_components/ckeditor/samples/toolbarconfigurator/css/fontello.css')
      ->addStyleFile('civicrm', 'bower_components/ckeditor/samples/css/samples.css')
      ->addVars('ckConfig', array(
        'plugins' => array_values($this->getCKPlugins()),
      ));

    $this->assign('skins', $this->getCKSkins());
    $this->assign('skin', $this->getConfigSetting('skin'));
    $this->assign('extraPlugins', $this->getConfigSetting('extraPlugins'));
    $this->assign('configUrl', self::getConfigUrl());
    $this->assign('revertConfirm', htmlspecialchars(ts('Are you sure you want to revert all changes?', array('escape' => 'js'))));

    CRM_Utils_System::appendBreadCrumb(array(array(
      'url' => CRM_Utils_System::url('civicrm/admin/setting/preferences/display', 'reset=1'),
      'title' => ts('Display Preferences'),
    )));

    return parent::run();
  }

  /**
   * Generate the config js file based on posted data.
   *
   * @param array $params
   */
  public function save($params) {
    $config = "/**\n"
      . " * CKEditor config file auto-generated by CiviCRM.\n"
      . " *\n"
      . " * Note: This file will be overwritten if settings are modified at:\n"
      . " * @link " . CRM_Utils_System::url(CRM_Utils_System::currentPath(), NULL, TRUE, NULL, FALSE) . "\n"
      . " */\n\n"
      // Standardize line-endings
      . preg_replace('~\R~u', "\n", $params['config']);

    // Use defaultSettings as a whitelist so we don't just insert any old junk into the file
    foreach ($this->defaultSettings as $key => $default) {
      if (isset($params[$key]) && strlen($params[$key])) {
        $pos = strrpos($config, '};');
        $setting = "\n\tconfig.$key = '{$params[$key]}';\n";
        $config = substr_replace($config, $setting, $pos, 0);
      }
    }
    self::saveConfigFile($config);
    if (!empty($params['save'])) {
      CRM_Core_Session::setStatus(ts("You may need to clear your browser's cache to see the changes in CiviCRM."), ts('CKEditor Saved'), 'success');
    }
  }

  /**
   * Get available CKEditor plugin list.
   *
   * @return array
   */
  private function getCKPlugins() {
    $plugins = array();
    $pluginDir = Civi::paths()->getPath('[civicrm.root]/bower_components/ckeditor/plugins');

    foreach (glob($pluginDir . '/*', GLOB_ONLYDIR) as $dir) {
      $dir = rtrim(str_replace('\\', '/', $dir), '/');
      $name = substr($dir, strrpos($dir, '/') + 1);
      $dir = CRM_Utils_file::addTrailingSlash($dir, '/');
      if (is_file($dir . 'plugin.js')) {
        $plugins[$name] = array(
          'id' => $name,
          'text' => ucfirst($name),
          'icon' => NULL,
        );
        if (is_dir($dir . "icons")) {
          if (is_file($dir . "icons/$name.png")) {
            $plugins[$name]['icon'] = "bower_components/ckeditor/plugins/$name/icons/$name.png";
          }
          elseif (glob($dir . "icons/*.png")) {
            $icon = CRM_Utils_Array::first(glob($dir . "icons/*.png"));
            $icon = rtrim(str_replace('\\', '/', $icon), '/');
            $plugins[$name]['icon'] = "bower_components/ckeditor/plugins/$name/icons/" . substr($icon, strrpos($icon, '/') + 1);
          }
        }
      }
    }

    return $plugins;
  }

  /**
   * Get available CKEditor skins.
   *
   * @return array
   */
  private function getCKSkins() {
    $skins = array();
    $skinDir = Civi::paths()->getPath('[civicrm.root]/bower_components/ckeditor/skins');
    foreach (glob($skinDir . '/*', GLOB_ONLYDIR) as $dir) {
      $dir = rtrim(str_replace('\\', '/', $dir), '/');
      $skins[] = substr($dir, strrpos($dir, '/') + 1);
    }
    return $skins;
  }

  /**
   * @param $setting
   * @return string
   */
  private function getConfigSetting($setting) {
    $value = CRM_Utils_Array::value($setting, $this->defaultSettings, '');
    $file = self::getConfigFile();
    if ($file) {
      $contents = file_get_contents($file);
      $matches = array();
      preg_match("/\sconfig\.$setting\s?=\s?'([^']*)'/", $contents, $matches);
      if ($matches) {
        $value = $matches[1];
      }
    }
    return $value;
  }

  /**
   * @return null|string
   */
  public static function getConfigUrl() {
    if (self::getConfigFile()) {
      return Civi::paths()->getUrl(self::CONFIG_FILENAME, 'absolute');
    }
    return NULL;
  }

  /**
   * @param bool $checkIfFileExists
   *   If false, this fn will return fileName even if it doesn't exist
   *
   * @return null|string
   */
  public static function getConfigFile($checkIfFileExists = TRUE) {
    $fileName = Civi::paths()->getPath(self::CONFIG_FILENAME);
    return !$checkIfFileExists || is_file($fileName) ? $fileName : NULL;
  }

  /**
   * @param string $contents
   */
  public static function saveConfigFile($contents) {
    $file = self::getConfigFile(FALSE);
    file_put_contents($file, $contents);
  }

  /**
   * Delete config file.
   */
  public static function deleteConfigFile() {
    $file = self::getConfigFile();
    if ($file) {
      unlink($file);
    }
  }

}
