<?php namespace ProcessWire;

/**
 * ProcessWire Edit Link Process
 *
 * Provides the link capability as used by the TinyMCE Inputfield
 * Works in combination with /modules/Inputfield/InputfieldTinyMCE/plugins/pwlink/editor_plugin.js
 * 
 * For more details about how Process modules work, please see: 
 * /wire/core/Process.php 
 * 
 * ProcessWire 3.x (development), Copyright 2015 by Ryan Cramer
 * https://processwire.com
 * 
 * @property string $relOptions
 * @property string $classOptions
 * @property string $targetOptions
 * @property int $urlType
 * @property int $extLinkRel
 * @property string $extLinkTarget
 *
 */

class ProcessPageEditLink extends Process implements ConfigurableModule {

	protected $page = null;

	public static function getModuleInfo() {
		return array(
			'title' => 'Page Edit Link',          
			'summary' => 'Provides a link capability as used by some Fieldtype modules (like rich text editors).', 
			'version' => 108, 
			'permanent' => true, 
			'permission' => 'page-edit',
			'icon' => 'link', 
			);
	}

	const urlTypeAbsolute = 0;
	const urlTypeRelativeBranch = 1; 
	const urlTypeRelativeAll = 2; 
	
	protected $startLabel = '';

	/**
	 * Language ID
	 * 
	 * @var int
	 * 
	 */
	protected $langID = 0;
	
	public static function getDefaultSettings() {
		return array(
			'classOptions' => "",
			'relOptions' => "nofollow",
			'targetOptions' => "_blank",
			'urlType' => self::urlTypeAbsolute,
			'extLinkRel' => '',
			'extLinkTarget' => '',
			'extLinkClass' => '', 
		);
	}

	public function __construct() {
		parent::__construct();
		foreach(self::getDefaultSettings() as $key => $value) {
			$this->set($key, $value); 
		}
	}

	public function init() {
		
		$this->startLabel = $this->_('Choose page'); 
		$this->modules->get("ProcessPageList"); 	
		$id = (int) $this->input->get->id; 
		$this->langID = (int) $this->input->get->lang;
		if($id) $this->page = $this->pages->get($id); 
		if($this->page && $this->page->id && !$this->user->hasPermission("page-view", $this->page)) throw new WireException("You don't have access to this page"); 
		if(!$this->page) $this->page = $this->wire('pages')->newNullPage();

		//$target = $this->sanitizer->name($this->input->get->target); 
		//$this->config->js('ProcessPageEditLinkTarget', $target); 

		$this->config->js('ProcessPageEditLink', array(
			'selectStartLabel' => $this->startLabel, 
			'langID' => $this->langID, 
			'pageID' => $id,
			'pageUrl' => $this->page->url,
			'pageName' => $this->page->name, 
			'rootParentUrl' => $this->page->rootParent->url,
			'slashUrls' => $this->page->template ? $this->page->template->slashUrls : 1,
			'urlType' => $this->urlType,
			'extLinkRel' => $this->extLinkRel, 
			'extLinkTarget' => $this->extLinkTarget, 
			'extLinkClass' => $this->extLinkClass, 
			));

		parent::init();
	}

