Leaderboard
Popular Content
Showing content with the highest reputation on 02/19/2021 in all areas
-
This week I've been developing a client site, but also working through some of the feature requests. I'm going through them in order of "thumbs-up" on GitHub and trying to focus more on those that I can get through reasonably quickly. For a summary of what's been added, be sure to see the dev branch commit log. There's still a few more I'd like to add before bumping the version to 3.0.173, so will save that for next week. One of the requests was for the ability to add custom after-save actions in the Page editor. These are the "Save + Exit", "Save + Add New", etc., dropdown actions that you see on the Save button in the page editor. This is something that's already supported, but not formally documented. So I wanted to quickly go through a couple examples of how to do that here, as it is kind of useful and fun. Let's start with a "hello world" example to keep it simple, then we'll move on to a more practical example. Say we want a "Save + Say Hello" dropdown action in our page editor Save button. We need one hook to add the action, and another to process it. These hooks could go in your /site/ready.php file or your /site/templates/admin.php file, or in a module. First we'll want to hook ProcessPageEdit::getSubmitActions() to add our custom "hello" action: $wire->addHookAfter('ProcessPageEdit::getSubmitActions', function($event) { $actions = $event->return; // array of actions indexed by name $page = $event->object->getPage(); // page being edited // add a new action that says hello after saving page $actions['hello'] = [ 'value' => 'hello', // value for action that you will check 'icon' => 'hand-spock-o', // icon to show in action, excluding the 'fa-' part 'label' => '%s + Say Hello', // the '%' is replaced with 'Save' or 'Publish' 'class' => '', // optional class if you need different styling for button/link ]; $event->return = $actions; }); To process our action, we'll need to add a hook to ProcessPageEdit::processSubmitAction(): $wire->addHookAfter('ProcessPageEdit::processSubmitAction', function($event) { $action = $event->arguments(0); // action name, i.e. 'hello' $page = $process->getPage(); // Page that was edited/saved if($action === 'hello') { $notice = new NoticeWarning("Hello! You edited page $page->path"); $notice->icon = 'hand-spock-o'; $event->notices->add($notice); } }); That's all there is to it. That part where I created the $notice above could just as easily been $this->warning("Hello!..."); but I wanted to add a custom icon to it, which is why I created the Notice manually. Many of the built-in after-save actions perform a redirect to another location, such as adding another page, exiting back to the page list, viewing the page on the front-end, etc. If you have a need to perform a redirect after the save, use the $event->object->setRedirectUrl($url); method. This is preferable to calling $session->redirect(); yourself, as it is handled by the page editor after it has finished everything it needs to do. What if you just want to remove one of the existing actions? For instance, maybe you want to remove the "Save + Add New" action. That action has the name "add", so we can remove it like this: $wire->addHookAfter('ProcessPageEdit::getSubmitActions', function($event) { $actions = $event->return; // array of actions, indexed by name unset($actions['add']); $event->return = $actions; }); If there's another you'd like to remove, I'd recommend using TracyDebugger and bd($actions); so you can see and identify all the actions that are present from your hook. Now let's get to a more practical example. Let's say that you are using ProCache and you want to add a save action to automatically prime the cache after performing the save. By "prime the cache" I mean have it perform a page-view on the front-end that makes it save a new cache file for the page. Here's how you could do that: $wire->addHookAfter('ProcessPageEdit::getSubmitActions', function($event) { $actions = $event->return; // array $page = $event->object->getPage(); // page being edited $procache = $event->wire('procache'); if(!$procache) return; // if procache not installed, do not add action if(!$page->isPublic()) return; // page not public is also not cacheable if(!$page->viewable()) return; // page not viewable has no template file if(!$procache->isPageCacheable($page)) return; // page not setup for cacheing $actions['cache'] = [ 'value' => 'cache', 'icon' => 'fighter-jet', 'label' => '%s + Cache', // Save + Cache 'class' => '', ]; $event->return = $actions; }); …and our hook to process the action: $wire->addHookAfter('ProcessPageEdit::processSubmitAction', function($event) { $action = $event->arguments(0); // action name, i.e. 'cache' $page = $process->getPage(); // Page that was edited/saved if($action === 'cache') { $http = new WireHttp(); $response = $http->get($page->httpUrl); if($response) { $size = wireBytesStr(strlen($response)); $event->message("Cache primed for page $page->path ($size)", "nogroup"); } else { $this->warning("Error caching page: " . $http->getError()); } } }); Note that we don't have to clear the cache file here because that's something that ProCache has already done prior to our hook above being called. Thanks for reading and have a great weekend!10 points
-
From the recent discussion about the roadmap & wishlist for 2021 and some other posts by @ryan, it comes to my mind that developing and coordinating the whole project for one person is becoming harder and harder and leads nearly to the reverse of expanding the ProcessWire ecoysystem. This is not against Ryan, i think everyone here knows how engaged he is about ProcessWire, but he has only 24/7 (sometimes i think he's got far more than that...). We as the community could support the project (financially) to relieve Ryan and could take over some tasks from him. This could be, but is not limited, to: Building a Foundation/Association/Company to ensure the persistence of the project and to fund the work put in ProcessWire of Ryan (and others). Nearly every other CMSs i checked has something like this (Drupal Association, Typo3 Association, Joomla Foundation, Wordpress Foundation, Contao Association, ...). This also puts more trust in the project, if someone new will check on his engagement in ProcessWire. Assigning persons/teams to work on things: Extending the core (when necessary) Developing and maintaining major modules (e.g. page builder, admin themes, internationalization, marketing, ecommerce system, ...) Testing and inspection of modules developed by others Making translations of modules (translation of the core is mostly covered, i think) Working on PRs & issues submitted on github Working on the homepage Coordinating the community efforts I know, some resorts are already covered by others (e.g. @Pete for the forum, @horst for images, ... ), but there are many other areas where this ist not the case. By joined efforts by the ProcessWire community this hopefully will also attract new developers to the system and by a growing number of users this assists in the things above in a circular process. What do you think?6 points
-
Thanks for the report @adrian. The newly released v0.3.6 adds support for ProcessProfile and ProcessUser, and also adds a config field to show the Tracy debug bar in the dialog which can be helpful for debugging if you are building the dialog form with a hook.2 points
-
Check out the Multisite module: https://github.com/somatonic/Multisite/2 points
-
Hi, I have multiple $pages->find() operations which result in multiple page ids. e.g.: $find1: 8044|10045|3702|11067 $find2: 2004|5421|9392 Next i'm searching these ids to get a new WireArray, which i can limit and paginate: $unifiedMatches = $pages->find("id=$find1|$find2, limit=$limit"); This works all as intended. The problem is, that this new collection of pages doesn't maintain the order of the ids in the selector. They are sorted by their id. $unifiedMatches: 2004|3702|5421|8044|9392|10045|11067 Is there a way to get the pages in the $unifiedMatches result in the order of the selector input? $unifiedMatches: 8044|10045|3702|11067|2004|5421|9392 Thanks!1 point
-
A couple weeks ago there were a lot of ideas about page builders and different approaches in ProcessWire. Some really interesting ideas actually, and great proof of concepts as well. The conversation started moving faster than I could keep up with, and I think Teppo's summary in ProcessWire Weekly 351 is a great overview, if you haven't yet seen it. These are so great in fact, that I don't think anything I build could live up to what some of you have already built. In part because my focus is more server-side than client-side development, and I can clearly see most of these page builders are much more client-side based than I have expertise in (though would like to learn). Whereas some of you are clearly amazing client-side developers; I'm blown away by what's been built, and how quickly it happened. One struggle I have with page builders is that I don't have my own good use cases for them, with the projects that I work on. Others here (like Jonathan) already have a lot of good use cases. Clearly there are a lot of benefits. I'm fascinated and enthusiastic by all of it, but can't envision how or if I'd use such a tool in my own projects. And having experience with (and particularly close use cases for) a tool is important in hitting the right notes in building it. Likely page builder support belongs in ProcessWire in some form or another, and ProcessWire can be an excellent platform for it. But the actual page builder needs to be designed and built by the people that have expertise in the tools needed to build it, and by the people that will actually use it. I think it should be a community collaboration. For anything needed to support this on the PHP side, count me in. I don't think I've got the background to "lead" the development of the page builder, but I do think I've got plenty to contribute to it. I'd also like to continue building out existing tools like Repeater/Matrix to further support page builder type use cases. I'd like to rebuild and move the auto-save and Live Preview features from ProDrafts into the core, which should also help support some of these page builder cases. Whether these will eventually apply to the larger page-builder project, I don't know, but they'll be useful either way. What are the next steps? Who's interested in working on this? Let's keep the conversion going and move forward with it.1 point
-
Appreciate the consideration! I tried to make Fluency as modular as possible so hopefully it shouldn't be too hard to add another engine. If you take a look at how DeepL works with Fluency, it exists as it's own class and Fluency doesn't know how or what happens in DeepL, and vice versa. Following that structure, you could create a Lingvanex class that Fluency sends data to. Matching DeepL method parameters when calling it and data structures it returns could make it pretty straightforward. An idea I had would be to create a bootTranslationEngine() method that is called in ready() which would determine what engine/API key is available and instantiate the proper translator class in something like $this->translationEngine. That would replace $this->deepL in the Fluency module. I would make it a goal to abstract out any references to any brand of translator in the Fluency module's logic. As long as getClientBootData() and translate() take and return the same structure of data you wouldn't even need to touch the front end. I haven't really written any code that is collaborated on before so I hope that the commenting and overall structure makes sense. If you ever have any questions or want a shortcut to figuring something I did out then message me and we can chat.1 point
-
Hm not really since in the end it's about PW outputting data, whether it's within HTML tags or as a json object. Here you only provide a very small sample of your setup so it's hard to tell, but there could a number of things that impacts the performance of PW. Hooks that take a long time to execute or something... Using TracyDebugger could help you narrow down what's going wrong.1 point
-
It should work for your situation as well. Render your HTML markup and once you're done and want to stop PW output call $this->halt. Try and let me know if it works. Edit: That is, in your initial setup where you have _main.php included by default after your template1 point
-
I assume you're using the markup regions output strategy ? If so, when getting an ajax call, once you're done in your code and have echoed your response, you can call $this->halt(); to stop the template output (from https://processwire.com/blog/posts/processwire-2.6.8-brings-new-version-of-reno-admin-theme-and-more/#new-this-gt-halt-method-for-use-in-template-files) (I couldn't find it referenced in the doc though...) So for example in my case I have (in my page.php) if ($config->ajax) { http_response_code(200); header('Content-Type: application/json'); // code to populate $json echo json_encode($json); return $this->halt(); }1 point
-
Thanks for your thoughts @FireWire - I think I'll be having to switch to Lingvanex sooner than later, so there might be a PR coming you way. Let me know if you already have any thoughts on how you would like to implement different translation engines so that I hopefully do it in a way that you're happy with.1 point
-
Thanks for the suggestion, however this has the same infinite loop effect as the ::loaded hook. It just goes Page::render -> Saml -> Page::render -> Saml -> ... Okay, that's silly but I just added a condition to actually check for the Saml Response. This way it doesn't try it after the user is in... Thanks @louisstephens if (!isset($_SESSION['samlUserdata'])) { // User not logged in to SAML $this->session->addHookBefore('PageRender::renderPage', $this, 'loginsaml'); }1 point
-
maybe this can help you out: https://processwire.com/api/ref/wire-array/sort-flags/ (but haven't tested anything myself)1 point
-
@Robin S - just discovered that if you have this attached to a field that is editable under a user's profile page, it breaks the field with a JS error because it can't load this component. No rush for a fix for my needs, but someone else might come across it.1 point
-
Not sure if I got this right, but when you say that the images are "in a custom folder directly from ERP", do you mean that this custom folder is at the local disk, not some external server/service/whatever? If that's the case, you can resize them with ImageSizer, but it requires going through some extra hoops. You may find this thread useful — it's about generating resized versions of Pagefiles, but the concept here is similar, with the difference that the path to file is not in a Pagefile object but rather your text field. That being said, it would be much easier if you would just store those images in an image field on one of your pages. The process would be more straightforward, you wouldn't need to worry about cleaning up old variations, and so on. I guess the TL;DR here is that if you have a good reason for not handling those images as regular ProcessWire images, then yes, technically it's possible to do what you've asked here with ImageSizer, but a) this is rarely the best approach, and b) even then you may find working with Imagick or something similar directly a tad easier ?1 point
-
Perhaps FatFree can be another source of inspiration (or it could even be added to our projects easily? The whole library is 483 KB and 45 files...!) https://fatfreeframework.com/3.7/routing-engine https://www.youtube.com/watch?v=XGldwy1pmU0&feature=youtu.be FatFree has good a history of being maintained: https://github.com/bcosca/fatfree/releases1 point
-
Really no problem at all. I've released a newer version since then that addresses an issue that I didn't see come up in testing previously. Just a courtesy tag in case the bugfixes end up helping someone. I haven't heard of Lingvanex before so I didn't know to look into them. Initially when I found DeepL I was really impressed by the service but needed someone else to really verify what was going on because I only speak English. I had a coworker who speaks Spanish look at the results and he was very impressed. I kind of stopped there and didn't think to look further. Overall if someone was able to verify the quality of translation of any translation then that's all that matters. I don't have any personal allegiance to DeepL, it's just what I found and what I heard about on the PW forums which also nudged me towards them. It would be interesting in the future to update Fluency to allow the dev to select a translation engine, and the way the module is built I think it could be done well. I wish I could work on that now but I just had 3 clients contact me for work and that's in addition to my day job...1 point
-
You’re not jumping in an out of namespaces in that sense (you could do that if you wanted to get really freaky). As long as you’re in the ProcessWire namespace, any function you declare belongs to that namespace. Since you’re inside the namespace anyway, you can call them without specifying ProcessWire\myFunction(), but apparently PHP’s dynamic features like call_user_func() need you to be more specific. I don’t know this per se, it’s a combination of googling and talking out of my bum bum, lol, so I’m probably getting a good amount of technicalities wrong. Here’s a link to the PHP docs about it https://www.php.net/manual/en/language.namespaces.dynamic.php.1 point
-
If anybody else is looking for something like this: There's now a successor to this module on github: https://github.com/BitPoet/ProcessCKInlineComplete1 point
-
I've done that too complicated too many times ? $wire->addHookAfter("Pages::saveReady('template=foo,id=0')", function($event) { $event->arguments(0)->status = 1; }); Now create a new "foo" page and it will automatically be published (and also the "temp" status will be removed) ? I need that all the time for backend applications...1 point
-
This bit me today so I created a feature request: https://github.com/processwire/processwire-requests/issues/382 Would you guys mind giving it a thumbs up to get Ryan's attention? Thanks.1 point
-
Here are links for a few different approaches to the general question: https://github.com/LostKobrakai/Paginator https://gist.github.com/somatonic/5420536 https://processwire.com/talk/topic/5558-adding-pagination-after-merging-two-pagearrays/?do=findComment&comment=54122 And my own suggestion: // Find all the matches, but only find the IDs to reduce memory overhead $matchest = $pages->findIDs("title~=$q, template=BN-article|BN-infopage"); $matchest_str = implode('|', $matchest); $matchesb = $pages->findIDs("body~=$q, template=BN-article|BN-infopage, id!=$matchest_str"); $match_ids = array_merge($matchest, $matchesb); // Define some pagination settings $items_per_page = 10; $start = ($input->pageNum - 1) * $items_per_page; $total = count($match_ids); // Get the IDs for this page of the pagination $item_ids = array_slice($match_ids, $start, $items_per_page); // Get the pages using the IDs $items = $pages->getById($item_ids); // Apply the pagination settings to the pager $items->setTotal($total); $items->setLimit($items_per_page); $items->setStart($start); // Output the items as needed foreach($items as $item) { echo "{$item->title}<br>"; } // Output the pager echo $items->renderPager();1 point