Jump to content

Recommended Posts

Posted

Is it possible to hook into ProcessPageEditLink in order to add a custom Page Reference field (for a Repeater on the same page!) for the user to link? This is specifically for annotations or footnotes so my thinking is the link would return a `data-annotation-id` of the repeater ID and row or similar.

What are your thoughts?

Posted

I had written something like the following before, adding to `wire/modules/Process/ProcessPageEditLink/ProcessPageEditLink.module`, but I'd prefer to use a hook and avoid the custom JS if possible?

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

protected function getAnnotations() {
	$annotations = array();
	if($this->page->id) foreach($this->page->speech_archive_detail_annotations as $annotation) {
		$annotation_clean = '#annotation--' . $this->wire('sanitizer')->pageName($annotation->id);
		$annotations[$annotation_clean] = $annotation->speech_archive_detail_annotations_title; 
	}
	asort($annotations); 
	return $annotations;
}

protected function getAnnotationsField() {
	$field = $this->modules->get("InputfieldSelect"); 
	$field->label = $this->_("Select Annotation");
	$field->attr('id+name', 'link_page_annotation'); 
	$annotations = $this->getAnnotations();
	$field->addOption('');
	$field->addOptions($annotations); 
	$field->collapsed = Inputfield::collapsedYes; 
	if($this->page->id) $field->notes = $this->_('Showing annotations on page:') .  ' **' . $this->page->url . '**';
	$field->description = 
		$this->_('Select the annotation from this page that you want to link to.');
	$field->icon = 'comment-o';
	return $field;

}

 

Posted

I think I've kinda got it, based on this post below, however I need to get the repeater elements on the page I'm editing and I can't seem to get the page?

This is very rough and not tidied up at all, yet.

$wire->addHookAfter('ProcessPageEditLink::execute', function(HookEvent $event) {

	$page = $event->object;

	$form = $this->modules->get("InputfieldForm");
    $form->attr('id', 'ProcessPageEditLinkForm');

    $field = wire('modules')->get('InputfieldSelect');
    $field->label = $this->_("Select File");
    $field->attr('id+name', 'link_page_file');
    $field->icon = 'file-pdf-o';

	var_dump($field->hasPage) ;

	$annotations = array();
	foreach($page->textAnnotations as $annotation) {
		$annotation_clean = '#annotation--' . wire('sanitizer')->pageName($annotation->id);
		$annotations[$annotation_clean] = $annotation->text;
	}
	asort($annotations);
	var_dump($page);

	$options = $annotations;

    $field->addOption(''); // Allow unselect
    $field->addOptions($options);
    $form->add($field);

    // We need something like this to get the nice JS generated preview
    $markup = '<span id="link_page_url_input"></span>';
    $form->set('appendMarkup', $markup);
    $event->return =  $form->render() . "<p class='detail ui-priority-secondary'><code id='link_markup'></code></p>";

});

 

Posted

 

Think I've got it.
 

Only thing would be the ability to add a data attribute to the link AND append to the other inputfields rather than replace them?

$wire->addHookAfter('ProcessPageEditLink::execute', function(HookEvent $event) {

	$page = wire('pages')->get(wire('input')->get('id'));

	$form = $this->modules->get('InputfieldForm');
    $form->attr('id', 'ProcessPageEditLinkForm');

    $field = wire('modules')->get('InputfieldSelect');
    $field->label = $this->_('Select annotation');
    $field->attr('id+name', 'link_page_file');
    $field->icon = 'link';

	$annotations = array();
	foreach($page->textAnnotations as $annotation) {
		$annotationClean = '#annotation--' . wire('sanitizer')->pageName($annotation->id);
		$annotations[$annotationClean] = $annotation->text;
	}
	asort($annotations);

    $field->addOption(''); // Allow unselect
    $field->addOptions($annotations);
    $form->add($field);

    // JS generated preview below selected link in popup
    $markup = '<span id="link_page_url_input"></span>';
    $form->set('appendMarkup', $markup);
    $event->return =  $form->render() . "<p class='detail ui-priority-secondary'><code id='link_markup'></code></p>";

});

 

Posted

I also think I need to add the `$field` to the `$form` that already exists, rather than creating a new one.

Any thoughts?

Posted

Here's a quick and dirty demo how to add your own anchors to the builtin anchor feature of ProcessPageEditLink. No form manipulation shenanigans and no JS either. ?

