Subversion Repositories web.active

Rev

Rev 22 | Blame | Compare with Previous | Last modification | View Log | Download

<?php namespace ProcessWire;

/**
 * An Inputfield for handling ProcessWire "name" fields
 * 
 * @property Page|null $parentPage
 * @property Page|null $editPage
 * @property string $sanitizeMethod Default = 'pageName'
 * @property string $languageSupportLabel
 * @property bool|int $slashUrls whether a trailing slash should be shown in the URL preview (default=1)
 * 
 * optional checkbox associated with the input, for use with language support
 * @property string $checkboxName Leave blank to disable 
 * @property string $checkboxLabel
 * @property string $checkboxValue
 * @property string $checkboxSuffix
 * @property bool $checkboxChecked 
 *
 */
class InputfieldPageName extends InputfieldName implements ConfigurableModule {

  public static function getModuleInfo() {
    return array(
      'title' => 'Page Name',
      'version' => 106,
      'summary' => 'Text input validated as a ProcessWire Page name field',
      'permanent' => true, 
      );
  }

  public static $defaultReplacements = array(
    'æ' => 'ae',
    'å' => 'a', 
    'ä' => 'a',
    'ã' => 'a',
    'ß' => 'ss', 
    'ö' => 'o',  
    'ü' => 'u',  
    'Ä‘' => 'dj',  
    'ж' => 'zh',  
    'х' => 'kh',  
    'ц' => 'tc',  
    'ч' => 'ch',  
    'ш' => 'sh',  
    'щ' => 'shch',    
    'ю' => 'iu',  
    'я' => 'ia',
    ':' => '-',
    ',' => '-',
    'à' => 'a',
    'á' => 'a',
    'â' => 'a',
    'è' => 'e',
    'é' => 'e',
    'ë' => 'e',
    'ê' => 'e',
    'ě' => 'e', 
    'ì' => 'i',
    'í' => 'i',
    'ï' => 'i',
    'î' => 'i',
    'ı' => 'i',
    'İ' => 'i',
    'ğ' => 'g',
    'õ' => 'o', 
    'ò' => 'o',
    'ó' => 'o',
    'ô' => 'o',
    'ø' => 'o', 
    'ù' => 'u',
    'ú' => 'u',
    'û' => 'u',
    'ů' => 'u',
    'ñ' => 'n',
    'ç' => 'c',
    'č' => 'c',
    'ć' => 'c',
    'Ç' => 'c',
    'ď' => 'd',
    'ĺ' => 'l',
    'ľ' => 'l',
    'ń' => 'n',
    'ň' => 'n',
    'Å•' => 'r',
    'ř' => 'r',
    'š' => 's',
    'ş' => 's',
    'Åž' => 's',
    'ť' => 't',
    'ý' => 'y',
    'ž' => 'z',
    'а' => 'a',
    'б' => 'b',
    'в' => 'v',
    'г' => 'g',
    'д' => 'd',
    'е' => 'e',
    'ё' => 'e',
    'з' => 'z',
    'и' => 'i',
    'й' => 'i',
    'к' => 'k',
    'л' => 'l',
    'м' => 'm',
    'н' => 'n',
    'о' => 'o',
    'п' => 'p',
    'р' => 'r',
    'с' => 's',
    'т' => 't',
    'у' => 'u',
    'ф' => 'f',
    'ы' => 'y',
    'э' => 'e',
    'ę' => 'e',
    'ą' => 'a',
    'ś' => 's',
    'ł' => 'l',
    'ż' => 'z',
    'ź' => 'z',
  );

  /**
   * Whether or not the system has LanguageSupportPageNames module installed
   * 
   * @var bool
   * 
   */
  protected $hasLanguagePageNames = false;

