Jump to content

Robin S

Members
  • Posts

    4,928
  • Joined

  • Days Won

    321

Everything posted by Robin S

  1. 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', ''); });
  2. 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.
  3. 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.
  4. @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.
  5. 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.
  6. 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.
  7. 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
  8. ProcessPageEdit has a getPage() method you can use: $page = $event->object->getPage();
  9. But this isn't a solution for touch devices which don't have a hover state, and we all know that touch devices are a big share of internet use. There's discussion in the Invision Community support forums about how to bring back reputation points into the author pane (some of which I can't see but maybe @Pete can)... https://invisioncommunity.com/forums/topic/462326-current-ranks-–-and-–-reputation/ https://invisioncommunity.com/forums/topic/462099-bring-back-reputation-in-userinfopane/ Could we implement that here? Also, it would be nice to bring back custom member titles for those who had them (e.g. Ryan's reiska title): https://invisioncommunity.com/forums/topic/462409-46-how-to-restore-custom-member-titles/
  10. Not sure if it's just a forum typo but you should only have quotes around the key - it should be: $page->save(['noHooks' => true]); It seems to be working here for me. The PhpDoc comments form the basis of the online documentation: https://processwire.com/api/ref/pages/save/ So you're right that it wouldn't be hard to miss but it's not buried in the sense that you'd only find it by examining the core files. As a general thing, if you call any hookable method and include the underscores at the start of the method name then any hooks to that method won't be triggered. So I think the idea here is that the noHooks option takes care of making sure that several potential downstream hooks wont fire (e.g. Pages::saveReady, Pages::savePageOrFieldReady, Pages::saved, Pages::savedPageOrField, Pages::added) but in case Pages::save was itself hooked you'd need to call it with the underscores to prevent that hook from firing. But what's interesting is that the core itself doesn't call Pages::___save (with the underscores) when you do call Page::save() with the noHooks option true, and likewise with Pages::___saveField. Maybe the documentation for the noHooks option should be expanded to explain exactly which hooks are avoided, or the core should call Pages::___save and Pages::___saveField with underscores when noHooks is true. @ryan?
  11. Not a response to your code, but more a general suggestion... If the point of this is to restrict what editors can do in the PW admin then I think it would be cleaner and a better experience for editors if you focus on making the admin UI conform to your restrictions. In other words, if the user may not change a page's name or template then don't show those fields in Page Edit, or set them to readonly (I seem to remember that the name input needs to remain in the Page Edit form). And if the user may not sort a page then don't show the "Move" item in page lists. You should be able to achieve this with hooks to methods such as ProcessPageEdit::buildForm() and ProcessPageListActions::getActions().
  12. Double-check that the path to your JS file is correct, because that conditional will only append the script markup if is_file() is true. With some debugging (see item 2) hopefully you can get it working. I just tested here again and it works for me. The label of that config setting is perhaps not perfect - I'll update it. The setting doesn't actually ensure that Tracy is displayed, rather it only disables the default CSS in the module that would otherwise hide the Tracy debug bar. <?php if(!$hcd->show_tracy): ?> #tracy-debug { display:none !important; } <?php endif; ?> So you first need to have Tracy set to display the debug bar in the PW admin generally and then if you have that HCD setting checked it won't be hidden in the dialog. There's not much I can do about that because it's just how the CKEditor Widget plugin works. In such a situation I tend to use the arrow keys to move the cursor to the start of the field, or type the text underneath and then drag the widget to its new position.
  13. Technically files on any page are available to link to on any other page. You use the "Select Page" field to choose the page that holds the file you want to link to and then select a file from that page. But to make it quicker to select files from one or more special "library" pages there is this handy module: https://processwire.com/modules/media-library/
  14. You will get the URL to the image variation (small image) from the src attribute of the <img> tag. When you have that variation URL you can use it with PagefilesManager::getFile() to get the Pageimage (see here), and from the Pageimage you can get the original URL by $pageimage->url
  15. Here is a simple proof-of-concept Textformatter that might help: You can adapt the example in the readme to add a lightbox link to your images.
  16. Process Images A basic, proof-of-concept Textformatter module for ProcessWire. When the Textformatter is applied to a rich text field it uses Simple HTML DOM to find <img> tags in the field value and passes each img node through a hookable TextformatterProcessImages::processImg() method. This is a very simple module that doesn't have any configurable settings and doesn't do anything to the field value unless you hook the TextformatterProcessImages::processImg() method. Hook example When added to /site/ready.php the hook below will replace any Pageimages in a rich text field with a 250px square variation and wrap the <img> tag in a link to the original full-size image. For help with Simple HTML DOM refer to its documentation. $wire->addHookAfter('TextformatterProcessImages::processImg', function(HookEvent $event) { // The Simple HTML DOM node for the <img> tag /** @var \simple_html_dom_node $img */ $img = $event->arguments(0); // The Pageimage in the <img> src, if any (will be null for external images) /** @var Pageimage $pageimage */ $pageimage = $event->arguments(1); // The Page object in case you need it /** @var Page $page */ $page = $event->arguments(2); // The Field object in case you need it /** @var Field $field */ $field = $event->arguments(3); // Only for images that have a src corresponding to a PW Pageimage if($pageimage) { // Set the src to a 250x250 variation $img->src = $pageimage->size(250,250)->url; // Wrap the img in a lightbox link to the original $img->outertext = "<a class='lightboxclass' href='{$pageimage->url}'>{$img->outertext}</a>"; } }); GitHub: https://github.com/Toutouwai/TextformatterProcessImages Modules directory: https://processwire.com/modules/textformatter-process-images/
  17. That sounds like a JavaScript issue - have a look for errors in the browser console. I just checked with the latest PW/HannaCode/HannaCodeDialog and it all seems to be working here.
  18. This feature sort of exists already, and it's set on the parent template. If a template is allowed to have only a single child template then there is a setting "Name format for children" available on the "Family" tab. Info: https://processwire.com/docs/modules/guides/process-template/ There is a module that extends the feature: But my preference is to just set a date format like "Y/m/d H:i:s" for "Name format for children" and then set the final title/name in a hook to Pages::added, e.g. $pages->addHookAfter('added', function(HookEvent $event) { /** @var Page $page */ $page = $event->arguments(0); if($page->template == 'your_template') { // Set whatever you want the title and name to be $page->of(false); $page->title = "Page ID: $page->id"; $page->name = $event->wire()->sanitizer->pageName($page->title, true); $page->save(); } }); Unfortunately "modified" and "created" cannot be used in a Pages::added hook because these properties are not populated on new pages until the next request. But you could use time(), or subsequently use the modified/created properties to set the title in a Pages::saveReady hook.
  19. @Pete, the reputation count seems to have disappeared (Ryan's is around 20K). Deliberate? Also, the rocket that is poking everyone in the face looks stupid. Can we lose that? Like, what exactly is a "hero" member anyway and does anyone really care about that status? ?
  20. You could edit /site/templates/admin.php so that it looks something like this: // Get the user's IP address $ip = $session->getIP(); // Define the allowed IP addresses $allowed_ips = ['111.111.111.111', '222.222.222.222']; // Check user's IP is allowed if(!in_array($ip, $allowed_ips)) { return 'Access denied'; } require($config->paths->adminTemplates . 'controller.php');
  21. Given how simple FieldtypeRuntimeOnly is, it might be good to avoid the dependency in your module and just render your own markup directly. I haven't looked closely at your module but it just seems like it would make sense to keep it self-contained and avoid any fragility due to the chance that FieldtypeRuntimeOnly changes in the future.
  22. I can't reproduce that. It's showing collapsed => 4 when I dump the field in a FieldtypeRuntimeOnly render file. This module doesn't do anything regarding inputfield visibility so I think your issue must be a general one relating to the PW API rather than specific to this module. Really all this module is doing is rendering a TemplateFile via $files->render(). One thing though... take note of the readme regarding the variables supplied to the render file: I don't know if it relates to your issue but I suggest you avoid overwriting the $field variable by choosing a different name for the field you are getting.
  23. Here's a hook that might help: $wire->addHookBefore('ProcessModule::executeEdit', function(HookEvent $event) { if($event->wire()->input->get('name') !== 'YourModuleName') return; $event->wire()->addHookBefore('InputfieldForm::render', function(HookEvent $event) { /** @var InputfieldForm $form */ $form = $event->object; if($form->id !== 'ModuleEditForm') return; $your_module = $event->wire()->modules->get('YourModuleName'); if($your_module->foo === 'bar') { $uninstall = $form->getChildByName('uninstall'); $uninstall->description('You may not uninstall this module because foo equals bar.'); $uninstall->attr('disabled', 'disabled'); } }); }); Update: an example in the context of your module... public function ready() { $this->addHookBefore('ProcessModule::executeEdit', $this, 'disableUninstall'); } protected function disableUninstall(HookEvent $event) { if($this->wire()->input->get('name') !== $this->className) return; $this->wire()->addHookBefore('InputfieldForm::render', function(HookEvent $event) { /** @var InputfieldForm $form */ $form = $event->object; if($form->id !== 'ModuleEditForm') return; if($this->foo === 'bar') { $uninstall = $form->getChildByName('uninstall'); $uninstall->description('You may not uninstall this module because foo equals bar.'); $uninstall->attr('disabled', 'disabled'); } }); }
  24. In my opinion the best solution would be to use a ProFields Table field because it has less overhead than pages and everything is viewable inside a single compact inputfield. But if you go with a page-based approach (which will be just fine if not 100% optimal) then your options are: Repeater PageTable Child pages Another opinion: purchasing the ProFields bundle should be a no-brainer for any regular PW user. Any one of the Table, Repeater Matrix and Combo fieldtypes alone is worth the purchase price, but you get all of them, plus several more goodies which are just cherries on the top. And as Ryan explained in the most recent blog post, the purchase of Pro modules is an important part of what keeps the ongoing development of PW viable. So it's a nice way to play a part in that. ?
  25. @ryan, regarding the update to ProcessWireUpgrade, could you please add an option to skip this interstitial screen? Because it doesn't add much value and just requires an extra click. If you are visiting the Upgrades page it already implies that you want to load the latest core and module versions to check if there are any upgrades available and the few seconds needed isn't going to be a big deal to you. Thanks.
×
×
  • Create New...