Jump to content

Robin S

Members
  • Posts

    5,007
  • Joined

  • Days Won

    333

Everything posted by Robin S

  1. It depends who needs to see these test emails. If only you, the developer, needs to see them you can use Tracy Debugger's Mail Interceptor panel and then (I think) you won't need an email server.
  2. I did a bit of reading about this last night and reports are that in Adminer v4.6.2 and below there was a security hole that hackers exploited to attack WordPress and Magento sites - not sure if other platforms were vulnerable but given that Adminer is a general DB tool I assume so. But this hole was patched and consensus seems to be that recent versions of Adminer are safe. So I wonder if the OP's host has an overly broad rule that is simply flagging Adminer generally rather than detecting specific versions. Having said that, I checked the Tracy Debugger history and the first bundled Adminer version was v4.6.3 so it was kind of a near miss. Maybe it is better that the Adminer panel is separated into its own module so that having it present on a server is more of a conscious choice. I think it's an awesome addition to Tracy so I'll be installing it for sure.
  3. Very interesting, although the visualisation doesn't quite demonstrate the fact that the bundled site profiles make up more than half of the total ZIP filesize when you download PW from the repo. I believe this has some negatives so I'm keen to start a discussion about moving most of the profiles to separate repos. For anyone interested, please participate here: https://github.com/processwire/processwire-requests/issues/415
  4. You need to get the unformatted value of the Page Reference field in order to have unpublished pages appear: https://github.com/processwire/processwire-issues/issues/1159 $value = $page->getUnformatted('your_field_name');
  5. When you say "uses", do you mean your module extends ProcessPageLister? If not that would probably be the simplest approach because then bookmarks will work out-of-the-box. Here's a bare-bones example: <?php namespace ProcessWire; class ProcessListerExtend extends ProcessPageLister { /** * Module information */ public static function getModuleinfo() { return array( 'title' => 'Lister Extend', 'summary' => 'An example of a module that extends ProcessPageLister', 'version' => '0.1.0', 'author' => 'Robin S', 'icon' => 'search-plus', 'requires' => 'ProcessWire>=3.0.0, PHP>=5.4.0', 'page' => array( 'name' => 'lister-extend', 'title' => 'Lister Extend', 'parent' => 'setup', ), 'useNavJSON' => true, ); } /** * Init * * Load the CSS and JS assets for ProcessPageLister rather than needing to duplicate them in this module's folder */ public function init() { $this->wire()->modules->loadModuleFileAssets('ProcessPageLister'); parent::init(); } /** * Install * * Call Process::install() to automatically create the admin page for the process on install * This is needed here because ProcessPageLister overrides with an empty install() method */ public function ___install() { Process::___install(); } /** * Uninstall * * Call Process::uninstall to automatically removes the admin page for the process on uninstall * This is needed here because ProcessPageLister overrides with an empty uninstall() method */ public function ___uninstall() { Process::___uninstall(); } } Wherever you need to modify a method of ProcessPageLister you can create a corresponding method in your module to override it, and you can make use of parent::methodName() within your method if you just want to add something to the ProcessPageLister method.
  6. Generally speaking this is already possible. The syntax is: !your_field_name^=foo Paths are a special case though because these are only searchable when the PagePaths module is installed and that module doesn't support this kind of negation: But I don't think this actually matters because there is a dedicated selector option for when you want to find pages that do or do not have a particular ancestor, and that is "has_parent": https://processwire.com/docs/selectors/#finding2 So rather than needing anything like... ...you can instead do... has_parent!=/foo/|/bar/|/baz/
  7. You have to first get the "itinerary" array as a variable distinct from the $session->itinerary overloaded property, modify it as needed, and then set it back to $session->itinerary. if(isset($_GET[$key])){ $itinerary = $session->itinerary; if(is_array($itinerary)) { // destroy a single element of an array unset($itinerary[$key]); $session->itinerary = $itinerary; $session->redirect($page->url); } }
  8. The module readme says:
  9. @neonwired, as explained a couple of posts above yours, you need to be using PW 3.0.166 or newer (currently on the PW dev branch) to install or upgrade to v0.3.2 of Connect Page Fields. If you don't want to use the PW dev version then you can manually download Connect Page Fields v0.3.1 by browsing back in the commit history: https://github.com/Toutouwai/ConnectPageFields/tree/99f5e06b959d100a57ab130fa6e6f57ffea19dc9 After you download make sure you rename the module folder from "ConnectPageFields-99f5e06b959d100a57ab130fa6e6f57ffea19dc9" to "ConnectPageFields" before you copy it to /site/modules/ and install it (see "Installing manually").
  10. @hollyvalero, after these posts were written a powerful truncate feature was added to the core as a sanitizer method. https://processwire.com/api/ref/sanitizer/truncate/ https://processwire.com/blog/posts/processwire-3.0.101-core-updates/
  11. FieldtypeDecimal uses InputfieldFloat as its inputfield, and it's the renderValue() method that determines what is shown when a field is not editable. So you could do this: $wire->addHookAfter('InputfieldFloat::renderValue', function(HookEvent $event) { /** @var InputfieldFloat $inputfield */ $inputfield = $event->object; $field = $inputfield->hasField; if($field && $field->name === 'your_field_name') { $event->return = '$' . number_format((float) $inputfield->value, 2); } });
  12. Welcome to the PW forums @virgomania! I don't believe that's possible. The PW core philosophy is to have images and files connected to the pages they are uploaded to and not in one central repository. In the past Ryan (lead developer of PW) has discussed why he favours this approach. Here's one old topic... ...and maybe others will chime in with links to more background on this subject. So a few suggestions: 1. Consider if you absolutely must have a central image/file repository. Maybe you could try the PW-way and you might find it works pretty well for 99% of cases. I think a lot of people new to PW first thought they needed a media library and then gradually learned that they didn't. ? 2. If your main use case is linking/inserting files or images into rich text (CKEditor) fields then be aware that you can link/insert images from any page, not only the current page. Some people even create one or more dedicated "library" pages which hold a lot of files/images that they use on other pages. The Media Library module can help with this. 3. If your main use case is dedicated fields which will hold files/images then have a look at the commercial Media Manager module. I think this module also allows you to link/insert the media in CKEditor fields. 4. You mentioned the Select Images inputfield (used together with Dynamic Options I assume). Because you supply the selectable images via a hook you can make images from multiple different pages selectable. Here's an example hook that would make images selectable from any image field on any page using the basic_page or home template: $wire->addHookAfter('FieldtypeDynamicOptions::getSelectableOptions', function(HookEvent $event) { // The Dynamic Options field $field = $event->arguments(1); // For a field named "select_images" if($field->name === 'select_images') { $options = []; // For each page that uses the basic_page or home template foreach($event->wire()->pages->find("template=basic_page|home") as $p) { // Get the FieldtypeImage fields that are in the page's template $image_fields = $p->fields->find("type=FieldtypeImage"); // For each image field foreach($image_fields as $field) { // Get the images stored on the page in that field $images = $p->getUnformatted($field->name); // For each image foreach($images as $image) { // Add a selectable option $options[$image->url] = "{$image->basename}<br>{$image->filesizeStr}"; } } } $event->return = $options; } }); But be aware that this won't scale infinitely. At some point the number of selectable images might be unmanageable and you may strike performance issues in the page editor. I think Media Manager might be more scalable in this respect.
  13. @rsi you can use $pages->sort() to set the sort value of the new page to zero after it is added, and that will make it appear at the top of its siblings but still be manually sortable. In /site/ready.php: $pages->addHookAfter('added', function(HookEvent $event) { $pages = $event->object; // The page that is being added $page = $event->arguments(0); // If the page passes some kind of test if($page->template == 'news_item') { // Set the sort value for the new page to zero (sibling sort will be automatically adjusted) $pages->sort($page, 0); } });
  14. If you just want to do this from time-to-time you can add a filter row like this: Or if you want the default limit to always be 50 (or whatever limit you want) then you can add a hook in /site/ready.php: $wire->addHookBefore('ProcessPageLister::execute', function(HookEvent $event) { /** @var ProcessPageLister $lister */ $lister = $event->object; // Only for the "Find" lister if($event->wire()->page->name === 'lister') { $lister->defaultLimit = 50; } });
  15. In most cases I expect people to use the Select Image inputfield in conjunction with the Dynamic Options fieldtype. And regarding the value of a Dynamic Options field: The Select Images input type is a bit of a special case because it can be a "multiple" or a "single" input type: And beyond that there is a config option for Dynamic Options that can be used if the values that are saved are paths or URLs to Pagefiles or Pageimages, and this could be a good option when you're using Select Images as the inputfield: When that option is enabled you could use the Pageimage sizing methods on the field value. Note that for multiple images the value is an array of Pageimage objects and not a Pageimages object. That's because these modules are intended to be flexible enough to allow selection of images from more than one PW page but a Pageimages object only supports Pageimage objects associated with a single page (see the class constructor). The readme for Select Images says: So you can have the value for each option (thumbnail) be any string you like and output that in a template file or use it in a conditional to affect the page markup in some way. But I think my most common use case will be as an "image reference" field to select URLs to Pageimages and have the formatted value be a Pageimage or array of Pageimage objects. People have suggested different use cases for an image reference field in the GitHub request linked to at the start of the readme: https://github.com/processwire/processwire-requests/issues/207 Personally I have used image references in these scenarios: To allow an editor to select a social media "share" image from among all the uploads to Images fields on a page. In cases where an editor may only select from a collection of existing images and is not allowed to upload new images. Similar to the previous example, but where the editor needs to choose images that have been prepared in advance to fit in spaces with particular aspect ratios. So for a landscape space they can only select landscape images, square space can only select square images, etc. The allowed options for a Dynamic Options field are determined at runtime according to the FieldtypeDynamicOptions::getSelectableOptions hook you are using. These allowed options are used to validate any stored value when it is accessed. So if an image that was referenced is deleted then it will no longer be in the value you get from $page->your_dynamic_options_field
  16. v0.1.3 released. This version adds options to set a limit to the number of items that may be selected. When the limit is reached the supported core "multiple" inputfields become disabled. It adds an option for the formatted value to be Pagefile/Pageimage object(s) where that would be relevant. And it integrates with the newly released Select Images module to create a kind of "image reference" field. See the updated readme for more detail.
  17. Select Images An inputfield that allows the visual selection and sorting of images, intended for use with the FieldtypeDynamicOptions module. Together these modules can be used to create a kind of "image reference" field. Integration with FieldtypeDynamicOptions InputfieldSelectImages was developed to be used together with FieldtypeDynamicOptions (v0.1.3 or newer): Create a Dynamic Options field. Choose "Select Images" as the "Inputfield type". Select Images appears in the "Multiple item selection" category but you can set "Maximum number of items" to 1 if you want to use Select Images for single image selections. Define selectable options for the field via a FieldtypeDynamicOptions::getSelectableOptions hook. See some examples below. FieldtypeDynamicOptions is recommended but is not a strict requirement for installing InputfieldSelectImages in case you want to use an alternative way to store the field data. Selection of Pageimages In this example the field allows selection of Pageimages that are in the "images" field of the home page. The field will store URLs to the Pageimages so it works as a kind of "image reference" field. You can use the "Format as Pagefile/Pageimage object(s)" option for the Dynamic Options field to have the formatted value of the field be automatically converted from the stored Pageimage URLs to Pageimage objects. $wire->addHookAfter('FieldtypeDynamicOptions::getSelectableOptions', function(HookEvent $event) { // The page being edited $page = $event->arguments(0); // The Dynamic Options field $field = $event->arguments(1); // For a field named "select_images" if($field->name === 'select_images') { $options = []; // Get Pageimages within the "images" field on the home page foreach($event->wire()->pages(1)->images as $image) { // Add an option for each Pageimage // When the key is a Pageimage URL the inputfield will automatically create a thumbnail // In this example the label includes the basename and the filesize /** @var Pageimage $image */ $options[$image->url] = "{$image->basename}<br>{$image->filesizeStr}"; } $event->return = $options; } }); Selection of image files not associated with a Page When not working with Pageimages you must add a "data-thumb" attribute for each selectable option which contains a URL to a thumbnail/image. In this example the field allows selection of image files in a "/pics/" folder which is in the site root. $wire->addHookAfter('FieldtypeDynamicOptions::getSelectableOptions', function(HookEvent $event) { // The page being edited $page = $event->arguments(0); // The Dynamic Options field $field = $event->arguments(1); // For a field named "select_images" if($field->name === 'select_images') { $options = []; // Get files that are in the /pics/ folder $root = $event->wire()->config->paths->root; $path = $root . 'pics/'; $files = $event->wire()->files->find($path); // Add an option for each file foreach($files as $file) { $basename = str_replace($path, '', $file); $url = str_replace($root, '/', $file); // The value must be an array with the following structure... $options[$url] = [ // The label for the image 'label' => $basename, 'attributes' => [ // An image URL in the "data-thumb" attribute 'data-thumb' => $url, ], ]; } $event->return = $options; } }); The field values don't have to be image URLs The values stored by the Dynamic Options field don't have to be image URLs. For example, you could use the images to represent different layout options for a page, or to represent widgets that will be inserted on the page. Also, you can use external URLs for the thumbnails. In the example below the options "calm" and "crazy" are represented by thumbnails from placecage.com. $wire->addHookAfter('FieldtypeDynamicOptions::getSelectableOptions', function(HookEvent $event) { // The page being edited $page = $event->arguments(0); // The Dynamic Options field $field = $event->arguments(1); // For a field named "calm_or_crazy" if($field->name === 'calm_or_crazy') { $options = []; // Add options that are illustrated with thumbnails from placecage.com $options['calm'] = [ // The label for the option 'label' => 'Nicolas Cage is a calm man', 'attributes' => [ // An image URL in the "data-thumb" attribute 'data-thumb' => 'https://www.placecage.com/260/260', ] ]; $options['crazy'] = [ // The label for the option 'label' => 'Nicolas Cage is a crazy man', 'attributes' => [ // An image URL in the "data-thumb" attribute 'data-thumb' => 'https://www.placecage.com/c/260/260', ] ]; $event->return = $options; } }); Field configuration If the inputfield values are Pageimage URLs then you can optionally include a button for each selected image to open the containing page for editing in a modal window. Note that if the selected image is contained in a Repeater item then it is the Repeater page that will be opened for editing. You can define labels for the button, notices, etc, that are used within the inputfield if the defaults don't suit. https://github.com/Toutouwai/InputfieldSelectImages https://processwire.com/modules/inputfield-select-images/
  18. This isn't caused by ProcessWire; it's just the default behaviour of browsers. The same as if you followed a link to a JPG file the content would be shown in the browser rather than downloaded. The simplest way to have any link trigger a download is to add the "download" attribute to the <a> tag: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#attr-download If you were constructing the link markup in PHP by looping over the files in a Files field it would be easy to add that download attribute to the link. But it sounds like you are creating the links in CKEditor and it's a bit more difficult there because both ACF and HTML Purifier will strip out the download attribute by default. Rather than messing around trying to configure ACF and HTML Purifier to allow the download attribute I would simply add the attribute on the front-end with a bit of JavaScript. Here's how you could automatically add the download attribute to any MP3 link using jQuery: $(document).ready(function() { // Add "download" attribute to MP3 links $('a[href$=".mp3"]').attr('download', ''); });
  19. It's working for me. I'm using PHP 7.3 so if you are using a newer version then it could be that the included Parsedown library needs an update. If you trace it back to being related to PHP version then please open a GitHub issue because TextformatterMarkdownExtra is a core module.
  20. I can't reproduce that here in v3.0.181 My guess would be it relates to custom hooks or some third-party module but if you can reproduce it on a clean PW installation then it would be good to create a GitHub issue for it.
  21. @Cybermano, I don't understand what you are doing with this line inside the foreach: $boat->barca_results->sort('id', $race); This doesn't seem to correspond to the correct use of either $wirearray->sort() or $pages->sort() so maybe you are getting mixed up with the methods? To go back to brass tacks (you may already know a lot of this but it might be useful for others)... The first meaning of "sort" as it relates to pages is the sort value. This is a column in the "pages" database table. Every page (including Repeater pages) has a sort value and this determines its sort position relative to its sibling pages (assuming no automatic sort has been set on the parent page or template). The sort value starts at zero, and the lower the number is the closer to the top of the sort order the page will be relative to its siblings. So in this page structure... The sort value of "Red" will be 0, the sort value of "Green" will be 1, and so on. In some circumstances you can end up with gaps in the sort values as pages are deleted or moved and that is where the $pages->sort() method can be used to "rebuild" those sort values under a parent. But in 99% of cases you don't need to worry about sort values because PW just takes care of it and you don't have to use $pages->sort() to sort your Repeater items so we can ignore that. You can set the sort value of a page the same as you would an integer field in the page's template: $page->of(false); $page->sort = 123; $page->save(); The second meaning of "sort" is the WireArray::sort() method. When it comes to pages, a PageArray is a kind of WireArray, and a RepeaterPageArray is a kind of PageArray, so this means we can use WireArray::sort() to sort a RepeaterPageArray. The method works by sorting the WireArray by one or more properties of its items. In the case of a RepeaterPageArray this could be something like the "modified" timestamp of the items or it could be a field such as "title". So you could do something like this to sort a field named "test_repeater" alphabetically by title (assuming that the Title field was used in the Repeater): $page->test_repeater->sort('title'); And you can get more advanced by adding a temporary custom property to the items in a WireArray/PageArray/RepeaterPageArray and then using WireArray::sort() on that temporary property. I'll show an example of that in a moment. If you have a page $p that contains a Repeater field "test_repeater" then you can sort the RepeaterPageArray as shown above in your template file and when you output the items in $p->test_repeater they'll be in the sort order you want. But if you wanted to save $p after you sorted test_repeater then it's not enough to just have the items in test_repeater in the right order and then save $p. And that's because PW uses the sort value of each Repeater page to determine the sorting of the Repeater field when it loads it from the database. So to make the sort order stick we need to save the sort value of each Repeater page in the RepeaterPageArray, and to do that we can use an incrementing counter that starts at zero. A couple of code examples... Sort test_repeater by title and save: // Get the page containing the Repeater items $p = $pages(1066); // Turn off output formatting for the page because we are going to save it later $p->of(false); // Sort the Repeater items alphabetically by title $p->test_repeater->sort('title'); // $i is a counter we will use to set the sort value of each Repeater item/page $i = 0; foreach($p->test_repeater as $repeater_item) { $repeater_item->of(false); $repeater_item->sort = $i; $repeater_item->save(); $i++; // Increment the counter } // Save the test_repeater field for $p $p->save('test_repeater'); More advanced sorting using a temporary property: // Get the page containing the Repeater items $p = $pages(1066); // Turn off output formatting for the page because we are going to save it later $p->of(false); // More advanced sorting: sorting by title length // We add a temporary "custom_sort" property to each Repeater item // And then sort the RepeaterPageArray by that custom_sort property foreach($p->test_repeater as $repeater_item) { $repeater_item->custom_sort = strlen($repeater_item->title); } $p->test_repeater->sort('custom_sort'); // $i is a counter we will use to set the sort value of each Repeater item/page $i = 0; foreach($p->test_repeater as $repeater_item) { $repeater_item->of(false); $repeater_item->sort = $i; $repeater_item->save(); $i++; // Increment the counter } // Save the test_repeater field for $p $p->save('test_repeater'); Hope this helps.
  22. 10 templates is definitely not going to be a problem. There's no particular number of templates that you should never go above, but I've asked Ryan about this topic in a Pro module forum and he said: ...and in reply to my proposal of a strategy to reduce the number of templates needed in a project... On that particular project I currently have 51 templates and there are no noticeable performance issues.
  23. You can achieve this with hooks to Page::addable and Page::editable. 1. Define access controls for the news-region and news-article templates so that users with the news-editor role can add children under news-region pages and they can edit and create news-article pages. 2. Create a news_regions Page Reference field which allows pages with template news-region. Best to make it a "multiple" AsmSelect field so it covers the potential case that a user needs to add/edit within multiple regions. Add this field to the user template. 3. Edit the news-editor users and set the regions they are allowed to add/edit within. 4. In /site/ready.php: $wire->addHookAfter('Page::addable', function(HookEvent $event) { /** @var Page $page */ $page = $event->object; $user = $event->wire()->user; // Return early if PW has already determined that the user is not allowed to add if(!$event->return) return; if($user->hasRole('news-editor') && $page->template == 'news-region') { // Addability depends on if the news_regions Page Reference field has the page $event->return = $user->news_regions->has($page); } }); $wire->addHookAfter('Page::editable', function(HookEvent $event) { /** @var Page $page */ $page = $event->object; $user = $event->wire()->user; // Return early if PW has already determined that the user is not allowed to edit if(!$event->return) return; if($user->hasRole('news-editor') && $page->template == 'news-article') { // Editability depends on if the news_regions Page Reference field has the parent page $event->return = $user->news_regions->has($page->parent); } }); The result for user "ringo": You could add all the regions to that user's page, but personally I would create a different news-supereditor role and just not make them subject to the restrictions in the hooks.
  24. No, it does prevent a bunch of downstream hooks from firing, but specifically it doesn't include Pages::save and Pages::saveField (and I think it should include those methods). And to avoid confusion it would be good if the documentation explained exactly which hookable methods are affected by the noHooks option when it used in any of Page:save(), Pages::save() and Pages::saveField(). I've opened a GitHub issue so that Ryan is more likely to notice the problem and our discussion of it: https://github.com/processwire/processwire-issues/issues/1405
  25. ProcessPageEdit has a getPage() method you can use: $page = $event->object->getPage();
×
×
  • Create New...