	public function ___execute() {
	
		if($this->wire('input')->get('href')) {
			$currentValue = $this->wire('sanitizer')->url($this->wire('input')->get('href'), array(
				'stripQuotes' => false,
				'allowIDN' => true,
			));
		} else {
			$currentValue = '';
		}

		$form = $this->modules->get("InputfieldForm"); 
		$form->attr('id', 'ProcessPageEditLinkForm');
		//$form->description = $this->_("Enter a URL, select a page, or select a file to link:"); // Headline
		
		$this->wire('modules')->get('JqueryWireTabs'); 
		
		$fieldset = $this->wire(new InputfieldWrapper());
		$fieldset->attr('title', $this->_('Link')); 
		$fieldset->addClass('WireTab');
		$form->add($fieldset); 

		$field = $this->modules->get("InputfieldPageAutocomplete"); 
		$field->label = $this->_('Link to URL'); 
		$field->attr('id+name', 'link_page_url'); 
		$field->icon = 'external-link-square';
		$field->description = $this->_('Enter a URL, email address, anchor, or enter word(s) to find a page.'); 
		$field->labelFieldName = 'url';
		if($this->wire('modules')->isInstalled('PagePaths') && !$this->wire('languages')) {
			$field->searchFields = 'path title'; 
		} else {
			$field->searchFields = 'name title';
		}
		if($this->langID) $field->lang_id = $this->langID;
		$field->maxSelectedItems = 1; 
		$field->useList = false; 
		$field->allowAnyValue = true; 
		$field->disableChars = '/:.#';
		$field->useAndWords = true; 
		$field->findPagesSelector = 
			"has_parent!=" . $this->wire('config')->adminRootPageID . ", " . 
			"id!=" . $this->wire('config')->http404PageID;
		if($currentValue) $field->attr('value', $currentValue);
		$fieldset->add($field);
		
		if(is_array($this->wire('input')->get('anchors'))) {
			$field->columnWidth = 60; 
			$field = $this->modules->get('InputfieldSelect'); 
			$field->columnWidth = 40; 
			$field->attr('id+name', 'link_page_anchor');
			$field->label = $this->_('Select Anchor'); 
			$field->description = $this->_('Anchors found in the text you are editing.'); 
			$field->icon = 'flag';
			foreach($this->wire('input')->get('anchors') as $anchor) {
				$anchor = '#' . $this->wire('sanitizer')->text($anchor); 
				if(strlen($anchor)) $field->addOption($anchor); 
				if($currentValue && $currentValue == $anchor) $field->attr('value', $currentValue); 
			}
			$fieldset->add($field); 
		}
		
		$field = $this->modules->get('InputfieldInteger'); 
		$field->attr('id+name', 'link_page_id');
		$field->label = $this->_("Select Page");
		$field->startLabel = $this->startLabel;
		$field->collapsed = Inputfield::collapsedYes; 
		$field->icon = 'sitemap';
		$fieldset->add($field); 

		if($this->page->numChildren) {
			$field = $this->modules->get('InputfieldInteger'); 
			$field->attr('id+name', 'child_page_id');
			$field->label = $this->_("Select Child Page");
			$field->description = $this->_('This is the same as "Select Page" above, but may quicker to use if linking to children of the current page.'); 
			$field->startLabel = $this->startLabel;
			$field->collapsed = Inputfield::collapsedYes; 
			$field->icon = 'sitemap';
			$fieldset->append($field); 
		}

		$fieldset->append($this->getFilesField()); 
		
		$fieldset = $this->wire(new InputfieldWrapper());
		$fieldset->attr('title', $this->_('Attributes'));
		$fieldset->attr('id', 'link_attributes'); 
		$fieldset->addClass('WireTab'); 
		$form->append($fieldset); 
	
		$field = $this->modules->get('InputfieldText'); 
		$field->attr('id+name', 'link_title'); 
		$field->label = $this->_('Title'); 
		$field->description = $this->_('Additional text to describe link.');
		if($this->wire('input')->get('title')) {
			$field->attr('value', $this->wire('sanitizer')->text($this->wire('input')->get('title')));
		}
		$fieldset->add($field);
		
		if($this->targetOptions) {
			$field = $this->modules->get('InputfieldSelect');
			$field->attr('id+name', 'link_target');
			$field->label = $this->_('Target'); 
			$field->description = $this->_('Where this link will open.');
			$this->addSelectOptions($field, 'target', $this->targetOptions); 
			if($this->relOptions) $field->columnWidth = 50;
			$fieldset->add($field); 
			/*
		} else {
			$field = $this->modules->get("InputfieldCheckbox");
			$field->label = $this->_("Launch In New Window?");
			$field->description = $this->_("If checked, the link will open in a new window leaving the current window in place."); // Details for 'launch in new window' checkbox
			$field->attr('name', 'link_target');
			$field->attr('id', 'link_target_blank'); 
			$field->attr('value', '_blank');
			$fieldset->add($field);
			*/
		}
		
		if($this->relOptions) {
			$field = $this->modules->get('InputfieldSelect');
			$field->attr('id+name', 'link_rel');
			$field->label = $this->_('Rel'); 
			$field->description = $this->_('Relationship of link to document.');
			if($this->targetOptions) $field->columnWidth = 50; 
			$this->addSelectOptions($field, 'rel', $this->relOptions); 
			$fieldset->add($field); 
		}
		
		if($this->classOptions) {
			$field = $this->modules->get('InputfieldCheckboxes');
			$field->attr('id+name', 'link_class');
			$field->label = $this->_('Class');
			$field->description = $this->_('Additional classes that can affect the look or behavior of the link.'); 
			$field->optionColumns = 1; 
			$this->addSelectOptions($field, 'class', $this->classOptions); 
			$fieldset->add($field);
		}
		
		if($this->wire('user')->isSuperuser()) $fieldset->notes = 
			sprintf(
				$this->_('You may customize available attributes shown above in the %s module settings.'),
				"[ProcessPageEditLink](" . $this->wire('config')->urls->admin . "module/edit?name=ProcessPageEditLink)"
				);
		
		return $form->render() . "<p class='detail ui-priority-secondary'><code id='link_markup'></code></p>";
	}
	
