Leaderboard
Popular Content
Showing content with the highest reputation on 10/31/2023 in all areas
-
Thx for the additions ? That would be a great use case for using $page->meta() ! It's already there without doing anything and it's also a lot easier to manipulate meta data from within a hook than updating a hidden field's content. Everything needed is doable with just a few lines of code without any modules. The modules do not help here. Quite the contrary, they make things more complicated and less robust. To add a runtime markup field to page edit: <?php $wire->addHookAfter("ProcessPageEdit::buildForm", function (HookEvent $event) { /** @var InputfieldForm $form */ $form = $event->return; $page = $event->process->getPage(); // custom rules where to add the field // eg add it only to pages with template "home" // otherwise exit early and do nothing if ($page->template !== 'home') return; $form->insertAfter([ 'type' => 'markup', 'name' => 'mymarkup', // we need that later ;) 'value' => '<h1>I am a custom markup field</h1>', ], $form->get('title')); }); In this markup field you can show any HTML/PHP markup you want. Now we can also add dynamic selects - also just a single and quite simple hook: <?php $wire->addHookAfter("ProcessPageEdit::buildForm", function (HookEvent $event) { /** @var InputfieldForm $form */ $form = $event->return; $page = $event->process->getPage(); // custom rules where to add the field // eg add it only to pages with template "home" // otherwise exit early and do nothing if ($page->template != 'home') return; // data coming from your file $races = ['foo', 'bar', 'baz']; // add all options $f = new InputfieldRadios(); $f->label = 'Choose your race'; $f->name = 'myrace'; foreach ($races as $race) { $f->addOption($race, "Use Race '$race' for something"); } // now add it after our markup field: $form->insertAfter($f, $form->get('mymarkup')); }); And finally we save the data: Another small hook ? <?php $wire->addHookAfter("Pages::saveReady", function (HookEvent $event) { // get the selected race from the radio input // we sanitize the input to make sure it is a string $race = $this->wire->input->post('myrace', 'string'); if (!$race) return; // save selected race to page meta data $page = $event->arguments(0); $page->meta('myrace', $race); }); Of course we have now two hooks for the same process, so we can combine those two to one and we end up with two hooks which should all you need: <?php $wire->addHookAfter("ProcessPageEdit::buildForm", function (HookEvent $event) { /** @var InputfieldForm $form */ $form = $event->return; $page = $event->process->getPage(); // custom rules where to add the field // eg add it only to pages with template "home" // otherwise exit early and do nothing if ($page->template != 'home') return; $race = $page->meta('myrace'); $time = $page->meta('chosenat') ?: ''; if ($time) $time = date("Y-m-d H:i:s", $time); $form->insertAfter([ 'type' => 'markup', 'name' => 'mymarkup', 'value' => '<h1 class=uk-margin-remove>I am a custom markup field</h1>' . "<p>Last Chosen Race: $race (@$time)</p>", ], $form->get('title')); // data coming from your file $races = ['foo', 'bar', 'baz']; // add all options $f = new InputfieldRadios(); $f->label = 'Choose your race'; $f->name = 'myrace'; foreach ($races as $race) { $f->addOption($race, "Use Race '$race' for something"); } // now add it after our markup field: $form->insertAfter($f, $form->get('mymarkup')); }); $wire->addHookAfter("Pages::saveReady", function (HookEvent $event) { // get the selected race from the radio input // we sanitize the input to make sure it is a string $race = $this->wire->input->post('myrace', 'string'); if (!$race) return; // save selected race to page meta data $page = $event->arguments(0); $page->meta('myrace', $race); $page->meta('chosenat', time()); }); No modules, no rocket science. Just ProcessWire awesomeness ?2 points
-
As @bernhardwrote, it is pretty doable. In addition to the markup field, I'd also add two hidden fields to the template to: store the parsed data (textarea) indicate we need to make a choice (checkbox) In a Pages::saveReady hook, I'd check if the file field has changed. If yes, parse the contents, and either: a) assign the page fields if there is only one race present; b) or fill the textarea with the parsed data (probably json encoded) and set needsChoice to 1. If the file field hasn't changed, needsChoice is 1 and a choice was sent, wake up the data from the textarea, pick the chosen race and fill your fields. Then set needsChoice to 0. In a ProcessPageEdit::buildForm hook, I'd check if needsSelection is set, and if yes, I'd fill the markup field with the race selection radio input. (Instead of the markup field, one could even use a regular InputfieldRadios and insert that into the form). It also has the charm that you can put "needsChoice!=1" in your selectors to see all unfinished multi-race pages. Two hooks, three fields, done.2 points
-
This is where you make it more complicated than it needs to be ? You don't need to cancel the save at any stage. You just need to make sure that only the correct values are written to a dedicated field of your page. But there's nothing bad in having the file stored in one field, then process that file on save, then let the user choose the correct race and then on the next save you write the correct race to the dedicated field that only stores one race. This would be fairly easy to achieve with a custom markup field. There you process the file and display results, for example with a radio list element. The markup field could show some information like "this field will list all races from the uploaded file. please upload a file and save the page." After upload and save you present something like this: Once the user selects for example "race 2" and saves the page you take that information and save all the necessary data of race 2 to your page's dedicated fields and voila you always have the correct data in your pages and you don't need to revert anything or such. In general chances are high that this feeling is wrong ?2 points
-
ProcessWire stores the users in /admin/access/users/. This means if you want to view the user list in the page tree, you have to click into it and that's not ideal. It would be better if the Users were just presented as if they were nested under the Home page. Approach 1: actually move Users under Home (avoid this; explanation) While I haven't tried it (and have no plans to), you could do what Ryan described in this article a long time ago, this but it's probably dangerous and overkill: https://processwire.com/blog/posts/processwire-core-updates-2.5.14/#multiple-templates-or-parents-for-users Approach 2: fake it! (better and very easy) What would be better for some is to have it appear as if the Users were directly under the home page, but without actually having to move the Users page itself as it could break ProcessWire functionality. This can be accomplished easily with the following hook (add this to /site/templates/admin.php): // make Users appear as nested under Home page $wire->addHookAfter('ProcessPageList::find', function(HookEvent $event) { if($event->arguments('page')->id!=1) return; $event->return = $event->return->prepend(wire('pages')->get('/admin/access/users/,include=all')); });2 points
-
Sooner or later, everybody gets hit by the dreaded "can't we integrate this with [Azure|Google|Facebook|whatever|...]?" question. Lately, those have more or less assaulted me, and I've been looking for a clean-cut solution to link my PW instances up to a big Identity Provider. There is already the SamlAuth module in the module repository, but it has not been updated in a while and it's not as "graphical" as I'd like it to be - I want to be able to take long holidays away from cellphone reception while our IdP's certificate might expire. So I started wiring things up from scratch (as much as "building a PW UI and endpoints around php-saml) can be called "from scratch". So I've been starting to build: PoetSaml2 A SAML2 Service Provider for the ProcessWire CMS/CMF The module is still very alpha, rough around the edges and lacking a bunch of features I consider essential for long-term production use. Still, I decided to get the word out there early, maybe find even find some daring early adopters willing to restore a backup or two of their PW instance in case things go wrong, and also perhaps get some feedback about use cases and requirements I am not aware of. My SAML2 experience so far is limited to an enterprise environment with only Azure / Entra Id and SamlTest.id. It uses OneLogin's php-saml library for the hard work. The necessary SSO endpoints are realized with ProcessWire's URL hooks. Requirements ProcessWire >= 3.0.218 FieldtypeOptions FieldtypeRepeater PHP-OpenSSL Compatibility Basic compatibility has been verified with both Entra Id and SamlTest, meaning I could initiate successful logins into ProcessWire both from PW itself and from the Identity Provider. Screenshots SAML2 login button on the admin login form: PoetSaml2 comes with an admin page that lists all configured profiles and gives you quick links for adding and deleting profiles, lets you download your metadata file so you can upload it to your Identity Provider and even lets you backup profiles to a file. Uses ProcessPagesExportImport to import backup files. The profile configuration is a regular page edit screen. There are sections for the local endpoint (SP Configuration) and for the Identity Provider (IdP Configuration). You can set a redirect URL or even role specific URLs so PW knows where to take you if you initiate a login on the IdP side. Fine grained login permission, redirect URL discovery and even user creation based on SAML Claims can be realized through hooks. A checkbox lets you create a self-signed SP certificate. You can import your IdP's metadata.xml from a file or URL instead of having to copy & paste the URLs and certificate (thanks to php-saml's metadata parser). A lot of it is already documented to some extent in the README file on GitHub.1 point
-
That's a serious amount of PW awesomeness you've provided! Bookmarked for my own purposes ?1 point
-
Good call! Its existence had completely slipped my mind.1 point
-
Thanks for the feedback! I tested things with 4.1 and just committed the change to GitHub. If you don't mind me asking: which IdPs have you had success with, and did you have to dabble with advanced settings to get things up and running? (If you don't want to or can't answer that, I'll understand).1 point
-
To implement what has been suggested by @bernhard and @BitPoet, there are modules that might help. Perhaps you could display data using the RuntimeMarkup module. And for selecting a race, the Dynamic Options module or the Dynamic Selects (commercial) module might be useful.1 point
-
I always have https://processwire.com/modules/module-settings-import-export/ installed on all my sites, so no need from my end for it to be built into your module.1 point
-
Yes I saw that, but it was in the commented line so I didn't fixed it instantly, will push the fix. Ok I see, in first instance I had in mind to put a button to get the template script copied in the clipboard in order to paste and modify it. I will push also the fix following your recommendation ? I was also thinking of pushing a new feature that would allow you to export and import module settings on other installations, what do you think, it could help?1 point
-
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!1 point
-
Hi both, Actually, this was supposed to be last week! I was hoping to release version 009 of Padloper which is packed with a number of new features including discounts but life got in the way! I am now going to be unavailable from this week for a number of weeks (at least 2) without access to internet. Hope to finish this quickly when I get back. Nice work all the same!1 point
-
Actually, the only other suggestion is to make it a real initial value for the textarea rather than a placeholder, because I actually had to go and open mysqldump.unix.sh to get a starting script I could then edit.1 point
-
Thanks @flydev The only issue I noticed is that the supplied mysqldump.unix.sh looks like this: #!/bin/sh # (1) Set up all the mysqldump variables FILE=%%FILE%% DBSERVER=%%SERVER%% DATABASE=%%DATABASE%% USER=%%USER%% PASS=%%PASS%% CACHEPATH=%%CACHEPATH%% # Fix trailing slash in cache path CACHEPATH="${CACHEPATH%/}/" OUTPUT="${CACHEPATH}${FILE}" # (2) In case you run this more than once a day, remove the previous version of the file # unalias rm 2> /dev/null # rm ${FILE} 2> /dev/null # rm ${FILE}.zip 2> /dev/null # (3) Do the mysql database backup (dump) # - to log errors of the dump process to a file, add --log-error=mysqldump_dup_error.log # (a) Use this command for a remote database. Add other options if need be. # mysqldump --opt --protocol=TCP --user=${USER} --password=${PASS} --host=${DBSERVER} --port=${PORT} ${DATABASE} ${DATABASE} > ${OUTPUT} # (b) Use this command for a database server on localhost. Add other options if need be. mysqldump --routines --triggers --single-transaction --user=${USER} --password=${PASS} --databases ${DATABASE} > ${OUTPUT} Note the missing PORT from the variables in #1 and also the duplication of ${DATABASE} in #3a Once I fixed those two things, it work great! Thanks again for making this configurable - a really nice improvement!1 point
-
Just to confirm: I've got a php-saml based setup running (via in-house PW SAML module, which is loosely based on https://github.com/adamxp12/ProcessWire-SAMLAuth) and only requirement there is onelogin/php-saml ^4.1 ? Anyway, it's good to have an up to date SAML module available, so thanks for sharing!1 point
-
Hey @Robin S - good question. I have played around a little without any success. I feel like I am missing something obvious like output buffering getting involved, but perhaps it is just down to the way I instantiate a PW template and render it via AJAX https://github.com/adrianbj/TracyDebugger/blob/ec79b76bf0443118a6c402c9ffe2a9c491a8b5cc/includes/CodeProcessor.php#L197-L203 I'll keep mulling it over, but not sure what the solution might be at the moment.1 point
-
There are no modules for that as far as I know. What you are looking for is not so easy. ProcessWire does that by redirecting to a dedicated form that is only built for that very specific action and takes all the arguments via GET parameters: That way PW knows what to do after the confirmation was given. In that case it will remove field 102 from template 1. You see that very basic behaviour and its limitations if you try to remove fields and also try to rename the template. Then PW needs to show to confirmations and do two actions and just skips one with informing the user: That's some background to what you were asking. If you tell us more about your real use case it would be easier to think of the best solution. Details really matter here. Which fields do you want to check? Are we talking about 2? 10? 100? Can we simply wipe data if some conditions are not met and show a warning or do we need to restore old values or such?1 point
-
I don't know how to do what you're suggesting (perhaps someone else can help!), but I'm wondering if it might be possible to achieve what you need using field dependencies. It might work to combine dependencies with validating fields or setting field values as discussed here:1 point
-
Thanks Brendon. Happy to know that I’m not blind. I will give a thanks trophy anyway. People in this forum are so friendly, if I can boost their ego, it will be my pleasure... ?1 point
-
The "Thanks" trophy is nice in that it provides reputation points back to members, but aside from a little ego boosting, I don't think it has any terribly high value or merit -- with perhaps the exception of visually providing a means to let other visitors know what replies are worth reading carefully? The "mark as solution" feature is not enabled, or is at least hidden. I'm not sure why the email notifications are still worded that way, but you won't be able to find that button. ?1 point
-
Ah, should have mentioned that. wireRenderFile returns the rendered content, so you need to echo it or use <?= wireRenderFile(...) ?>.1 point
-
TL:DR I've updated a PW page we've built 9 years ago for the first time and it's still a solid experience. Backstory Back in May I was on a crowded train somewhere in the middle of Germany. Now working as a "Consultant" who builds slidedecks instead of websites, I happily noticed the men next to me talking about responsive webdesign with his friend. During the obligatory "This train is late" announcement we started to chat. My seatmate, a geography teacher, recently attended a web workshop at a large Hamburg agency. He told me he now understands the value of a CMS for updating their site and he wonders how to build a responsive layout. They don't get paid for this and work on their homepage in their spare time. And they have a Typo3 installation ? Back in 2013, together with my friend Marvin, we've rebuild our school website with ProcessWire optimized for mobile devices. Launched in 2014 this was quite an impressive feat including online time tables, a working event calendar (with import feature) and many small nice touches. After my encounter on the train, I checked the page and yes, It's still online and updated daily! The next day I wrote my old teacher a short email if we should have a closer look into the underlying tech and within minutes I got a super happy reply that he is so glad that somebody would help (again). So let's dive into what we've done. Situation First some details about this ProcessWire installation that is updated by a few teacher on a regular basis. Over the 9 years they've wrote nearly 900 news articles and kept more than 250 pages up to date. The asset folder is over 11GB. Build with Processwire 2.4 (?) and lots of janky code we've updated the page once to 3.0.15 somewhere in 2016 quick and dirty. They even used the old admin layout. ProCache, CroppableImages3 and a few other plugins were used. Every single one of them required an update It's used the classical append-template approach with a single big "function.php" included file. It's running on PHP 5.6 and for whatever reason no PHP update was enforced by the hoster (But the admin panel screamed at me) A privacy nightmare: Google fonts embedded directly, no cookie banner and a no longer working Google Analytics tag included The old ProcessDatabaseModule made a database backup every week as planned over all these years. Nice. No hacks, no attacks and all teachers are using their own account with assigned permissions Changelog I've updated the page with a focus on making it stable and reliable for the next 9 years. After making a development copy of the page, I've started working on the following changes: Updated ProcessWire and all modules to the latest stable version. After reloading a few times, no errors encountered Updated the whole templates to make it work with PHP 8.2 Removed all externally hosted scripts, disabled cookies for all regular visitors and introduced a 2-click-solution for external content Reworked a few frontend style issues around the responsive layout, made slight visual changes for 2023 (e.g. no double black and white 1px borders) Ported the image gallery feature to more templates (Big wish of the people updating the site, they've used a workaround) Cleaned up folder and structures, removed a few smaller plugins and admin helpers no longer needed All this was done back in May and - with a big break - completed now in October. It took a few days and most of the time was spent figuring out our old code. Learnings ProcessWire is robust as f*ck. I just clicked "Update" and it mostly worked instantly I nearly removed features for the PHP update. A custom written importer for the proprietary XML schedule was hard to debug and understand (5-dimensional-arrays...). Gladly I've tossed a coin and just gave ChatGPT the php function source and error message and within a single iteration it updated the code for PHP8. The "responsive" CSS framework aged badly. The used 960gs skeleton uses fixed widths for the responsive layout. I couldn't get it be wider than 320px on mobile screens. So the site is responsive but with a slim profile for now. Replacing it would be a complete layout rewrite Result and looking forward The Werkgymnasium site is now updated and live again. It still loads superfast and looks great after all these years. We have a few more features planned to help our editors input new content but overall it just works. Looking forward a few issues remain. ProCache would require the paid update but it still works fine. The layout needs improvement on mobile screens. There is still an error with the pagination. We'll cleanup the code more and then make the whole template public on Github so that maybe a few students after us can continue with the updates. Maybe even rebuild the frontend one day. I hope I can give you an update in a few years again. As a closing note: I'm still grateful for the amazing community here and all the features ProcessWire has to offer. My daily work no longer resolves around websites but PW has a permanent spot in my heart. Thanks Ryan and all the contributors.1 point
-
This week there are a few updates on the dev branch. Since I'm short on time today, I'll just briefly cover the most interesting ones. Support for OR-groups, sub-selectors, and match same (1) @item groups (see last example at that link) have been added to ProcessWire's in-memory page matching selectors, as used primarily by the $page->matches() method. Previously these features were supported only by the database selectors from $pages->find() and methods that delegated to it. The $page->matches() method that gained these features is used by the core in various places for runtime matching of pages, such as when finding/filtering in a PageArray, for example. Support has also been added for OR-groups to the base Selectors class, which makes it possible to use OR-groups on non-Page objects too. These database-specific selector features were added because there's been a recurring request to support OR groups in places that currently use memory selectors, so this should do that. Though it'll remain just on the dev branch until it's had more thorough testing. While there will always be some differences between database and memory selectors, this does narrow the gap a bit. Thanks for reading and have a great weekend!1 point
-
Thanks @ryan - this is fantastic update. My initial testing shows that OR selectors are now working for the WireCache $expire selector (https://github.com/processwire/processwire-issues/issues/1828) which really opens up the ability for some huge performance benefits. Thanks again!1 point
-
Two great news!! 1) The module directory now reads module version numbers from package.json files!! Now when using automated workflows like shown above your module's version in the modules directory will automatically be in sync with your module ? 2) Microsoft has just released the github actions extension for VSCode that helps developing and debugging github actions directly from within your IDE!! https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-github-actions1 point
-
Regardless of whether handling this with processwire you should change your server variables to handle such a request. Your php.ini should be adjusted for it (at least): upload_max_filesize = 350M post_max_size = 350M And your apache should be configured too: memory_limit = 400M Update (thx Wanze) You should also adjust settings like max_execution_time max_input_time in your php.ini... I don't know if your clients host has such capabilities (-> memory limitations for such one vhost) but you should consider to convince your client to upload such files with ftp (not with http) and it is less error-prone for you and him with ftp...1 point