<?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; 
}
