Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 06/19/2022 in all areas

  1. This week I've been busy developing a site. It's the same one as the last few weeks, which is an established site moving out of WordPress and into ProcessWire. Next week the whole thing is getting uploaded to its final server for collaboration and for client preview. I've been pretty focused on getting that ready and don't have any core updates to report this week, though should next week. One thing about the prior version of this site (and perhaps many WordPress sites) is that there wasn't much structure to the pages used in the site, and hundreds of unrelated pages in the site confusingly live off the root, such as /some-product/ and /some-press-release/ and /some-other-thing/. There's no apparent structure or order to it. And those pages that do have some loose structure in the wp-admin have URLs that don't represent that structure on the front-end. There's very little relation between the structure one sees in the wp-admin and the structure that one sees in the URLs, or in the front-end navigation. They all seem to be completely unrelated. That's one thing that I've tried to fix, so that there is some logic and structure rather than having a bunch of unrelated pages all in the same bucket (is this common in WordPress?) But there's one big caveat. We didn't want to change anything about the actual URLs that are used on the site. This is a site with a long history, a lot of incoming links, and a lot of search traffic. The current URLs have been in place a long time and we didn't want to introduce more redirects into the site (there are already a ton of 301 redirects accumulated over time). So we wanted to make sure the existing URLs in the new ProcessWire-powered site are identical to what they were in the WordPress site. That might seem difficult to do, going from an unstructured WordPress site into a highly structured ProcessWire site... but actually it's very simple. Here's how: Making ProcessWire render pages from WordPress URLs We created a new URL field named "href" and added it to most of the site's templates. For established pages that came in from the old WP site, this "href" field contains the WordPress URL for the page. Depending on the page, it contains values like /some-product/ or /some-press-release/, /some-country/some-town/, etc. In most cases this is different from the actual ProcessWire URL, since the page structure is now much more orderly in the back-end. And for newly added pages, we'll be using the more logical ProcessWire URL. But for all the old WordPress pages, we'll make them render from their original URL. This is certainly preferable from an SEO standpoint but also helps to limit the redirects in the site. In order to make $page->url return the old/original WordPress URL (value in $page->href), a hook was added to the /site/init.php file: /** * Update page paths and URLs to use the 'href' field value on pages that have it * */ $wire->addHookBefore('Page::path', function(HookEvent $event) { $page = $event->object; /** @var Page $page */ $href = $page->get('href'); if(!$href) return; // skip if page has no 'href' value $event->return = $href; $event->replace = true; }); Now we've got $page->url calls returning the URLs we want, but how do we get ProcessWire to accept those URLs for rendering pages? The first thing we'll need to do is enable URL segments for the homepage template. We do this by going to: Setup > Templates > home > URLs > and check the box to enable URL segments. Save. Then we need to edit our /site/templates/home.php to identify when URL segments are present and render the appropriate page, rather than the homepage: $href = $input->urlSegmentStr; if($href) { $href = '/' . trim($href, '/') . '/'; $page = $pages->get("href=$href"); if(!$page->id || !$page->viewable()) wire404(); $wire->wire('page', $page); // set new $page API var include($page->template->filename); // include page's template file } else { // render homepage output } As you can see from the above, when URL segments are present, we find a page that has an "href" field value matching those URL segments ($input->urlSegmentStr). If we don't find one, we stop with a 404. But if we do find one, then we set the $page API variable to it and then include its template file, making that page render rather than the homepage. If there is no $input->urlSegmentStr present then of course we just render the homepage. That's it! By using these little bits of code snippets to replace ProcessWire's URL routing, now all the URLs of the old WordPress site are handled by ProcessWire. Like most things in ProcessWire, there's more than one way to accomplish this… We could have used URL/path hooks, or we could have hooked before Page::render to take over homepage requests with URL segments, before the homepage even got involved. Or perhaps we could have hooked in even earlier, to something in the ProcessPageView module or PagesRequest class. Or we could have used an existing module. Any of these might be equally good (or even better) solutions, but I just went with what seemed like the simplest route, one that I could easily see and adjust. Plus, it'll work in any version of ProcessWire. The actual solution I used is a little more than what's presented here, as it has a few fallbacks for finding pages and scanning redirect lists, plus passes along remaining pagination/URL segments to rendered pages. I'm guessing most don't need that stuff, and it adds a decent chunk of code, so I left that out. But there are a couple of optional additions that I would recommend adding in a lot of cases: Forcing pages to only render from their "href" URL and not their ProcessWire URL (optional) Our existing hooks ensure that any URLs output for pages having "href" values are always based on that "href" value. But what if someone accesses a given page at its ProcessWire path/url? The page would render normally. We might want to prevent that from happening, ensuring that it only ever renders at its defined "href" URL instead. If the page is requested at its ProcessWire URL, then we redirect to its "href" URL. We can do that by adding the following code to our /site/templates/_init.php file: if($page->id > 1) { // ensure that we are rendering from the 'href' URL // rather than native PW url, when href is populated $href = $page->get('href'); $requestUrl = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''; if($href && $requestUrl && strpos($requestUrl, $href) === false) { // href value is not present in request URL, so redirect to it $session->redirect($config->urls->root . ltrim($href, '/')); } } Enforcing uniqueness and slashes in "href" values (optional) It's worthwhile to enforce our "href" values being consistent in having both a leading and trailing slash, as well as making sure they are always unique, so no two pages can have the same "href" value. To do that, I added this hook to my /site/ready.php file (/site/init.php would also work): /** * Ensure that any saved 'href' values are unique and have leading/trailing slashes * */ $wire->addHookAfter('Pages::savePageOrFieldReady', function(HookEvent $event) { $page = $event->arguments(0); /** @var Page $page */ $fieldName = $event->arguments(1); /** @var string $fieldName */ if($fieldName === 'href' || in_array('href', $page->getChanges())) { // the href field is being saved $href = $page->get('href'); if(!strlen($href)) return; // make sure value has leading and trailing slashes if(strpos($href, '/') === 0 && substr($href, -1) === '/') { // already has slashes } else { $href = '/' . trim($href, '/') . '/'; $page->set('href', $href); } // make sure that the 'href' value is unique $pages = $event->object; /** @var Pages $pages */ $p = $pages->get("id!=$page->id, href=$href"); if($p->id && !$p->isTrash()) { $page->set('href', ''); $page->error( "Path of '$href' is already in use by page $p->id “{$p->title}” - " . "Please enter a different “href” path and save again" ); } } }); That's all for this week. Thanks for reading, have a great weekend!
    3 points
  2. Hey and thanks for the interest. I am head of community at CrowdSec and I had never heard about ProcessWire before this, so thanks for pointing me to it. I haven't heard of anyone working on a bouncer (or parsers or scenarios) for ProcessWire. I won't rule out that anyone is, but I doubt it. Could I ask what motivates you to specifically ask for a bouncer? I would say that it probably also would make sense to have a parser that's able to parse PW logs and a scenario able to detect attacks such at bruteforce access to the PW backend (assuming that there is one) for instance. Both the Wordpress and Magento bouncer is really a specialized version of the generic PHP bouncer. Here's an article on how to use it with Drupal but in reality it works with anything PHP. If more people are interested in PW support it would be a great idea to join our Discord and talk about it there. I hope this was to any help. If I can do more please let me know.
    3 points
  3. We recently launched DOMiD Labs, a new microsite for our existing client DOMiD. We already built the main site for DOMiD in 2019, as shown in our previous post here and featured in ProcessWire Weekly #296. The new DOMiD Labs site belongs to a project to plan participative museum concepts for the upcoming migration museum in Cologne, Germany. Concept, design and implementation by schwarzdesign - web agency in Cologne, Germany. Featured in ProcessWire Weekly #433 We're using an in-house starter project to bootstrap new ProcessWire projects. This allows us to deliver smaller projects on a low budget while still providing an extensive, modular content system and to spend some time polishing the features our clients care about. Notable features of the site include: A modular content builder utilizing the excellent Repeater Matrix field. A blog area with a paginated news index page. Multi-language support (site available in English and German). Q&A sections using accordion elements. A contact form built with the Form Builder module. Fully configurable navigation and footer components. Custom translation management for interface translations. Learn more: domidlabs.de
    1 point
  4. I've got a scenario where I use ProcessWire as a CRM for a client and they have several companies using this now, so it's important to be able to update things easily across all installations. A tricky one recently was working out how to manage translations via API to avoid going into several installations, enabling language support and translating the file manually (in this case they wanted to tweak a message in LRP's confirmation routine). On your master installation, do your translation as normal Export to CSV Upload the CSV file somewhere where your updater code can reference it - I created a folder in site/assets called "uploads" for some CRM-specific stuff, so I added another folder under there for ' Use code similar to the below in your updater script on other installations to first create the JSON file to be translated (it may not already exist) and then import your CSV file and tidy up afterwards - something like the below: <?php $this->modules->refresh(); // I've found this helps first, mostly for modules that have just been pushed to the Git repo but doesn't hurt to do it every time $this->modules->install('LanguageSupport'); // This wasn't yet installed $languages = $this->wire()->languages; $default = $languages->get('default'); // We only use the default language as it's only for the UK market $default->of(false); // It will complain without outputformatting switched off under some scenarios $translator = new LanguageTranslator($default); $translator->addFileToTranslate('site/modules/LoginRegisterPro/LoginRegisterProConfirm.php'); // This generates the JSON file that you would see in PW on the translations page $languages->importTranslationsFile($default, $this->config->paths->assets . 'uploads/imports/default-site.csv'); // This imports the CSV we exported from our master installation unlink($this->config->paths->assets . 'uploads/imports/default-site.csv'); // And we don't need the file afterwards I appreciate it's not a particularly in-depth tutorial, but may help someone out there with a similar need. Our CRM installations are all on an Amazon EC2 instance under separate subdomains, so separate databases too and there's a shell script I use to iterate all the installation folders, get the latest updates from the Github repo and then run an updater script that will do more complex updates like the above code or perhaps alter tables etc. This may get trickier in future if we shift to using something like Docker, or that may make life easier, but at the moment this is all nicely manageable the way it is.
    1 point
  5. I might do that, but I am still not sure what's the best way to implement this. I made changes to the PagesExportImport Module, but I think it would be better to implement this in the core PagesExportImport Class. That would make the feature available to the API and other modules as well. But I am not sure how to do that, because to import the meta data, the pages must be created first, so the code might only work in the module, after the pages are created. EDIT: I made a PR
    1 point
  6. @bernhard Including the default translations in the code has also fallen a bit out of favour for me, for that exact reason. Since we're using snake-case message IDs (e.g. contact_form_submit_label instead of Submit), it's nice to see the actual translation directly in the code alongside the ID. But if those get changed in the table field, it gets confusing indeed. Usually the message IDs are enough to understand what the message is intended for (and if not, this can be solved by writing better message IDs). Lately I don't include the default translations in the code as it's just as easy to add them in the table field after. I still like that the empty row with the message ID is created whenever a new translation is added, this makes it easy to add a bunch of translatable strings in the code and add the translations later in bulk. Absolutely, this is why I always prefer message IDs over writing translations directly in a fixed source language. This has the nice benefit that you can differentiate between contexts using the message ID - for example, contact_form_submit_label is different from order_form_submit_label. This means you don't have to built context awareness into your custom translation system, making it much easier to implement.
    1 point
  7. Hi @alexm, Padloper Import API is now ready in Padloper 002 announced here. Here are the Import API docs.
    1 point
  8. This thread is for me to announce Padloper releases (until I find a better way to do it). Please use your download link (sent to your email) to get the latest Padloper version. Please don't post your bug reports on this thread. Instead, create a new thread for that in this support forum. Thanks. Releases Release Sunday 12 June 2022: Padloper 002 Features Add Import API (currently for product-related imports only). This allows you to import items (attributes, categories, products, generate variants from scratch, etc) into your padloper shop. Please see the Import API documentation. Rest of the World Shipping Zone is now fully functional. Configurable dynamically loaded product variants. This is useful for cases where a product contains lots of variants. Editing such a product will slow down the browser considerable as all children pages (variants) will be loaded as well. Bug Fixes Please see the bug fixes prior to today's release date referenced in the bugs and fixes thread. --- Release Thursday 14 July 2022: Padloper 003 Features Add Addons Feature. This allows you to extend the functionality of your shop by developing, adding and activating addons (mini apps). The forum announcement about this feature is here. Please see the Addons documentation and the related Addons API documentation. A short demo of the feature is available here. Bug Fixes Please see the bug fixes for today's release date referenced in the bugs and fixes thread. Release Sunday 24 July 2022: Padloper 004 Features Update FieldtypePadloperOrder schema to include details about selected shipping rate name and delivery times. Amend single order view GUI to show order shipping rate name and delivery times. See the request details here. Make a number of methods hookable - for extra validation of custom form inputs and custom determination of whether to apply EU digital goods tax. See the request details here. Release Tuesday 30 August 2022: Padloper 005 Features Amend inventory GUI to allow non-editing of locked variants (similar to products without variants). Bug Fixes Please see the bug fixes for today's release date referenced in the bugs and fixes thread. Release Friday 23 September 2022: Padloper 006 Features Make PadloperProcessOrder::getOrderCustomer hookable. This has various uses, e.g. allows to initially pre-fill customer details to the order checkout form during order confirmation. Can be used, for instance, in conjunction with LoginRegister module. See the request details here. Demo code is available here. Bug Fixes Please see the bug fixes for today's release date referenced in the bugs and fixes thread. Release Tuesday 27 September 2022: Padloper 007 Features/Enhancements Don' t show product properties settings in General Settings > Standards Tab if product property feature was not installed. Bug Fixes Please see the bug fixes for today's release date referenced in the bugs and fixes thread. Release Sunday 08 January 2023: Padloper 008 Features/Enhancements Fix bugs related to the demo starter projects. Bug Fixes Please see the bug fixes for today's release referenced in the bugs and fixes thread.
    1 point
  9. Hi @alexm, Just a quick update that I have made very good progress with the 'add' API. I hope to release this by the end of the week.
    1 point
  10. Hi @alexm, Lazy loading (htmx-ajax) of product variants is now implemented. I have not yet implemented pagination. Still having a think about that. To set the threshold for dynamic loading of product variants please see the Details Tab of the field 'padloper_runtime_markup'. Settings are: -1: No lazy (ajax) loading. 0: Always lazy (ajax) load. > 0: Lazy (ajax) load if the quantity of the product variants for the product being edited exceed specified threshold. Thanks.
    1 point
  11. Hi, this is a textformatter-module to globally set all kinds of YouTube and Vimeo options to embedded videos. It works well with TextformatterVideoEmbed and I think also with TextformatterOEmbed - https://github.com/blynx/TextformatterVideoEmbedOptions ... this is my first module - so I hope the code is acceptable ... ; ) What do you think about the way I implemented the configuration? I Seperated all the config and defaults into a JSON file and generated all fields automatically ... I was wondering if this was already possible or a good idea at all here: https://processwire.com/talk/topic/11155-create-inputfields-from-json-array/ Looking forward hearing your feedback! Cheers, Steffen
    1 point
×
×
  • Create New...