  public function init() {
    parent::init();
    $this->label = $this->_('Name'); // Field label for 'Name'
    $this->icon = 'angle-double-right';
    $this->set('editPage', null); // page being edited, when available
    $this->set('parentPage', null); // parent of page being edited, when available
    $this->set('languageSupportLabel', ''); 
    $this->set('slashUrls', 1); // whether a trailing slash should be shown in the URL preview
  
    // disable autocomplete for page name with custom attribute value
    $this->attr('autocomplete', 'pw-page-name');

    // optional checkbox associated with the input, for use with language support
    $this->set('checkboxName', ''); // leave blank to disable 
    $this->set('checkboxLabel', ''); 
    $this->set('checkboxValue', ''); 
    $this->set('checkboxChecked', false); 
    $this->set('checkboxSuffix', ''); 

    if($this->wire('config')->pageNameCharset === 'UTF8') {
      // no need to indicate input limitations
      $this->set('sanitizeMethod', 'pageNameUTF8');
      $this->description = '';
    } else {
      $this->description = $this->_("Any combination of letters (a-z), numbers (0-9), dashes or underscores (no spaces)."); // Field description describing what characters are allowed
      $this->set('sanitizeMethod', 'pageName');
    }
    $this->hasLanguagePageNames = $this->wire()->modules->isInstalled('LanguageSupportPageNames'); 
    
    $this->removeClass('InputfieldNoBorder', 'wrapClass'); 
  }

  public function ___render() {
    
    $config = $this->wire()->config;
    
    $url = '';
    $out = '';
    $box = '';
    $user = $this->wire()->user;
    $languages = $this->wire()->languages;
    $sanitizer = $this->wire()->sanitizer;
    $editable = $this->attr('disabled') ? false : true; 
    $template = $this->editPage ? $this->editPage->template : null;
    $noLang = $template && $template->noLang; 
    
    if($this->parentPage) {
      if($noLang && $languages && !$user->language->isDefault()) {
        // if noLang active for page edited by non-default user, ensure we use default language url
        $languages->setDefault();
        $url = $this->parentPage->path;
        $languages->unsetDefault();
      } else {
        $url = $this->parentPage->path;
      }
      if($this->hasLanguagePageNames && $this->parentPage->id == $config->rootPageID) {
        if($user->language->isDefault()) {
          $parentName = $this->parentPage->name;
          if(!trim($url, '/')) $url = ($parentName === Pages::defaultRootName ? "" : $parentName);
        }
      }
    }

    if($noLang) $languages = false;
    if($editable && $languages && $this->hasLanguagePageNames && !$languages->editable($user->language)) $editable = false;

    if($this->languageSupportLabel) {

      if($this->checkboxName) {
        $checked = $this->checkboxChecked ? " checked='checked'" : '';
        $disabled = $editable ? '' : " disabled='disabled'";
        $name = $sanitizer->entities($this->checkboxName); 
        $value = $sanitizer->entities($this->checkboxValue); 
        $label = $sanitizer->entities($this->checkboxLabel); 
        $adminTheme = $this->wire()->adminTheme;
        $checkboxClass = $adminTheme ? $adminTheme->getClass('input-checkbox') : '';
        $box = 
          "<label class='checkbox detail'>" . 
          "<input type='checkbox' class='$checkboxClass' name='$name{$this->checkboxSuffix}' value='$value'$checked$disabled /> $label" . 
          "</label>";
      }

      $label = $sanitizer->entities($this->languageSupportLabel); 
      if(!$editable) $label = "<s>$label</s>";
      $id = $sanitizer->entities($this->attr('id')); 
      $out .= "<div class='LanguageSupport'>" . 
          "<label for='$id' class='LanguageSupportLabel detail'>$label</label>";
    }

    $value = $this->attr('value'); 
    $link = '';
    $slashUrls = (int) $this->slashUrls;

    if(strlen($value) && $this->hasLanguagePageNames) {
      $link = trim($url, '/') . "/$value" . ($slashUrls ? '/' : '');
      $link = $config->urls->root . ltrim($link, '/');
      $link = "<a href='$link'>";
    }
    
    $p = "\n<p id='{$this->id}_path' data-slashUrls='$slashUrls' class='InputfieldPageNameURL'>";
    if($link) $p .= $link;
    $p .= rtrim($url, '/') . "/<strong></strong>";
    if($link) $p .= "</a>";
    $p .= "</p>";
    $out .= $p;
    $disabled = $this->attr('disabled');
    if(!$editable) $this->attr('disabled', 'disabled');
    $out .= parent::___render();
    if(!$editable && !$disabled) $this->removeAttr('disabled'); // restore previous state
    if($this->languageSupportLabel) $out .= $box . "</div>";

    // make the replacements part of the JS config  
    $charset = $config->pageNameCharset;
    if($charset == 'UTF8') {
      $replacements = array(':' => '-', ',' => '-');
      $whitelist = $this->config->pageNameWhitelist;
    } else {
      $replacements = empty($this->replacements) ? self::$defaultReplacements : $this->replacements; 
      $whitelist = '';
    }
    $config->js($this->className(), array(
      'replacements' => $replacements,
      'charset' => $charset, 
      'whitelist' => $whitelist
    )); 

    return $out; 
  }
  