	protected function addSelectOptions(InputfieldSelect $field, $attrName, $optionsText) {
		
		$isExisting = $this->wire('input')->get('href') != ''; 
		$existingValue = $this->wire('sanitizer')->text($this->wire('input')->get($attrName));
		$existingValue = explode(' ', $existingValue); 
		
		if($field instanceof InputfieldRadios) {
			$field->addOption('', $this->_('None')); 
		}
		
		foreach(explode("\n", $optionsText) as $value) {
			$value = trim($value);
			$isDefault = strpos($value, '+') !== false;
			if($isDefault) $value = trim($value, '+'); 
			$attr = array();
			if(($isDefault && !$isExisting) || in_array($value, $existingValue)) {
				if($field instanceof InputfieldCheckboxes) {
					$attr['checked'] = 'checked';
				} else {
					$attr['selected'] = 'selected';
				}
			}
			$value = trim($value, '+ ');
			$label = '';
			if(strpos($value, '=') !== false) {
				list($value, $label) = explode('=', $value); 
				$value = trim($value);
				$label = trim($label); 
			} else {
				if($value == '_blank') $label = $this->_('open in new window');
				if($value == 'nofollow') $label = $this->_('tell search engines not to follow');
			}
			if($label) {
				$label = "$value ($label)";
			} else {
				$label = $value; 
			}
	
			$field->addOption($value, $label, $attr);
		}
	}

	public function ___executeFiles() {
		if(!$this->page->id) throw new WireException("A page id must be specified"); 	
		$files = $this->getFiles();	
		return wireEncodeJSON($files);
	}

	protected function getFiles() {
		$files = array();
		if($this->page->id) foreach($this->page->fields as $f) {
			if(!$f->type instanceof FieldtypeFile) continue;
			foreach($this->page->get($f->name) as $file) {
				$files[$file->url] = $f->getLabel() . ': ' . $file->basename; 
			}
		}
		asort($files); 
		return $files;
	}

	protected function getFilesField() {
		$field = $this->modules->get("InputfieldSelect"); 
		$field->label = $this->_("Select File");
		$field->attr('id+name', 'link_page_file'); 
		$files = $this->getFiles();
		$field->addOption('');
		$field->addOptions($files); 
		$field->collapsed = Inputfield::collapsedYes; 
		if($this->page->id) $field->notes = $this->_('Showing files on page:') .  ' **' . $this->page->url . '**';
		$field->description = 
			$this->_('Select the file from this page that you want to link to.') . ' ' . 
			$this->_("To select a file from another page, click 'Select Page' above and choose the page you want to select a file from."); // Instruction on how to select a file from another page
		$field->icon = 'file-text-o';
		return $field;

	}
	
