Jump to content

Robin S

Members
  • Content Count

    3,663
  • Joined

  • Last visited

  • Days Won

    212

Everything posted by Robin S

  1. So long as you don't have a custom sort setting on the parent template or parent page (i.e. the pages are just sorted according to the default which is "Manual drag-n-drop") then you can compare the "sort" value of the pages. if($sibling_1->sort > $sibling_2->sort) { // $sibling_1 is after $sibling_2 in the page tree }
  2. Thanks, but not necessary. Pretty much all my modules get created because I have a need for them in work that I get paid to do, and it doesn't cost me anything to make them available for others to use too.
  3. v0.3.0 released. Notable changes in this version: 1. Fix for page clone issue reported by @a-ok in the post above. 2. The module now hooks Pages::save() and Pages::saveField() instead of Pages::saveReady() and Pages::saveFieldReady() in order to work around this core issue. Hopefully that issue gets resolved and then the module can go back to saveReady hooks because those would be preferable, but for now something had to be done because the issue was making page changes unsaveable when "single" Page Reference fields were configured in this module.
  4. Have you tried it? There is already support for strtotime strings for datetime fields in PageFinder selectors, and your example query works just as you wrote it. 🙂 For in-memory (WireArray) selectors you have to use a timestamp in the selector.
  5. You cannot have two pages that reference each other via "single" Page Reference fields: https://github.com/processwire/processwire-issues/issues/572 Also watch out for: https://github.com/processwire/processwire-issues/issues/1092 Workarounds include the Page IDs module or use "multiple" Page Reference fields instead of "single". I feel that the convenience of getting a Page object back as the value of a single Page Reference field is sadly outweighed by the frustration of these issues. I'd rather just get an ID that a Page object can then be loaded from if/when needed.
  6. If you are regularly building new sites you might want to consider creating a "default" profile that you install as the starting point of each new project. The idea is that you start with the core "blank" profile and... install any modules that you tend to use in every site configure everything the way that you want (module configs like the one mentioned above, /site/config.php settings, etc) create any template files that are always needed (e.g. _init.php and _main.php depending on your output strategy) ...then export that profile with Site Profile Exporter. Now when you start a new project you install PW with this default profile and everything is set up how you like it to be. Different folks have different strategies but this one works well for me.
  7. https://processwire.com/blog/posts/processwire-3.0.107-core-updates/#trash-for-all
  8. I think you edited your original post and topic title, right? My answer was for the original question and isn't relevant now that you've changed it. Better to mark a topic as solved and start a new topic in such situations I think.
  9. Give this a try: // Create WireUpload instance (it doesn't matter what name you give it) $wu = new WireUpload('foo'); $file_path = '/the/path/to/your/image.jpg'; // Get the basename only $basename = basename($file_path); // Sanitise basename the same as would be done if the file was uploaded via admin $sanitised_basename = $wu->validateFilename($basename); // Look for an existing file with that sanitised basename $existing_file = $page->images->get($sanitised_basename); // Add the file if it doesn't exist if(!$existing_file) $page->images->add($file_path);
  10. There's no magic option - you have to code your own search in the way that suits you. If the number of search results is not huge and pagination is not required then you can get all the results where any field matches, and then divide off the pages that have matches in the title field, rendering those results before the others. Otherwise you might want to use an SQL query, perhaps returning just an array of page IDs which you could then slice according to the pagination number and load to a PageArray via $pages->getById().
  11. @Jozsef, I'm not sure but the failure to save changes and the fact that the order pages you are getting have a reference to the current page being saved makes me think it could be this issue: https://github.com/processwire/processwire-issues/issues/1092 But as far as I know this issue should only occur if you get the value of the order_event field (i.e. the page that is currently being saved) and I don't see that in your code. If you have "Autojoin" enabled for that field this could also cause the issue.
  12. @Marco Ro, you can use InputfieldWrapper::setMarkup() to customise the markup of inputfields. See the defaultMarkup property as a starting point for what can be customised: /** * Markup used during the render() method - customize with InputfieldWrapper::setMarkup($array) * */ static protected $defaultMarkup = array( 'list' => "<ul {attrs}>{out}</ul>", 'item' => "<li {attrs}>{out}</li>", 'item_label' => "<label class='InputfieldHeader ui-widget-header{class}' for='{for}'>{out}</label>", 'item_label_hidden' => "<label class='InputfieldHeader InputfieldHeaderHidden ui-widget-header{class}'><span>{out}</span></label>", 'item_content' => "<div class='InputfieldContent ui-widget-content{class}'>{out}</div>", 'item_error' => "<p class='InputfieldError ui-state-error'><i class='fa fa-fw fa-flash'></i><span>{out}</span></p>", 'item_description' => "<p class='description'>{out}</p>", 'item_head' => "<h2>{out}</h2>", 'item_notes' => "<p class='notes'>{out}</p>", 'item_detail' => "<p class='detail'>{out}</p>", 'item_icon' => "<i class='fa fa-fw fa-{name}'></i> ", 'item_toggle' => "<i class='toggle-icon fa fa-fw fa-angle-down' data-to='fa-angle-down fa-angle-right'></i>", // ALSO: // InputfieldAnything => array( any of the properties above to override on a per-Inputifeld basis) ); There are still some limitations (e.g. item_label is always rendered before item_content) but you can get closer to what you want like so: InputfieldWrapper::setMarkup([ 'list' => "<div {attrs}>{out}</div>", 'item' => "<div {attrs}><div class='InputfieldContent'>{out}</div></div>", 'item_label' => "<label class='{class}' for='{for}'>{out}</label>", 'item_content' => "{out}", 'item_toggle' => "", ]); $form = $modules->InputfieldForm; $f = $modules->InputfieldText; $f->name = 'greeting'; $f->label = 'Greeting'; $form->add($f); $f = $modules->InputfieldSubmit; $form->add($f); echo $form->render(); If I understand right you don't really need to change the markup to do this. The label element can never literally be inside the input element, but you can position it overlaying the input using CSS with the default markup. .Inputfield { position:relative; } .InputfieldHeader { position:absolute; left:5px; top:3px; font-size:12px; text-transform:uppercase; } input[type=text] { padding:17px 5px 5px; border:1px solid #ccc; } .InputfieldSubmit { margin-top:20px; } If you want to change the label styling when the input is focused you can use some JS to add a class to the parent .Inputfield element when its child input is focused.
  13. Oh right, I was thinking about InputfieldSelector in the context of Lister. You just set the value of the inputfield: $f = $this->wire('modules')->InputfieldSelector; $f->name = 'test_selector'; $f->label = 'Test InputfieldSelector'; $f->value = "template=news_item, colours.title=Blue";
  14. There is some example API code in a post above:
  15. Use the defaultSelector property instead of initSelector if you want the user to be able to see (and therefore change) the selector in the filters section.
  16. Is the event handler specific to the page reference field inside only new repeater items, or should it handle events of the page reference field inside all repeater items? Because if it's the latter you probably don't need to do anything in particular when a new repeater item is added - rather you can attach the handler to "document" and then use a selector to filter descendants. So supposing your handler is for the "change" event of selects named "foo" inside repeater items... $(document).on('change', 'select[name^=foo_repeater]', function() { console.log('repeater select changed'); }); Edit: and if only inside new repeater items then this seems to do the job: $(document).on('change', '.InputfieldRepeaterItemRequested select[name^=foo_repeater]', function() { console.log('new repeater item select changed'); });
  17. To do this you would set the "maxFiles" property of the inputfield dynamically. In the field settings set "0" for "Maximum files allowed" (i.e. no limit). In /site/ready.php: $wire->addHookBefore('InputfieldImage::processInput', function(HookEvent $event) { /* @var InputfieldImage $inputfield */ $inputfield = $event->object; $process = $event->wire('process'); $user = $event->wire('user'); // Only for a specific field if($inputfield->hasField != 'images') return; // Only in ProcessPageEdit if($process != 'ProcessPageEdit') return; $page = $process->getPage(); // Only for a specific template if($page->template != 'basic-page') return; $limit = null; if($user->hasRole('basic-member')) $limit = 2; if($user->hasRole('silver-member')) $limit = 5; // Only if a limit applies to this role if(!$limit) return; $inputfield->maxFiles = $limit; });
  18. For 90% of these kinds of cases I do it like @Jens Martsch - dotnetic suggested, and in the template of the page (event, news item, etc) I have: // Throw 404 if item is future-dated if(!$user->isLoggedin() && $page->getUnformatted('date_1') > time()) throw new Wire404Exception(); But on a complex site where the pages might be queried from many selectors in many different places then unpublishing is the way to go.
  19. @FlorianA, you can probably achieve what you want by using hooks. ProcessForgotPassword::renderEmailBody is hookable and includes the link URL as an argument, to which you could add the verification code (which is also an argument) as a GET parameter. Then you could hook before InputfieldText::render and if the field name is "verify" (you could do additional checks if needed) you could check $input and set the value from the GET parameter.
  20. The $page->links() method takes two optional arguments: NAME TYPE(S) DESCRIPTION selector (optional) string, bool Optional selector to filter by or boolean true for “include=all”. (default='') field (optional) string, Field Optionally limit results to specified field. (default=all applicable Textarea fields) You are passing only one argument, so this is interpreted as the first argument. Passing "$field='blog_body'" doesn't make this become the second argument - your code is saying "pass the string 'blog_body' as the first argument and also assign the string 'blog_body' to a variable named $field". If you want to specify the second argument (field) but not the first (selector) you need to pass a value for the first argument, and in this case you would pass the default value which is an empty string. So you would do this: $items = $page->links('', 'blog_body'); It would be reasonable to think that you could pass a sort as part of the selector argument, but actually this doesn't work because behind the scenes this method is ultimately just getting some page IDs and then loading data from the database using those IDs without any ORDER_BY clause. So you'll need to sort the links after you get them: $items = $page->links('', 'blog_body'); $items->sort('-blog_date');
  21. The way that I find easiest is to get the Field object that is associated with the Inputfield via hasField and check its name, because the Inputfield object's name changes inside a Repeater but the Field object's name doesn't. So something like this: $wire->addHookAfter('InputfieldDatetime::processInput', function(HookEvent $event) { $inputfield = $event->object; $field = $inputfield->hasField; if(!$field || $field->name !== 'your_field_name') return; // ... });
  22. Yes. Some possible solutions, in order from easiest to hardest or least recommended: 1. Use FieldsetGroup instead of FieldsetPage if you have ProFields. 2. Use a normal Fieldset instead of FieldsetPage, and just bite the bullet if you have to spend some time setting up the fields in multiple templates. 3. Write some custom JS for the admin that adds/removes the HTML required attribute to the adjacent field depending on the state of the checkbox. 4. Use a hook to a processInput method to do custom validation for the required fields. So if your required field is a text field then you would hook InputfieldText::processInput. Here is an example for a text field named "text_1" that is required if a field named "checkbox_1" is checked inside the same Repeater item (or FieldsetPage item): $wire->addHookBefore('InputfieldText::processInput', function(HookEvent $event) { /* @var InputfieldText $inputfield */ $inputfield = $event->object; // Get field $field = $inputfield->hasField; // Only for field "text_1" if(!$field || $field->name !== 'text_1') return; // Get page $page = $inputfield->hasPage; // Only for fields in a Repeater page if(!$page instanceof RepeaterPage) return; // Repeater suffix $r_suffix = '_repeater' . $page->id; // Get checkbox field $wrapper = $inputfield->parent; $checkbox = $wrapper->getChildByName('checkbox_1' . $r_suffix); if(!$checkbox) return; // If checkbox is checked then field is required if($checkbox->value) { $inputfield->required = 1; } else { $inputfield->required = 0; } }); If you have multiple required fields you'll need to adjust this to suit. 5. I posted some suggested changes to the core to support required-if inside Repeaters here: https://github.com/processwire/processwire-requests/issues/262 But making custom changes to the core is not recommended because they will be lost when you upgrade.
  23. If you have the Version Control module installed it might be the issue linked to below, which has been fixed in an update to the module:
  24. Hey @adrian, What do you think about the idea of adding a link to the "Restore" page somewhere within the main interface? I know it's accessible via the flyout menu but it's easy (for me at least) to overlook or forget this and there have been a few times where I've started to worry that I don't have the ability to restore a backup when an action fails due to an error or I've navigated away from the success screen which includes the restore link. Then after a moment I remember where the link is and breathe a sigh of relief. 😅
  25. Loaded pages are cached: https://github.com/processwire/processwire/blob/master/wire/core/PagesLoaderCache.php Try calling $pages->uncacheAll() between renders of the same page.
×
×
  • Create New...