Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation since 06/02/2022 in all areas

  1. This week core updates are focused on resolving issue reports. Nearly all of the 10 commits this week resolve one issue or another. Though all are minor, so I'm not bumping the version number just yet, as I'd like to get a little more in the core before bumping the version to 3.0.202. This week I've also continued development on this WP-to-PW site conversion. On this site hundreds of pages are used to represent certain types of vacations, each with a lot of details and fields. Several pages in the site let you list, search and filter these things. When rendering a list of these (which a lot of pages do), it might list 10, 20, 100 or more of them at once on a page (which is to say, there can be a few, or there can be a lot). Each of the items has a lot of markup, compiled from about a dozen fields in each list item. They are kind of expensive to render in terms of time, so caching comes to mind. These pages aren't good candidates for full-page caches (like ProCache, etc.) since they will typically be unique according to a user's query and filters. So using the $cache API var seems like an obvious choice (or MarkupCache). But I didn't really want to spawn a new DB query for each item (as there might be hundreds), plus I had a specific need for when the cache should be reset — I needed it to re-create the cache for each rendered item whenever the cache for it was older than the last modified date of the page it represents. There's a really simple way to do this and it makes a huge difference in performance (for this case at least). Here's a quick recipe for how to make this sort of rendering very fast. But first, let's take a look at the uncached version: // find items matching query $items = $pages->find('...'); // iterate and render each item foreach($items as $item) { echo " <!-- expensive to render markup here ---> <div class='item'> <a href='$item->url'>$item->title</a> ...and so on... </div> "; } That looks simple, but what you don't see is everything that goes in the <div class="item">...</div> which is a lot more than what you see here. (If we were to take the above code literally, just outputting url and title, then there would be little point in caching.) But within each .item element more than a dozen fields are being accessed and output, including images, repeatable items, etc. It takes some time to render. When there's 100 or more of them to render at once, it literally takes 5 seconds. But after adding caching to it, now the same thing takes under 100 milliseconds. Here's the same code as above, but hundreds of times faster, thanks to a little caching: // determine where we want to store cache files for each list item $cachePath = $config->paths->cache . 'my-list-items/'; if(!is_dir($cachePath)) wireMkdir($cachePath); // find items matching query $items = $pages->find('...'); // iterate and render each item foreach($items as $item) { $file = $cachePath . "$item->id.html"; // item's cache file if(file_exists($file) && filemtime($file) > $page->modified) { // cache is newer than page's last mod time, so use the cache echo file_get_contents($file); continue; } $out = " <!-- expensive to render markup here ---> <div class='item'> <a href='$item->url'>$item->title</a> ...and so on... </div> "; echo $out; // save item to cache file so we can use it next time file_put_contents($file, $out, LOCK_EX); } This is a kind of cache that rarely needs to be completely cleared because each item in the cache stays consistent with the modification time of the page it represents. But at least during development, we'll need to occasionally clear all of the items in the cache when we make changes to the markup used for each item. So it's good to have a simple option to clear the cache. In this case, I just have it display a "clear cache" link before or after the list, and it only appears to the superuser: if($user->isSuperuser()) { if($input->get('clear')) wireRmdir($cachePath, true); echo "<a href='./?clear=1'>Clear cache</a>"; } I found this simple cache solution to be very effective and efficient on this site. I'll probably add a file() method to the $cache API var that does the same thing. But as you can see, it's hardly necessary since this sort of cache can be implemented so easily on its own, just using plain PHP file functions. Give it a try sometime if you haven't already. Thanks for reading and have a great weekend!
    29 points
  2. The ProcessPageList module now has a configuration setting where you can select pages that should not be shown in the page list. For example, maybe once you've set up your 404 page, you don't really need it to display there anymore, except maybe in debug mode. Or maybe you don't ever need the "Admin" page to display in the page list. This new feature lets you do that, and for any page that you decide. Next, a "Usage" fields has been added to the "Basics" tab in the Field editor, just like the one in the Template editor (requested feature #445). This shows you what fields are using the template. It's essentially the same information that you'll find in the "Actions > Add or remove from templates" feature, but easier to find, especially for people newer to PW. That's all for this week, I hope you have a great weekend!
    27 points
  3. 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!
    20 points
  4. When we released ProcessWire 3.0.200 one of the biggest differences from prior master/main versions was that PW now only includes 1 profile: the site-blank profile. This means there's probably going to be more people starting with it than before. Yet it is so minimal that I thought it deserved a bit of a walkthrough in how you take it from nearly-nothing to something you can start to build a site around. Everyone will have a little bit different approach here, which is one reason why the blank profile is a useful starting point. In this new post, here are some steps you might take when starting a new site with the blank profile— https://processwire.com/blog/posts/starting-with-the-blank-profile/
    20 points
  5. https://www.autohaus-bendel.at/ Highlights/Features: New Logo and matching Backend using module AdminStyleRock Very good google- and performance-ratings thx to UIkit and ProCache Flexible content pages (using private module RockMatrix) Cars are fetched automatically from an austrian cardealer API and converted into ProcessWire pages with a custom filter page: https://www.autohaus-bendel.at/fahrzeuge/ Forms with honeypots and live validation (using private module RockForms based on nette forms) Web-Coach The client gets automated reminders if he does not add new content to the website. Thx @wbmnfktr Bendel Web Coach The last news entry was created 21 days ago. There must be something new that will interest your clients ;) > To the website > Create news entry Animated page transitions using barba.js Migrations and deployment using RockMigrations. Debugging using TracyDebugger. 😎
    17 points
  6. ProcessWire's core comes with a lot of helpful debugging tools and capabilities built-in. In this post we'll cover some of these useful tools and how to use them— https://processwire.com/blog/posts/debugging-tools-built-in/
    10 points
  7. Quickstart: Install the module Optional: Set custom language mappings Enjoy two-way language file syncing From PW Weekly: This week we're happy to introduce a brand new module from Bernhard Baumrock, called RockLanguage. As the name suggests, RockLanguage is a tool for dealing with translations, and more specifically with translations related to ProcessWire modules. While ProcessWire natively ships with extensive language support, including the ability to ship module translations with the module itself, this does still require some manual work. That is exactly what RockLanguage aims to solve. Here's what the module translation process looks like with RockLanguage: Install RockLanguage on your site and (optionally) configure custom language code mapping via the module configuration screen. Add a directory for the language you'd like to include translations for within your own module's directory, e.g. /site/modules/MyModule/RockLanguage/FI/ for the Finnish language. Translate your module for said language via ProcessWire's translations manager. RockLanguage will automatically notice the update and duplicate the translation file from its original source to the directory you've just created. Now if you install this module to another site with the language folder included, and the site has RockLanguage installed and Finnish as one of its languages, the translation files for your module will be automatically synced with ProcessWire. What's nice about this workflow is that it takes some manual steps out of the equation, thus streamlining translation management. It's too early to say how widely this module will be adopted among public third party modules, but if you like the concept, you can easily start using it for your own modules right away. Download + Docs: https://github.com/baumrock/rocklanguage https://processwire.com/modules/rock-language/ Feedback: What do you think of this concept? Will you use the module? Should the concept be part of the core instead? Could there be any problems I didn't think of...? Donations: 😎🤗👍
    10 points
  8. 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. 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 (not yet online). Multi-language support (English site yet to follow). 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
    9 points
  9. I've also been using this great module on another WP conversion project to do the same thing: https://processwire.com/modules/custom-paths/ I'm not 100% sure if it checks for uniqueness (probably) but since everything's being imported by a script I wrote I know they are unique for now.
    8 points
  10. I am still working on this one. It works great and is quite flexible now. I made a couple of improvements. Now the data to style the items is stored directly on the item with $page->meta(). This way, cloning pages and page reference fields work out of the box. Also there is no need for hidden fields anymore. The style panel now supports adding custom classes and assigning styles to them. These classes can be used globally on all pages (a css class is also a page). The style panel now supports selecting html tags to style tags globally across the whole site. I did a lot of refactoring to make the code cleaner and more efficient. There are still things where I am looking for feedback before releasing an open beta for this module: Sharing blocks and migrating pages I played with the core ProcessPagesExportImport and the migrator module and it seems that it's not exporting the meta() data, which is a bummer. So for now I might not be able to have a migration feature out of the box. Maybe someone else can take a look at this, but it seems to be too much hassle for me now. Importing/exporting the whole database with ProcessDatabaseBackups works as expected though. Also I did develop some block modules that you can install optionally, these will create the needed templates and fields for that block and give you a quick way to start working with PageGrid (without the need to create your own templates or install a site profile), more on that later. (Item pages are based on a block template). Structure of PageGrid items/pages I decided to always store the items under the admin page in a seperate parent, instead of making the parent customisable (like PageTable is doing). So there is a clear separation between pages for your site and item pages. PageGrid then creates a structure looking like this (makes it easy to access the pages): Admin – PageGrid Items – Home items (name: for-1213) – Item Editor – Item XY – Another Page items (name: for-1315) – Item Group – Item XY – Item XY – Item Editor The structure of these pages is an exact representation of the PageGrid items/pages (sorting, child/parent relation, hidden/published). While working with PageGrid via the UI this is not so important, but I think it will make interacting with the item pages via API more flexible and clean. Adding a page to any of the item pages via API or the backend also adds the page to PageGrid. If you delete a page containing a PageGrid field, the items also get deleted. You can also sort via API (or manually under the Admin page) and the changes are reflected for the items as well. The connection is via a name like for-1213, where 1213 is the ID of the page holding the PageGrid field (inspired by repeaters). I like this setup because it gives you a lot of options to access items via API and keeps the pagetree clean. Also it will make it easier to handle migrations for pages containing a PageGrid field in the future (just import/export the page and the item page container). One downside, though, is that the methods of PageTable like $page->pt->add($newpage) or $page->pt->remove($newpage) are no longer working (PageTable is using a PageArray that is saved as the field value). Also this will not work with multiple PagGrid fields on one page (A PageGrid field can however have a page reference field to another page containing a PageGrid field, as well as having nested pages, so I see no benefit of supporting multiple fields). You can add a page to the item parent like this: // Add an PageGrid item via API $itemParent = $pages->get("for-$page->id"); $itemParent->add($newpage) I am not decided if my approach is better or if I should just stick with the PageTable methods (keeping the items stored as PageArray inside the field). Since PageTable can only have one parent that you select in the field settings, all items will than be stored under the same parent, which is a bit messy. Not sure if any of that makes sense to you 🙂 Any feedback is welcome. More info and website is coming soon!
    7 points
  11. Hey folks, so in a project I'm working on there are 750+ news articles in WP that are image-heavy in recent years. WP has a habit of storing the original images full-size - well, ProcessWire does too but I always set max dimensions on the image fields to resize during upload to prevent 2mb+ images on my disk. This had resulted in an uploads folder some 25GB in size from 2003-2022. Disk space is, fortunately, cheap nowadays, however I soon realized after mulling it over with Ryan that the easiest and most sane option for importing all those articles was to scrape in the post content HTML into a PW CKEditor field (thanks SimpleHTMLDOM for making this so simple!) but leave the images alone - WP had already done resizes for various lightboxes and galleries so it was just the original, huge images I had to contend with. The solution - on my own copy of the WP uploads dir - was to run this command and watch it go: find ./ -type f \( -iname \*.jpg -o -iname \*.jpeg -o -iname \*.png \) -exec mogrify -quality 90 -verbose -resize 1600x1600\> {} \; It's basically searching all files and subfolders for .jpg, .jpeg and .png files and resizing to no more than 1600px landscape/portrait whilst preserving aspect ratio (no cropping) and 90% image quality. I chose those sizes as the various links in galleries and lightboxes were loading the original file, not one of the WP resized ones, so now we can be fairly sure that we're loading <500kb of image maximum instead of sometimes 10mb+ (some folks have really nice cameras 🙂 ). I'm sure @horst can probably tell me if that command could be better. I think mogrify is usually for a batch of files for example, whereas the find command is serving them up individually so convert might be more appropriate than mogrify, but this command iterates through the files quickly and only resizes which ones it needs to and is happily chugging away right now so I'm happy with it. One thing to note is that any resizing can be a bit CPU-heavy, so if you have the chance to do this on a local server first or out of "normal" hours for site visitors then that's recommended as the server may slow down for the duration.
    7 points
  12. Great! Is there a chance to add hiding pages (and their children) by template or by custom selector. That would make the new feature more complete and useful IMHO.
    7 points
  13. 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.
    6 points
  14. Hey @Thromisios welcome to the forum 🙂 In my opinion: ProcessWire is perfect for this scenario! Why I think that: ProcessWire has a very active and friendly community that will always be there to help (that's very important when learning something new). I used other systems before ProcessWire and in their forum you never knew if you would get an answer or not. Terrible. In the PW forum you'll almost instantly get great answers or even ready to use code snippets or even modules 🙂 ProcessWire hides a lot of complexity in the beginning. That makes it easy to use and you can come very far by using a few very basic concepts. Often it's just using fields together with the API. On the other hand ProcessWire is capable of so much more. That's all hidden under the hood and once you grow, you can start exploring more and more of it's features. In other systems you'll have to learn a lot upfront just to get simple things done. For example with other PHP frameworks you'd have to build the admin interface for your users completely from scratch (not all systems of course, but there are many where you have to do so). Hooks are such a brilliant concept that not every system has. Most of the time when you want a small customization of your system/program/website that means a small snippet of code. In other systems you often need to understand the whole OOP model to extend the correct class or you need to build a plugin or an extension. In ProcessWire you hook into the correct spot (if you don't find that the forum will help) and you add some simple lines of PHP and you get what you want. We have Adrian's great TracyDebugger which is such a huge help while developing. Just a simple dump of a variable is so much more helpful than doing a var_dump(). Other systems might have similar tools, but there are for sure systems that don't have anything like that. For me it was an eye opener in many cases. For the last part you can head over to my signature link "afraid of hooks?" A simple example could be: <?php // send me an email when the client adds a new blog post $wire->addHookAfter("Pages::added", function($event) { $page = $event->arguments(0); // only execute this hook for pages of type "blog-post" if($page->template != 'blog-post') return; // send mail $mail = new WireMail(); $mail->to('your@mail.com'); $mail->from('robot@yoursite.com'); $mail->subject('New blog post at yoursite.com'); $mail->bodyHTML("Url of the new post: ".$page->httpUrl()); $mail->send(); }); Good luck and have fun 🙂
    6 points
  15. I created a simple translation module for a project we're currently working on and I thought I would share it in case it's useful for anyone else as it's a request we've come across a few times recently. It's a basic page translation module using Google Translate to replace their now retired embeddable page translation widget. The module outputs a simple, un-styled, dropdown language selector that routes the visitor to a translated version of the current page via translate.google.com. From there you can then navigate the whole site in the your language of choice. There's also a method to output a single language-specific translation URL for any supported language using the language ISO code. Download, usage and supported language list available on my Github here: https://github.com/mrjcgoodwin/MarkupGoogleTranslate Caveat 1: Google seem to be trying to encourage use of their Google Translation API, so the continued working of this module is completely at their mercy - I can imagine they may discourage this usage at some point, but who knows! Caveat 2: I haven't yet deciphered what all the parameters are used for in the constructed Google Translate URL. It's possible there may be some sites/scenarios where the URL doesn't work. Feel free to report if you find any issues.
    5 points
  16. @joe_g No, you can specify this for each template if you want to. Edit a template and see the "files" tab. Or you could just use your _main.php as a controller that loads different layout files depending on some field value or condition that you determine. Sure you can. Use PHP include(), $files->include() or $files->render() with whatever files or order you want. Regions can be replaced, appended, prepended, removed, etc. as many times as they are output. You can do it individually for templates, see my response for #1. Though I usually structure my templates in a way that the _main.php is the "base" that's generic enough to not have ever needed to define alternate files. I think markup regions are the best way myself. Once you start using them a lot, I suspect you'll find you don't want to use anything else. That's been my experience at least. I don't know exactly what you mean by nested regions. You'd have to provide more details on what you are doing before we could say if you are using something in an intended way or not.
    5 points
  17. 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.
    5 points
  18. That's a good Blog post! I really want to read a part two, (and maybe three?). 🙂 Also very useful for new starters, I believe.
    5 points
  19. Hi, I know quite a few of us are working on multi-language websites and are most likely using the `strftime`, however it's deprecated as of 8.1.0 and set to be removed in 9.0. There's some time left but on my local environment using MAMP and 8.0.8, it's as if it's already gone as dates were not localized anymore. Looking for options, I came accross this which might be useful to others: https://gist.github.com/bohwaz/42fc223031e2b2dd2585aab159a20f30 What's your take on this ? Also I wonder how things will be handled in PW's core as the WireDateTime is using that function as well.
    5 points
  20. When you enable device mode in the dev tools the user agent will change and therefore the fingerprint will change. I solve this when developing locally with the following in config.php: if(in_array($_SERVER['REMOTE_ADDR'], ['127.0.0.1', '::1'])) { $config->sessionFingerprint = false; } If you're working on a remote site and don't want to change the fingerprint settings then you can use an incognito window when viewing the front-end in device mode.
    5 points
  21. Hi @Thromisios Welcome to the PorcessWire community. I came from non coder background, too. I first started learning ProcessWire basics by following this tutorial. Gideon
    5 points
  22. Very nice addition, thanks Ryan! 🙂 This is actually one of the features of a general purpose utility module we install on all our client sites, so will definitely be using this option in the future.
    5 points
  23. Hi folks, I just released version 1.1.1 which fixes the PHP 8.1 related errors from this issue: https://github.com/wanze/SeoMaestro/issues/41 These issues have been fixed by merging a pull request. I appreciate if the community can help me out, as I am quite disconnected from the ProcessWire world (as you might have noticed). However, I still want to support this module with bugfix releases. Cheers
    5 points
  24. I have worked with a few multilanguage sites in ProcessWire and always found it so easy. We recently took on a WordPress site with multilanguage and wow, what a difference. I appreciate its all about the plugins that were chosen, but the plugin used is in the top 2 and it is so clunky. An entry for every language, so you have to upload the same images for every language just to change the text (I know there will be better ways to do this when you dig deep, but I am talking out of the box). Well done @ryan, when the next multilanguage site comes up I will always be confident in what we use.
    5 points
  25. https://processwire.com/store/pro-drafts/
    4 points
  26. I looked at my HTML output today and all this chaotic whitespace triggered my OCD. This module simply hooks into Page::render and removes whitespaces from frontend HTML with a simple regex replace. I mostly put this together for cosmetics. I like my View source neat and tidy. This is not well tested yet, please open an issue if you run into problems. GitHub: https://github.com/timohausmann/MinifyPageRender
    4 points
  27. I had a look at it and it wasn‘t that complicated to implement. I have now a version of the ProcessPagesExportImport module that support export/import of $page->meta(): https://github.com/jploch/ProcessPagesExportImport would be nice to have this in the core
    4 points
  28. In general I'd say no, that's not a bad practice. Pages are built for storing data and they are built to scale. And they are built to work with all the nice things we have in PW world that are a pain to build with plain SQL database structures (like children/siblings/parent references and page reference fields in general).
    4 points
  29. Data is fetched via cron every night and then PW pages are created so that the client can enrich those pages with data that is not part of the API but is helpful on the website (eg a PDF sheet for the car or the point of contact): API-Fields are read-only. If a car is created by the cron and the point of contact is empty, the client gets a link to directly edit this page and select the POC 🙂 If cars are sold, the cron automatically trashes those pages on the website. Ah... I forgot a nice detail! They have a custom branded PDF viewer:
    4 points
  30. 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.
    4 points
  31. Even though I have been using ProcessWire for 7 years, starting a fresh install with the blank profile showed me a few new things. Good work, part 2 would be great.
    4 points
  32. Part two with "Adding in relevant 3rd party or Pro modules" would certainly help newcomers as well, in addition to "Optimizing settings in /site/config.php".
    4 points
  33. Alright, i think i've figured it out. The location of the layout file is wrong. According to the docs it's supposed to go into "templates/views/layouts", but according to the boilerplate it's actually in "templates/layouts". Putting it in there makes it work. Didn't expect that.
    4 points
  34. Hello @Thromisios First, I would refer you to the ProcessWire documentation - https://processwire.com/docs/start/ And then, look through the sections of the forum: https://processwire.com/talk/forum/8-getting-started/ https://processwire.com/talk/forum/13-tutorials/ https://processwire.com/talk/forum/6-faqs/ Next, I would look at the sections of the forum and documentation that raise more questions for me. And of course https://www.php.net/ when you don't understand how PHP works. Good luck with your adventures.
    4 points
  35. You can learn from studying the Helloworld module: https://processwire.com/modules/helloworld/ From the readme:
    4 points
  36. A week ago the new website of the wuppermann group went online. The Wupperman group is a EU-wide operating company with several locations in different countries. Their portfolio is all about steel fabrication. This includes flat producs and tubes & profiles. The technical production is developed by me, Olaf Gleba. The grafic design is supplied by C&G: Strategische Kommunikation GmbH. Homepage: https://www.wuppermann.com Some Impressions: (Secured) Shareholder portal, only available in german language Former screens deleted on behalf of the client. Technical notes: 1. All contents are populated by provided (i name them) content modules (e.g. Repeater Matrix Types) which gets the client what he needs and either prevent him from doing weird stuff. In nearly all textareas formatting is limited to a absolute minimum. For example, image insertion in CKEditor is generally prohibited. Instead there are dedicated fields for modules which holds media content. 2. This and that.. - vCards are build on the fly with a admin hook on page save. - PrivacyWire as CCM (just a few cookies to handle Matomo and external movie content) - Uses the SearchEngine Module to handle (multilanguage) site search - Email Obfuscation Module for frontend e-mail addresses - Wire Mail Smtp to deliver automated e-mails - Multilingual (currently german, english). Hungerian, polish, dutch following. - Ajax driven content (for example on the contact page) - Heavy use of Fieldtype AssistedURL (Fork by @adrian) to provide language dependend, local file linking (fieldname_[de|en] approach) - Login area (shareholder portal) with secured file downloads ($config->pagefileSecure = true) - Email New User, Admin Action (create users batcher), Force Password Change for functionality like adding new users with specific roles, Password reset, Change Passwort a.s.o. - Distribution of concatenate/minified css and javascript is cachebusted (happens within my developement environment,- no modules (like AIOM etc.) involved). - Thanks to @ryan* all images are delivered in WEBP format (with fallback). *) s. https://github.com/processwire/processwire-issues/issues/1497 - The site uses a bunch of modules provided by the ProFields Package (for example the Combo, Repeater Matrix and Table Fieldtypes).
    4 points
  37. @bernhard Admittedly, I had never really paid attention to php://input, so this thread prompted me to google it. As I understand it, that stream always contains the raw request body, so anything could be in there, however only two content-types end up in $_POST and thus also in PW’s $input->post: application/x-www-form-urlencoded and multipart/form-data. For these, in addition to sorting everything into an associative array, PHP also takes care of unescaping the URL encoding. When OP wants to send Json, I’m assuming he quite sensibly declares application/json. PHP doesn’t know this content-type (plus, the whole thing is a nameless Json object, so what would be the key for $_POST’s associative array?), so nothing happens automagically. This answer on SO gives a lengthy overview on how to handle php://input. If you’re asking about the client-side fetch() API, I have to confess I didn’t test the code I posted, but the idea is to create a request with the content-types application/x-www-form-urlencoded or multipart/form-data mentioned above. Indeed, for some reason I thought it would be necessary to specify the former content-type, but it’s actually set automatically when URLSearchParams are used for the request body (at least in Firefox). Likewise, when the body is of type FormData, the default content-type will be multipart/form-data. As for when to send a body, it’s just the HTTP request’s content, so I guess whenever you want to transmit data to the server, i.e. usually with POST (or PUT or something). When dealing with third party APIs you gotta do the weird things they want you to, of course. If it’s your own PHP/ProcessWire site you send requests to, I’d say it doesn’t really matter whether it’s application/x-www-form-urlencoded or multipart/form-data (or URLSearchParams or FormData for that matter), since both deliver keys and values that end up in $_POST/$input->post. In this case I would just use whatever is more convenient to assemble in JS, i. e. if the data actually comes from a HTML form, you can pass the form element to FormData’s constructor and you’re done. Sorry if I’m not telling you anything new here 😅 Trial and error makes up a large chunk of my modus operandi as well m)
    3 points
  38. New search engine on the block .... will their business model work ? yep.com ******************
    3 points
  39. PolishWire - very nice 😂👍
    3 points
  40. @bernhard I use redis alongside PW on some client sites (using php-resque for background jobs like email composition and send) - though I do not use this particular module in production yet. I think bitpoet is aiming for an API-compatible drop-in for wirecache that leverages the lower latency of redis' in-memory accesses.
    3 points
  41. Hi @Ivan Gretsky, I will at one point for sure, but that will be late in the year. I‘m currently hiking across the U.S. and won‘t get my hands on a computer until October.
    3 points
  42. True, and that need was satisfied 10 years ago 🙂 You can set (almost) every field's label and description in template context:
    3 points
  43. Just a quick one ... This is two questions in one. Integration: Probably not the answer you are looking for but literally any JavaScript library can be integrated into the PW backend. There are various options here including a dedicated ProcessModule or an Inputfield (e.g. a runtime one). Update data in real time. Any JavaScript and newer libs like HTMX can easily do this. The bottom line is this: (a) An action happens on the client (b) JS listens to that action/event (c) optionally but ideally, check if the action is valid (e.g. was this field completed) (d) send the info to the backend - usually via ajax for the type of app you are building (e) the backend processes the action you have sent including validation + sanitization. If these pass, it takes an action, e.g. 'create a page' or 'update a field' (f) the server sends the response back to the client which is listening for a response from the ajax request. Traditionally/usually, the response is JSON but with HTMX, the response is HTML. (g) The client handles the response, in many cases, updating the DOM. That's it. Yes. Media Manager, for instance. You drop files and it will upload the files then create media pages off of that. Yes. ProcessWire itself to be honest. Basically $input, $sanitizer, $config->ajax and $pages are the usual tools. Not a reuse per se answer. Listening to drag and drop using JavaScript used to be a chore. With modern browsers, APIs exist to do it easily in vanilla JavaScript. If you wish to get a ready made solution searching for JavaScript drag and drop will yield some results. Personally, I'd go for htmx + vanilla JS drag and drop, or htmx + sortable combo (here's a Python example).
    3 points
  44. More than a year ago I said I would try TailwindCSS... now it finally happened and... and the result is disastrous! Well... disastrous for my opinion back then and my thoughts about it until a few weeks back. I am quite surprised and happy with the result, to be honest. I did a rebuild of an ongoing side-project, with a full rebuild of the whole site structure all the way through to any breakpoints (CSS only, HTML was kept except additional classes). Yet some parts are still outside of TailwindCSS as those are really custom and are way better placed in an additional CSS file. Even though you can customize TailwindCSS to almost all of your needs but I didn't want to bother with it for now. The change in file size is quite dramatic... or even super awesome. However you want to see it. I went from... 59kb (the whole project) down to 17kb (structure - based on TailwindCSS CLI generated .css file) PLUS 5kb custom CSS for colors, custom properties PLUS 10kb custom CSS for icons, backgrounds, and fonts PLUS unknown size of additional kb within HTML due to those TailwindCSS classes Ignore those other files as those are helpers or part of other instances within that setup. Which is in total: 32kb for the full project, all pages, all icons, all fonts. Yet... the whole setup isn't ready for production right now as my setup only really works with 11ty as a middle-man for now and not within my PW workflows... but that's another story. There are probably even more things I could optimise in terms of fonts, icons and custom classes for colors. But for now... WOW... I always was against those utility frameworks and didn't like them at all. I am and always was a VanillaCSS Purist... didn't ever really like Bootstrap, UIKIT or anything like that, but TailwindCSS could and might have changed my mind. From now on... I might have to think and work different for some or another reason. Right now my test was built on a project I really knew in and out with all its details. Let's see how it works out in a real and new project. Results based on: TailwindCSS 3.1.2 (latest - as of now, 2022-06-14. all file sizes unzipped, without plugins like Typography)
    3 points
  45. You need to get out of habitual thinking when comparing Processwire with Wordpress. From the beginning, Wordpress was never made for 1) websites 2) safety. And so, all through the years tons of spaghetti plugins were made for Wordpress to make things work. Processwire has a completely different Architecture because it is made both a cms and a cmf with safety in mind to make websites and apps without the need for essential plugins.
    3 points
  46. So i tried the other way around and formatted a date using the IntlDateFormatter. This thread helped me find my way: https://stackoverflow.com/questions/71874030/local-language-without-strftime-deprecated-but-with-date So imagine we want to output "today" like this (formatted for german) Mittwoch 8 Juni 2022 This was the old way to to it: echo strftime('%A %e %B %Y', strtotime('now')); Since this will be deprecated in PHP 8.1 and removed in PHP 9 now we have to make use of this instead: $fmt = new IntlDateFormatter('de_DE', IntlDateFormatter::FULL, IntlDateFormatter::FULL ); $fmt->setPattern('EEEE d LLLL yyyy'); echo $fmt->format(strtotime("now")); The only thing that freaked my out about this was the weird formatting pattern. At first sight it makes no sense (to me), but the ICU documentation offers a big list of available options for formatting the date in any way you could imagine: https://unicode-org.github.io/icu/userguide/format_parse/datetime/ If you are looking for a "one liner solution" then this seems to be the right way (https://stackoverflow.com/questions/12038558/php-timestamp-into-datetime) echo IntlDateFormatter::formatObject( new DateTime('@' . strtotime("now")), "EEEE d LLLL yyyy", 'de_DE' ); EDIT: Beware that timezone settings get ignored when you work with timestamps. This might get you into some trouble. My solution was to format the timestamp into a date string: echo IntlDateFormatter::formatObject( new DateTime(date("d.m.Y", $timestamp)), "EEEE d LLLL yyyy", 'de_DE' );
    3 points
  47. It turned 25GB into 11GB by the way. There's still a lot of photos in there 🙂
    3 points
  48. Thx for the quick and detailed answer 🙂 Have you seen the new version? https://github.com/baumrock/rockmigrations#running-migrations What you describe is possible with a single file called /site/migrate.php (similar to /site/ready.php for hooks): https://github.com/baumrock/rockmigrations#quickstart No need for another module, no need for creating separate php files with php class notation for simple migration changes, no need for a CLI. Unless I'm missing something 🙂
    3 points
  49. I found a solution: instead of Wireframe::partial call $this->view->partials->get.
    3 points
  50. I just released a new extension module AppApiPage (waits for approval), which handles the initial steps from my post above completely automatic. You install AppApi and AppApiPage. That makes the /api/page route available and you only have to add the code on top of your template php to add a custom JSON output to your pages. <?php // Check if AppApi is available: if (wire('modules')->isInstalled('AppApi')) { $module = $this->wire('modules')->get('AppApi'); // Check if page was called via AppApi if($module->isApiCall()){ // Output id & name of current page $output = [ 'id' => wire('page')->id, 'name' => wire('page')->name ]; // sendResponse will automatically convert $output to a JSON-string: AppApi::sendResponse(200, $output); } } // Here continue with your HTML-output logic... I hope that this makes it even simpler to add a full-blown JSON api to new and existing pages.
    3 points
×
×
  • Create New...