  public function ___processInput(WireInputData $input) {
    if($this->attr('disabled')) return $this;
    $languages = $this->wire()->languages;
    if($this->editPage && $this->editPage->template->noLang) $languages = false;
    if($languages && $this->hasLanguagePageNames) {
      $user = $this->wire()->user;
      if(!$languages->editable($user->language)) return $this;
      $return = parent::___processInput($input);
      $process = $this->wire()->process;
      if($process instanceof WirePageEditor && $process->getPage()->id == $this->wire()->config->rootPageID) {
        if(!strlen($this->attr('value'))) {
          $this->attr('value', Pages::defaultRootName);
        }
      }
    } else {
      $return = parent::___processInput($input);
    }
    return $return;
  }

  static public function replacementStringToArray($str) {
    $r = preg_split('/[\r\n]+/', $str); 
    $a = array();
    foreach($r as $value) {
      if(!strpos($value, '=')) continue; 
      list($k, $v) = explode('=', $value);
      $a[trim($k)] = trim($v);  
    }
    return $a; 
  }

  static public function replacementArrayToString(array $a) {
    $str = '';
    foreach($a as $k => $v) $str .= "$k=$v\n";
    return rtrim($str); 
  }

  public function getModuleConfigInputfields(array $data) {
    $modules = $this->wire()->modules;
    
    $fields = $this->wire(new InputfieldWrapper());
    if($this->wire()->config->pageNameCharset === 'UTF8') {
      $this->message($this->_('Character replacements configuration is disabled because $config->pageNameCharset is UTF8.'));
      return $fields;
    }

    $modules->addHookBefore('saveModuleConfigData', null, 'InputfieldPageName_saveModuleConfigData'); 

    $name = 'replacements';

    if(empty($data[$name])) $data[$name] = self::$defaultReplacements; 

    if(is_array($data[$name])) {
      // data already in right save format, but need it to be a string for editing
      $replacements = self::replacementArrayToString($data[$name]);

    } else {
      // data is a string so they must have just saved, but we want to save the array version instead
      $replacements = $data[$name];
      $data[$name] = self::replacementStringToArray($replacements); 
    }

    /** @var InputfieldTextarea $field */
    $field = $modules->get("InputfieldTextarea");
    $field->attr('name', $name);
    $field->attr('value', $replacements);
    $field->attr('rows', 15); 
    $field->label = $this->_('Character replacements');
    $field->description = $this->_('Enter the replacements that will occur when a user is entering characters into a page name field. Enter one replacement per line in key=value format. Meaning, on each new line, enter the character(s) you want to replace followed by an equals sign "=" and the ascii character(s) you want to replace with.'); // Character replacements description
    $field->notes = $this->_('The replacement value for each must be one or more of: a-z, 0-9, dash, underscore or period.'); // Character replacements notes
    $fields->append($field);

    return $fields;
  }

  /**
   * Get default replacements setting
   * 
   * @return array
   * @since 3.0.170
   * 
   */
  public static function getDefaultReplacements() {
    return self::$defaultReplacements;
  }
}

function InputfieldPageName_saveModuleConfigData(HookEvent $event) {
  $arguments = $event->arguments; 
  if($arguments[0] != 'InputfieldPageName') return; 
  $data = $arguments[1];
  $name = 'replacements'; 
  if(!is_array($data[$name])) $data[$name] = InputfieldPageName::replacementStringToArray($data[$name]); 
  $arguments[1] = $data; 
  $event->arguments = $arguments; 
}