wire()->addHookBefore("ProcessPageEditLink::execute", function(HookEvent $event) {
	$page = wire('pages')->get((int)wire('input')->get('id'));
	// Limit to a certain template type:
	if($page->template->name !== 'home') return;
	
	$anchors = is_array(wire('input')->get->anchors) ? wire('input')->get->anchors : [];

	// This is the spot to add your own annotation anchors:
	$myAnchors = [
		"some_anchor",
		"other_anchor",
		"third_anchor"
	];
	
	$anchors = array_merge($anchors, $myAnchors);
	
	wire('input')->get->anchors = $anchors;
});

 

  • Like 1
Posted
21 hours ago, BitPoet said:

Here's a quick and dirty demo how to add your own anchors to the builtin anchor feature of ProcessPageEditLink. No form manipulation shenanigans and no JS either. ?

Thanks for the help!

What’s the built in anchor feature? I’ve added your code to test with but the options don’t appear when adding a link? Do I have to enable something else?

Posted
36 minutes ago, a-ok said:

What’s the built in anchor feature? I’ve added your code to test with but the options don’t appear when adding a link? Do I have to enable something else?

The builtin feature means that PW parses the HTML for named anchors, and if found, adds a "Select Anchor" select box at the top right. Did you remove the if($page->template...) line in my example? This was just meant for demonstration of limiting the functionality to a certain template.

Posted
3 hours ago, BitPoet said:

The builtin feature means that PW parses the HTML for named anchors, and if found, adds a "Select Anchor" select box at the top right. Did you remove the if($page->template...) line in my example? This was just meant for demonstration of limiting the functionality to a certain template.

Thanks again but I'm slightly confused. There's no 'Select Anchor' select box? What HTML does it parse? The template check is removed, yep.

 

Screenshot 2020-03-08 at 16.31.36.jpg

Posted

It looks at the HTML in the CKEditor field you're triggering PWLink from, but that shouldn't matter anyway. This is how it should look:

anchor.thumb.png.474ea2d5085b94470bf1de7ac36ce575.png

The anchor select only appears if there are anchors in the array. What does bd($anchors) say before it's assigned to the input var?

Posted

