Jump to content

Robin S

Members
  • Content Count

    3,840
  • Joined

  • Last visited

  • Days Won

    228

Posts posted by Robin S


  1. 2 hours ago, salmiak said:

    What's going wrong here?

    It's a bug - please open an issue on GitHub to bring it to Ryan's attention. The URL segments array has a 1-based index but this is lost to a 0-based index when the array is reversed here: https://github.com/processwire/processwire/blob/ee4c46a442f09cf81f9e1800c5fdfcd1176f538a/wire/core/WireInput.php#L576

    As a workaround for now you could do this:

    $segments = $input->urlSegments;
    $last_segment = end($segments);

     

    • Like 2

  2. I've added a note about the incompatibility with LanguageSupportPageNames to the module readme. I'm not sure how LanguageSupportPageNames works exactly or what part it plays in multi-language functionality but I can see that, like Custom Paths, it hooks Page::path and performs redirects before pages are viewed. This makes me think it may be impossible for these two modules to work together, but if anyone with multi-language experience can see a solution I'd be glad to apply any fix.

    • Like 1

  3. v0.1.4 released: this version adds the functionality that if a visitor attempts to view a page at its real path (maybe they have it bookmarked or are following an old link) they are redirected to the custom path. 

    • Like 1

  4. 4 hours ago, ngrmm said:

    But it looks like PagePathHistory is overwriting the new custom path.

    I can't reproduce that. If I set the custom path to something that it is in PagePathHistory for the page then I can successfully view the page at the custom path without being redirected by PagePathHistory.

    2020-09-17_085517.png.3924e2ad65e1a2b7414e763badfaf794.png

    2020-09-17_085550.png.b9c911713f22e80258243f6eeca222d8.png

    2020-09-17_085610.png.89d040b0edec9a847d6ed20d1530fddc.png

    If you can give me some steps to follow that will consistently reproduce the problem on a clean installation let me know and I'll try and investigate further.

     

     

     

    • Like 1

  5. On 9/9/2020 at 1:05 PM, Krlos said:

    I see that the image upload button is not available, I need to add an image, is it possible?

    You can't use the PW core image plugin because in FormBuilder there is no page to store images in.

    But I have just released v0.1.2 which lets you configure a global toolbar for all Markup CKEditor fields. If you add "Image" to the toolbar setting you can use the standard CKEditor image plugin to insert an image by URL.

    2020-09-10_151646.png.aad390116410479b61250d848e4acec7.png

    2020-09-10_152108.png.4dfabca8566957764049c22f007345d3.png

     

    • Like 2
    • Thanks 1

  6. Welcome to the PW forums @j-w!

    The thing you're asking about doesn't relate to PW as such - it's more an Apache question. As far as I know there isn't anything in the PW htaccess that would prevent you from using basic auth.

    I don't think you can use <Location> in htaccess - that has to go in your virtual hosts config file. If you want to use htaccess then this Stack Overflow answer might help (it worked for me when I tested it): https://stackoverflow.com/a/14605066/1036672

    Or see the answer to the same question for a virtual hosts example: https://stackoverflow.com/a/26980313/1036672 

    • Like 2

  7. For anyone interested in how you might do this by modifying the SQL query that's created from a PW selector, here is one way:

    In your template file or wherever:

    // A field name is supplied in a custom "empty_first" item in the $options array
    // The field name supplied here must also be used as a sort value in the selector string
    $items = $pages->find('template=news_item, sort=-date_1, limit=20', ['empty_first' => 'date_1']);

    In /site/ready.php:

    $wire->addHookAfter('PageFinder::getQuery', function(HookEvent $event) {
    	$options = $event->arguments(1);
    	// Return early if there is no "empty_first" item in the $options array
    	if(!isset($options['empty_first'])) return;
    	// Get the DatabaseQuerySelect object
    	/** @var DatabaseQuerySelect $dqs */
    	$dqs = $event->return;
    	// Prepend a custom ORDER BY
    	$dqs->orderBy("ISNULL(_sort_{$options['empty_first']}.data) DESC", true);
    	// Return the modified query
    	$event->return = $dqs;
    });

     

    • Like 3

  8. When you do a $pages->find($selector) operation your selector is translated by PW into an SQL query. It's a bit of a simplification (because there some exceptions for things like "count"), but essentially this means that you can only sort by values that correspond to columns in the database. So for example you can sort by "title" because that corresponds to a column in the database. But you can't use some logic at runtime to conditionally build up a custom value based on various properties of each page and then sort by that because there is no corresponding column for that custom value.

    So your options are...

    1. If the only way to work out the sort is to iterate over all the pages in a PageArray and you then want to paginate that sorted PageArray, you can do something like this:

    // $results is a PageArray that you have applied some custom sorting to at runtime
    
    $total = $results->count();
    $limit = 10;
    // Convert page number to be zero-based
    $page_num = $input->pageNum - 1;
    $start = $page_num * $limit;
    // Get the slice of results for the current pagination
    $results = $results->slice($start, $limit);
    $results->setStart($start)->setLimit($limit)->setTotal($total);
    
    foreach($results as $result) {
    	// Output result
    }
    echo $results->renderPager();

    But because you have to load all the results into memory in order to sort them, this strategy is not going to scale well with large numbers of results.

     

    2. If the sorting doesn't actually depend on dynamic input you can think about using an integer field in the template to store a weighting that you can sort by in a $pages->find() selector. You would use a saveReady hook to populate this sort_weighting field according to other field values in the page. You can use the API to loop over all your book pages to set a sort_weighting value initially.

    The sorting in your code didn't quite make sense to me so you'll probably need to adapt this to suit, but the general idea is this:

    // In /site/ready.php
    $pages->addHookAfter('saveReady', function(HookEvent $event) {
    	$page = $event->arguments(0);
    	if($page->template == 'book') {
    		$sort_weighting = 0;
    		if(!$page->subject) $sort_weighting = -1;
    		if(!$page->subject && !$page->author) $sort_weighting = -2;
    		$page->sort_weighting = $sort_weighting;
    	}
    });
    
    // In your template code
    $results = $pages->find("template=book, sort=sort_weighting, sort=subject, sort=cleanauthor, sort=cleantitle, limit=50");

     

    • Like 3

  9. Set the form action to the be the same URL that renders the form. Or just don't set the action for the form because the current URL is the default action. Look for the name of the submit button ("submit" is the default) in $input->post and process the form input when it is present.

    Example:

    public function ___execute() {
    	$input = $this->wire('input');
    	$modules = $this->wire('modules');
    
    	/** @var InputfieldForm $form */
    	$form = $modules->get('InputfieldForm');
    
    	/** @var InputfieldText $f */
    	$f = $modules->get('InputfieldText');
    	$f->name = 'greeting';
    	$f->label = 'Greeting';
    	$form->add($f);
    
    	/** @var InputfieldText $f */
    	$f = $modules->get('InputfieldText');
    	$f->name = 'name';
    	$f->label = 'Name';
    	$f->required = true;
    	$form->add($f);
    
    	/** @var InputfieldSubmit $f */
    	$s = $modules->get('InputfieldSubmit');
    	$form->add($s);
    
    	// Process input if form was submitted
    	if($input->post('submit')) {
    		// Core form processing (required, etc)
    		$form->processInput($input->post);
    
    		// Custom processing
    		$greeting = $form->getChildByName('greeting');
    		if($greeting->value !== 'Hello') {
    			$greeting->error('Please say "Hello"');
    		}
    
    		// Redirect (Post/Redirect/Get pattern)
    		$this->wire('session')->redirect('./');
    	}
    
    	return $form->render();
    }

     

    • Like 2

  10. On 8/24/2020 at 12:47 AM, uiui said:

    In other words: How can I get a page reference field to return unpublished references for the frontend?

    Ryan's comment in the fixed GitHub issue:

    Quote

    But I don't think they should be showing up in the formatted value at all, per your issue report, so I think that's the correct behavior. They should continue appearing in the unformatted front-end value though.

    Therefore, to include unpublished pages you must get the unformatted value of the field:

    $items = $page->getUnformatted('your_page_reference_field_name');

     

    • Like 2
    • Thanks 1

  11. Could you please show the code you are using to access your custom image fields in your template file, and explain what error/problem happens when you try to do this?

    I might be misunderstanding but it seems like you are getting hung up on the inputfield names in Page Edit when really you don't have to worry about these. You just access the custom fields from the Pageimage object using the names of the fields that you added to the template. So if you added the "title" field to the template that defines your custom image fields you access that field as $image->title, where $image is the Pageimage object from your image field.

    The relevant part of the introductory blog post is:

    Quote

    Custom fields on file/image fields can be accessed the same way that they can be from a $page. So if you've added a "caption" field to your images field (like in my screenshot above) you could output it in the same way that you previously would have output a description. For example, below I'm going to output my images and include all my custom fields with it:

    <?php foreach($page->images as $image): ?>
      <figure>
        <img src='<?=$image->url?>' alt='<?=$image->alt?>' />
        <figcaption>
          <?=$image->caption?> <br />
          <em>
            Photographer: <?=$image->photographer?>,
            License: <?=$image->license->title?>
          </em>
        </figcaption>
      </figure>
    <?php endforeach; ?>

     

    • Like 2

  12. 6 hours ago, rick said:

    UUID is a randomly generated ID

    Are you referring to the input names when you inspect them in your browser dev tools? It's normal for them to have that suffix - I expect it's to distinguish between inputs when there are multiple images in the same Page Edit form.

    But you should still be able to access the custom image fields without any suffix. It's working for me - see screenshot:

    2020-08-23_100455.thumb.png.e7df4bb7c91e3c70bf205d0680031e73.png


  13. 57 minutes ago, bookie said:

    Should I be adding a $page->of(true) line at the end of the while loop?

    It doesn't hurt to do this, but most of the time it's not necessary. When you change the output formatting state for a page, this only lasts for the current request. On the next request PW is going to automatically set the output formatting state depending on the context the code that's executing - to quote the docs, "By default, output formatting is turned on on the front-end of the site, and off on the back-end (admin) of the site."

    So probably the only case you would need to explicitly turn output formatting on after you have turned it off is if you had some code in a template file that was setting values to $page, and then later in that template file you are also outputting $page values to the front-end. But I think that would be quite rare, because most of the time when you are using the API to set page values you'll be doing that from a script separate from any template file that's outputting to the public front-end.

    • Like 1

  14. There's an open issue in the old PW repo about the inability to sort by the "sort" subfield of a Select Options field: https://github.com/ryancramerdesign/ProcessWire/issues/2049

    Looks like the "title" and "value" subfields don't work either. I've opened a new issue here: https://github.com/processwire/processwire-issues/issues/1231

    @rash, in the meantime, if sorting by title is critical to your site perhaps the best thing would be to replace your Select Options field with a Page Reference field. When you've created the Page Reference field and added it to the relevant templates you could execute some API code in the Tracy Debugger console to transfer the Select Options field values to the Page Reference field (match option title to page title).

    • Like 1

  15. 1 hour ago, rookie said:

    I just thought that this would somehow be possible in the backend as well.

    Everything is possible in ProcessWire. 😉

    For example, you could create a "max_children" integer field and add it to the template of any pages you want to limit the children of. Then use this hook in /site/ready.php:

    $wire->addHookAfter('Page::addable', function (HookEvent $event) {
    	/** @var Page $page */
    	$page = $event->object;
    	// If page has the "max_children" field, and the field isn't empty, and the number of children is at the maximum...
    	if($page->hasField('max_children') && $page->max_children !== '' && $page->numChildren >= $page->max_children) {
    		// ...then don't allow child pages to be added
    		$event->return = false;
    	}
    });

    But this is not to say it's the best solution, just that it's possible. 🙂

    • Like 2

  16. 4 hours ago, bookie said:

    $page->of(false); // output formating I don't understand but seems to be needed

    I agree that the concept of output formatting could be better documented - questions about it come up regularly in the forums. @ryan, it would be helpful if there was a page explaining output formatting in the "Getting started" documentation.

    The key thing to take onboard is that if you are going to be setting and saving values to a page, you must set output formatting to false before you do anything relating to that setting and saving. In your case, you are getting the value of a field that you will later modify...

    /* get the images object array for the Page */
    $myPageImg = $page->images;

    ...before you have turned off output formatting for $page. If you do...

    /* get the images object array for the Page */
    $page->of(false);
    $myPageImg = $page->images;

    ...then when you save $page, the changes you make to $myPageImg will be saved to the field value.

    Another thing: assigning a field value to a variable like this ($myPageImg = $page->images) when you intend to later modify that field value is probably not a good idea. You can get away with it for an images field because the value of an images field is an object, and objects in PHP are assigned by reference. But the same would not be true for any field whose value is not an object, e.g. a text field. When you assign the value of a text field to a variable you are assigning by value, meaning that changes made to the variable are not simultaneously applied to the field value. To illustrate...

    $page->of(false);
    $images = $page->images; // $images is an object assigned by reference
    $images->add('https://www.site.com/image.jpg');
    $page->save(); // Changes to $images will be saved to $page->images
    
    $page->of(false);
    $headline = $page->headline; // $headline is a string assigned by value
    $headline .= ' foo';
    $page->save(); // Changes to $headline will NOT be saved to $page->headline

    So in your case, there's really no benefit to assigning $myPageImg = $page->images near the start of your code - it just increases the chance of confusion. You'd be better to make changes to $page->images directly:

    //...
    $page->of(false);
    while($file_headers[0] == 'HTTP/1.1 200 OK'){
    	$page->images->add($file);
    	$affixnum++;
    	$file = 'https://www.site.com/pictures/' . $page->title . "_" . $affixnum . $extension;
    	$file_headers = @get_headers($file);
    	$page->save();
    	echo $file . " added. <br />";
    }
    //...

     

    • Like 3
    • Thanks 2

  17. 10 hours ago, jploch said:

    I would like to adapt this code to set icons to multiple buttons, based on the icon that is set in the template settings of the item template

    For /site/ready.php, but you can adapt for use in a module if needed:

    $wire->addHookBefore('InputfieldPageTable::render', function (HookEvent $event) {
    	/** @var InputfieldPageTable $table */
    	$table = $event->object;
    	$field = $table->hasField;
    	// Get array of table template data in the form 'label' => 'icon'
    	$table_template_ids = $field->template_id;
    	$table_templates = $event->wire('templates')->find(['id' => $table_template_ids]);
    	$table_template_data = [];
    	foreach($table_templates as $table_template) {
    		$table_template_data[$table_template->get('label|name')] = $table_template->icon;
    	}
    	
    	$event->wire()->addHookBefore('InputfieldButton::render', function (HookEvent $event) use ($table_template_data) {
    		/** @var InputfieldButton $button */
    		$button = $event->object;
    		// Return early if this is not a button we want to modify
    		if(!isset($table_template_data[$button->value])) return;
    		// Set button icon
    		$button->icon = $table_template_data[$button->value];
    	});
    });

    2020-08-16_120655.png.3345d03210b365728e76c944e96df305.png

    • Like 1

  18. I guess you could disable counting in your selector and get the count of matching pages separately via $pages->count() on the first page only. Then pass the count in the query string (or store it in $session) and use PaginatedArray::setTotal() to set the total count to the PageArray on each pagination. And if necessary you can fake the pagination entirely as shown by Ryan here:

     

    • Like 1

  19. 4 hours ago, dragan said:

    I have just tried it, and I noticed that the pagination doesn't work.

    Thanks for alerting me to this. In v0.1.0, if you didn't supply a limit as part of the selector string then all the matching pages are listed, which strictly speaking is the correct result. But it seems that Lister doesn't expect this scenario and renders pagination numbers as if a limit of 25 was supplied. Clicking these numbers doesn't do anything though because actually all the pages are already listed.

    But I think for most cases it's best to for Lister Selector to apply a default limit of 25 if none is supplied in the selector string - that way it's more in line with how Lister and Lister Pro behave. So I've changed to this in v0.1.1. And if you want a different limit you can supply it in the selector string (and you can use limit=0 if you want to see all results at once, but be aware that Lister will then render the incorrect pagination once again).

    • Like 2

  20. Lister Selector

    A Process module that uses Lister/ListerPro, but with a selector string input instead of the normal InputfieldSelector filters.

    Features

    • For power users, typing a selector string is often faster and more intuitive than fiddling with InputfieldSelector. It also lets you copy/paste selector strings that you might be using somewhere else in your code.

    • Allows the Lister rows to be sorted by multiple fields (not possible in Lister/ListerPro)

    • Allows the use of OR-groups (not possible in Lister/ListerPro)

    • If ListerPro is installed you can run ListerPro actions on the listed pages - the available actions are defined in the module config.

    • Bookmarks can be configured in the module config and accessed via the flyout menu for the module page.

    Usage

    • Type your selector string on the Selector tab. The selector is applied when the "Selector string" field is blurred, so hit Tab when you have finished typing your selector.

    • Unlike Lister/ListerPro, you can't sort results by clicking the column headings. Control the sort within the selector string instead.

    • Superusers can jump to the module config (e.g. to create a bookmark) by clicking the cog icon at the top right of the module interface.

    • The module is mostly intended for use by superusers, because in most cases site editors won't understand the ProcessWire selector string syntax. If you want another role to be able to access Lister Selector then give the role the "lister-selector" permission. Only superusers can define bookmarks because in ProcessWire module config screens are only accessible to superusers.

    Screenshots

    Process page

    pls

    Module config (when ListerPro is installed)

    pls-config

    Advanced

    If for any reason you want to create dynamic bookmark links to Lister Selector for a given selector you can do that like this:

    /** @var $pls ProcessListerSelector */
    $pls = $modules->get('ProcessListerSelector');
    $selector = "template=foo, title%=bar";
    $pls_link = $pls->getProcessPage()->url . '?bm=' . $pls->urlSafeBase64Encode($selector);
    echo "<a href='$pls_link'>My link</a>";

     

    https://github.com/Toutouwai/ProcessListerSelector
    https://modules.processwire.com/modules/process-lister-selector/

    • Like 10
    • Thanks 1

  21. 10 hours ago, rjgamer said:

    Any hint how to combine AutoTemplateStubs with custom page classes?

    So I get autocomplete for the methods of my custom page class and the properties of the stubs of the same class together?

    I've released v0.2.3 to add some support for this.

    v0.2.3

    1. Adds a setting to the module config: "Name stub classes for compatibility with custom Page classes". This option names the template stub classes using the same format as for custom Page class names. A side-effect of this is that your IDE may warn you that multiple definitions exist for your custom Page classes. This is because the stub class files generated by this module will always be distinct from any custom Page class files you create (overwriting the actual Page class files is not on the table for this module).

    If you enable this option then you'll indicate the class name for $page in your template files like this (using the home template as an example):

    /** @var HomePage $page */

    2. For new installs the default path for stub files is now "/site/templates/AutoTemplateStubs/". This path is still configurable though.

    • Like 4
×
×
  • Create New...