	public function getModuleConfigInputfields(array $data) {
	
		$data = array_merge(self::getDefaultSettings(), $data); 
		$inputfields = $this->wire(new InputfieldWrapper());
		
		$fieldset = $this->wire('modules')->get('InputfieldFieldset'); 
		$fieldset->label = $this->_('Attribute options');
		$fieldset->description = $this->_('Enter one attribute value per line. The user will be able to select these as options when adding links. To make an option selected by default (for new links), precede it with a plus "+".');
		$fieldset->icon = 'sliders';

		$f = $this->wire('modules')->get('InputfieldTextarea');
		$f->attr('name', 'classOptions');
		$f->label = 'class';
		$f->attr('value', $data['classOptions']);
		$f->columnWidth = 34; 
		$fieldset->add($f);

		$f = $this->wire('modules')->get('InputfieldTextarea');
		$f->attr('name', 'relOptions');
		$f->label = 'rel';
		$f->attr('value', $data['relOptions']);
		$f->columnWidth = 33; 
		$fieldset->add($f);

		$f = $this->wire('modules')->get('InputfieldTextarea');
		$f->attr('name', 'targetOptions');
		$f->label = 'target';
		$f->attr('value', $data['targetOptions']);
		$f->columnWidth = 33; 
		$fieldset->add($f);
		$inputfields->add($fieldset); 
		
		$fieldset = $this->wire('modules')->get('InputfieldFieldset');
		$fieldset->label = $this->_('External link attributes'); 
		$fieldset->description = $this->_('Specify the default selected attributed that will be automatically populated when an external link is detected.');
		$fieldset->description .= ' ' . $this->_('If used, the value must be one you have predefined above.'); 
		$fieldset->icon = 'external-link';
		$fieldset->collapsed = Inputfield::collapsedBlank;

		$f = $this->wire('modules')->get('InputfieldName');
		$f->attr('name', 'extLinkClass');
		$f->label = 'class';
		$f->attr('value', $data['extLinkClass']);
		$f->required = false;
		$f->columnWidth = 34;
		$fieldset->add($f);

		$f = $this->wire('modules')->get('InputfieldName');
		$f->attr('name', 'extLinkRel');
		$f->notes = $this->_('Example: Specifying **nofollow** would make external links default to be not followed by search engines.');
		$f->label = 'rel';
		$f->required = false; 
		$f->attr('value', $data['extLinkRel']);
		$f->columnWidth = 33; 
		$fieldset->add($f);

		$f = $this->wire('modules')->get('InputfieldName');
		$f->attr('name', 'extLinkTarget');
		$f->label = 'target';
		$f->notes = $this->_('Example: Specifying **_blank** would make external links default to open in a new window.'); 
		$f->attr('value', $data['extLinkTarget']);
		$f->required = false; 
		$f->columnWidth = 33; 
		$fieldset->add($f);
		$inputfields->add($fieldset); 
		
		$f = $this->wire('modules')->get('InputfieldRadios'); 
		$f->attr('name', 'urlType'); 
		$f->label = $this->_('URL type for page links'); 
		$f->addOption(self::urlTypeAbsolute, $this->_('Absolute (default)')); 
		$f->addOption(self::urlTypeRelativeBranch, $this->_('Relative URLs in the same branches only') . '*'); 
		$f->addOption(self::urlTypeRelativeAll, $this->_('Relative URLs always') . '*'); 
		$f->attr('value', isset($data['urlType']) ? $data['urlType'] : self::urlTypeAbsolute); 
		$f->notes = $this->_('*Currently experimental'); 
		$f->collapsed = Inputfield::collapsedYes;
		$inputfields->add($f); 
		
		return $inputfields; 
	}
	

}