The annotations are generated from the repeater field `$page->textAnnotations` and then the idea is that the user would be able to select a repeater row (nothing more than a sanitised anchor of the title/ID field for each repeater row. So with my original code I was attempting to create a new `InputfieldSelect` field within `ProcessPageEditLink` with options for each `$page->textAnnotations` repeater row and it would output the link href as, for example, `#annotation-example-row-title` or `#annotation-1827`.

I think we're on the same page BUT `$anchors` returns `array(3) { [0]=> string(11) "some_anchor" [1]=> string(12) "other_anchor" [2]=> string(12) "third_anchor" }`

Posted

Looking at `ProcessPageEditLink.module` I feel like the code you've presented is correct; we're pushing these options and setting them to `wire('input')->get->anchors` so it should show... but it's not. Hmm.

UPDATE

I was using `addHookAfter` instead of `addHookBefore` ?

Posted

Thanks so much for the help, @BitPoet. One final thing. Is it possible to keep the value of the select option as it is but change the label? My annotations list read as `#annotation-1020`, for example, which is super useful for some JS stuff I'll be doing with it, but ideally, for the user, it would be good to have these options a bit more descriptive. Any idea?

I know you can add a label within the `InputfieldSelect::addOption()` method but obviously as I'm updating the anchors before it runs this I can't amend it.

https://github.com/processwire/processwire/blob/51629cdd5f381d3881133baf83e1bd2d9306f867/wire/modules/Process/ProcessPageEditLink/ProcessPageEditLink.module#L203 I guess having the option on this line to pass in a key/value (key would be the label) into the anchors array (otherwise label = null).

Posted
21 hours ago, a-ok said:

One final thing. Is it possible to keep the value of the select option as it is but change the label?

Not with the built-in anchor select. If you don't mind a bit of regex ugliness:

wire()->addHookAfter("ProcessPageEditLink::execute", function(HookEvent $event) {
	$page = wire('pages')->get((int)wire('input')->get('id'));
	if(wire('input')->get('href')) {
		$currentValue = wire('sanitizer')->url(wire('input')->get('href'), array(
			'stripQuotes' => false,
			'allowIDN' => true,
		));
	} else {
		$currentValue = '';
	}
	
	$inp = wire('modules')->get('InputfieldSelect');
	$inp->attr('id+name', 'select_anchor');
	$inp->label = wire()->_('Select Repeater Anchor');
	$inp->icon = 'anchor';
	$inp->collapsed = $currentValue ? Inputfield::collapsedNo : Inputfield::collapsedYes;
	// Instead of addOptions here, fill your select however you want
	$inp->addOptions([
		"" => "",
		"#some_anchor" => "some_anchor_text",
		"#other_anchor" => "other_anchor_text",
		"#third_anchor" => "third_anchor_text"
	]);
	if($currentValue) $inp->attr('value', $currentValue);
	
	$wrap = new InputfieldWrapper();
	$wrap->append($inp);
	
	// It's a bit ugly, but to get our InputfieldSelect to render correctly, we need
	// it rendered by an InputfieldWrapper. We do that, then strip out the wrapping
	// parts afterwards (i.e. everything before the first <li> and after the last </li>).
	$html = $wrap->render();
	$html = preg_replace('~^.*?(?=<li\b)~is', '', $html);
	$html = preg_replace('~</li>.*?$~is', '', $html);
	
	$js = '<script>
		$("#select_anchor").bind("change", function(evt) { $("#link_page_url_input").val($(evt.target).val()).change(); });
	</script>
	';
	
	$html .= $js . "</li>";
	
	$event->return = preg_replace("~(?=<li[^>]+id='wrap_link_page_file')~", $html, $event->return);
});

 

  • Like 2
Posted

Building on @BitPoet's code, here's an alternative way you could add labels to the select options:

$wire->addHookBefore('ProcessPageEditLink::execute', function(HookEvent $event) {

	$input = $event->wire('input');
	$page = $event->wire('pages')->get($input->get->id);
	// Do some check on $page to return early when not applicable

	$anchors = $input->get->anchors ?: [];
	$my_anchors = [
		'some_anchor' => 'Some anchor',
		'other_anchor' => 'Other anchor',
		'third_anchor' => 'Third anchor',
	];
	$anchors = array_merge($anchors, array_keys($my_anchors));
	$input->get->anchors = $anchors;

	$event->wire()->addHookBefore('InputfieldSelect::render', function(HookEvent $event) use ($my_anchors) {
		$inputfield = $event->object;
		if($inputfield->name !== 'link_page_anchor') return;
		$options = $inputfield->options;
		$inputfield->options = [];
		foreach($options as $option) {
			$anchor_name = ltrim($option, '#');
			if(isset($my_anchors[$anchor_name])) {;
				$inputfield->addOption($option, $my_anchors[$anchor_name]);
			} else {
				$inputfield->addOption($option);
			}
		}
	});

});

 

  • Like 4
Posted

Thanks very much for the help everyone. Hopefully this'll help others too so much appreciated.

This is what I ended up with (went with @Robin S's solution in the end). MUCH appreciated help @BitPoet ?

// Add annotations as anchor options within text
$wire->addHookBefore('ProcessPageEditLink::execute', function(HookEvent $event) {

	$page = wire('pages')->get(wire('input')->get('id'));
	if ($page->template->name !== 'projectsSingle') return;

	$input = $event->wire('input');
	$anchors = $input->get->anchors ? : [];

	$annotations = array();
	foreach ($page->textAnnotations as $key => $annotation) {
		$annotationClean = 'annotation-' . wire('sanitizer')->pageName($annotation->id);
		$annotations[$annotationClean] = $annotation->text;
	}
	$anchors = array_merge($anchors, array_keys($annotations));

	$input->get->anchors = $anchors;

	//
	$event->wire()->addHookBefore('InputfieldSelect::render', function(HookEvent $event) use ($annotations) {

		$inputfield = $event->object;
		if ($inputfield->name !== 'link_page_anchor') return;

		$options = $inputfield->options;
		$inputfield->options = [];

		foreach ($options as $option) {
			$anchorName = ltrim($option, '#');
			if (isset($annotations[$anchorName])) {;
				$inputfield->addOption($option, $annotations[$anchorName]);
			} else {
				$inputfield->addOption($option);
			}
		}
	});

});

 

  • Like 1

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...