Jump to content

Search the Community

Showing results for 'find'.

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Forums

  • Welcome to ProcessWire
    • News & Announcements
    • Showcase
    • Wishlist & Roadmap
  • Community Support
    • Getting Started
    • Tutorials
    • FAQs
    • General Support
    • API & Templates
    • Modules/Plugins
    • Themes and Profiles
    • Multi-Language Support
    • Security
    • Jobs
  • Off Topic
    • Pub
    • Dev Talk

Product Groups

  • Form Builder
  • ProFields
  • ProCache
  • ProMailer
  • Login Register Pro
  • ProDrafts
  • ListerPro
  • ProDevTools
  • Likes
  • Custom Development

Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Joined

  • Start

    End


Group


AIM


MSN


Website URL


ICQ


Yahoo


Jabber


Skype


Location


Interests

  1. Last update: June 2024 Current stable version: 1.5.4 Dev version: github dev-branch You can find the module on Github and in the modules directory : https://modules.processwire.com/modules/duplicator/ https://github.com/flydev-fr/Duplicator/archive/master.zip Screenshots / Features Dir and files exclusion CRON job Advanced settings Local and Cloud storage duplicator.mp4
  2. Hi @adrian When uploading and inserting an image into a field (e.g., a TinyMCE "body" field) with this module active, any UTF-8 characters (like Greek letters) in other Textarea fields on the same page (e.g., a plain text "seo_description" field) are unexpectedly converted into HTML entities (e.g., "α" becomes "&alpha;" ) and saved to the database. I asked Gemini to provide a solution. Steps to Reproduce: Create a page with a TinyMCE field (body) and a standard Textarea field (seo_description). Enter UTF-8 characters (e.g., Greek text) into the "seo_description" field and save. Upload and insert an image into the "body" field. Save the page. Check the "seo_description" field or the database; the UTF-8 text has been converted to HTML entities. Root Cause Analysis: The issue stems from the "replaceRteLinks" method, which is triggered to update image URLs after a rename. Scope Logic: The method fetches all fields of type FieldtypeTextarea and FieldtypeTextareaLanguage and processes them using DOMDocument, regardless of whether the field actually contains the image URL. Encoding Handling: DOMDocument::saveHTML() natively converts extended characters into HTML entities. Because it processes unrelated fields, it permanently alters their raw text in the database. Proposed Solution: Updating the "replaceRteLinks" method to strictly check if the field contains the target URL before processing it, and utilizing ProcessWire's native sanitizer to restore entities converted by DOMDocument. Here is the patched method: private function replaceRteLinks($newFilename, $oldFilename) { $textareaFields = $this->wire('fields')->find("type=FieldtypeTextarea|FieldtypeTextareaLanguage"); $fieldsStr = $textareaFields->implode('|', 'name'); $oldRelativeUrl = str_replace($this->wire('config')->paths->root, '', $oldFilename); $oldRelativeUrlSansExt = str_replace(pathinfo($oldFilename, PATHINFO_EXTENSION), '', $oldRelativeUrl); foreach($this->wire('pages')->find("$fieldsStr%=$oldRelativeUrlSansExt, include=all") as $p) { foreach($textareaFields as $taf) { // FIX 1: Only parse the field if it actually contains the image URL. if($p->$taf != '' && strpos((string)$p->$taf, $oldRelativeUrlSansExt) !== false) { // FIX 2: Explicitly declare UTF-8 for the DOMDocument $pagedom = new DOMDocument('1.0', 'UTF-8'); libxml_use_internal_errors(true); // add <cun> as fake root element $pagedom->loadHTML('<?xml encoding="utf-8" ?><cun>' . $p->$taf . '</cun>', LIBXML_HTML_NODEFDTD | LIBXML_HTML_NOIMPLIED | LIBXML_SCHEMA_CREATE); $pagedom = $this->replaceRteLink($pagedom, $newFilename, $oldFilename, 'a', 'href'); $pagedom = $this->replaceRteLink($pagedom, $newFilename, $oldFilename, 'img', 'src'); // remove fake root element $html = str_replace(['<cun>', '</cun>', '<?xml encoding="utf-8" ?>'], '', $pagedom->saveHtml()); // FIX 3: Restore UTF-8 characters converted to entities by DOMDocument $html = $this->wire('sanitizer')->unentities($html); $p->of(false); $p->$taf = $html; libxml_clear_errors(); $p->save($taf); } } } }
  3. ProcessWire Email Obfuscation (EMO) Download | GitHub Email Obfuscation module for email addresses with 64 base crypting. This module finds all plaintext emails and email links from the document and replaces them with span elements including configurable replace text. All the addresses are encoded to 64 base strings and stored in spans data attributes. Then on client side we decode these strings back to their original state. Install Create new 'EmailObfuscation' folder into /site/modules/ and place the content of this repository into the directory. Login to processwire and go to Modules page and click 'Check for new modules'. Find 'EmailObfuscation' and click install. You can make optional configuration changes in the module admin page. Thanks This is a ProcessWire module fork from MODX Evolution plugin emo E-Mail Obfuscation. http://modx.com/extras/package/emoemailobfuscation EDITED ON: 2013-03-03 - Added description. 2020-04-16 - Fixed GitHub links and updated description. Hello all. Just found PW few days ago and it's already looking awesome! Here comes first contribute and some questions for developing it further. There was one existing email obfuscator on reposity that didn't use any crypting for addresses so I decided to do a little test run and port the one that we currenly use with MODX Evo to ProcessWire module. I'd like to make PageAutocomplete like gonfigure option to admin so that one could select and set templates to exclude module action. It looks like autocomplete is tied to pages and since templates are not set to system as pages this is option is no go, am I right?
  4. I wanted a way to chat with my Processwire site and built a Module (PW MCP) and an MCP server to connect into it. It's a private repo at the moment but it can be public if anyone finds it useful. It's basically a way to use the Cursor Chat Ui to query my site, fields, templates and content. Here's part of the readme which explains it better. What Is It? ProcessWire MCP is a bridge between ProcessWire and Cursor IDE (the AI-powered code editor). It lets you query your ProcessWire site's structure and content directly from Cursor's chat interface using natural language. Instead of writing selectors or browsing the admin, you can just ask: "What templates does this site have?" "Show me the fields on the blog-post template" "Search for pages containing 'summer'" "Find all images with 'lake' in the filename" Why I Built It Cursor can see your template files and code in the local directory, but it can't see what's actually in your ProcessWire database — which templates and fields are registered, what pages exist, or what content they contain. With ProcessWire MCP, the AI can: Query the actual database schema (not just parse template files) Look up page content by ID, path, or selector Understand field configurations (types, settings, which templates use them) Search across all text content and find files/images Get RepeaterMatrix content with type labels See file metadata (dimensions, descriptions, URLs) It's the difference between seeing $page->body in code vs. knowing what that page's body actually contains. Architecture Cursor Chat → MCP Server (Node.js) → PHP CLI → ProcessWire API The module consists of: PwMcp — A ProcessWire module with a CLI interface mcp-server — A Node.js server that speaks the Model Context Protocol The CLI can also be used standalone for quick queries from terminal. Available Commands Command Description health Check connection and get site info list-templates List all templates with field counts get-template [name] Get template details and fields list-fields List all fields with types get-field [name] Get field details and usage get-page [id\|path] Get page by ID or path with all field values query-pages [selector] Query pages using PW selectors search [query] Search content across all text fields search-files [query] Search files by name/extension export-schema Export complete site schema Example: Health Check php site/modules/PwMcp/bin/pw-mcp.php health --pretty { "status": "ok", "pwVersion": "3.0.241", "siteName": "www.example.com", "moduleLoaded": true, "counts": { "templates": 45, "fields": 72, "pages": 960 } } Example: Content Search Ask Cursor: "Search for pages containing 'summer'" { "query": "summer", "count": 5, "results": [ { "id": 1764, "title": "Lake District walks in summer", "path": "/guides/lake-district-summer/", "template": "page-guide", "matchedField": "Body", "snippet": "The Lake District offers some of the best walking trails in summer. From gentle lakeside strolls to challenging fell walks..." } ] } Example: File Search Ask Cursor: "Find images with 'lake' in the filename" { "query": "lake", "count": 5, "results": [ { "filename": "lake-windermere-sunset.jpg", "url": "/site/assets/files/1070/lake-windermere-sunset.jpg", "size": 31207, "sizeStr": "30.5 kB", "description": "Sunset over Lake Windermere", "field": "Images", "page": { "id": 1070, "title": "Lake District walks in summer", "path": "/guides/lake-district-summer/" }, "width": 500, "height": 626 } ] } Example: Get Page with All Fields Ask Cursor: "Get the page at /about/" { "id": 1050, "name": "about", "path": "/about/", "url": "/about/", "template": "basic-page", "status": 1, "statusName": "published", "parent": { "id": 1, "path": "/", "title": "Home" }, "numChildren": 5, "created": "2023-05-15T10:30:00+00:00", "modified": "2024-11-20T14:22:00+00:00", "fields": { "title": "About Us", "body": "<p>We are a team of dedicated professionals...</p>", "Images": { "_count": 2, "_files": ["team-photo.jpg", "office.jpg"] } } } Example: RepeaterMatrix Support The module fully supports RepeaterMatrix fields, returning the actual content with type labels: { "matrix": { "_count": 3, "_items": [ { "_typeId": 1, "_typeLabel": "Body", "Body": "<h2>Welcome to our guide</h2><p>This guide covers...</p>", "Images": null }, { "_typeId": 2, "_typeLabel": "FAQs", "faq_question": "What is the best time to visit?", "faq_answer": "The summer months offer the best weather for walking..." }, { "_typeId": 3, "_typeLabel": "Call to Action", "cta_title": "Plan Your Visit", "cta_link": "/contact/" } ] } } So thats the first part done and working. My next plan is to be able to 1. PULL / convert a databse page into a local text file which lists all page properties, fields, template etc 2. edit the file as a local text file 3 PUSH the text file back into PW so that the original content picks up the changes Just having fun and building something useful. Very likely there are similar solutions or better ways to handle this but this suits my workflow ATM. Cheers P
  5. I am about to put my first ProcessWire-powered website onto a live server, and I was searching the website and forum on how to do this. Maybe I used the wrong search-terms, but I did not find anything on how this should be done. I can obviously just throw all the files on the server through FTP, and put the database in place using PhpMyAdmin. Then change the DB-credentials in config.php and I think it should work, but I don't know what the recommended way is to push a website from your local/dev-environment to a live webserver.
  6. We send everything through Amazon SES. You can limit it to specific regions, and you won’t find a cheaper way to send bulk emails. It’s $0.10 per 1,000 emails. And the deliverability is top-notch. And no fixed monthly fees — just pay as you go.
  7. The client hosting company has a cap on emails per hour that is fine for now but we might exceed it eventually. Can anyone recommend a paid SMTP service that scales in tems of volume and works well with the stock ProcessWire SMTP module? I just want something that will plug and play but inputting the SMTP credentials rather than having to authorise an app, etc. I have used Mailgun before so that is an option and found the module in the PW directory works. I just find some SMTP services don't always work right away so I wondered if there are any you can recommend. The server is based in the UK so something that has a server nearby would be a bonus for lower latency. Thanks. 🙂
  8. psy

    SeoMaestro

    Still using Seomaestro. Discovered a scenario where it missed pages in the sitemap that used urlsegments. In my case, it was the blog module authors page. It should also handle paginated pages but untested. Hope this helps. $wire->addHookAfter('SeoMaestro::sitemapItems', function(HookEvent $event) { $items = $event->return; $pages = wire('pages'); $users = wire('users'); $roles = wire('roles'); $config = wire('config'); $seen = []; foreach($items as $item) { if(!empty($item->loc)) { $seen[rtrim($item->loc, '/')] = true; } } $getSeoSettings = function(Page $page) { foreach($page->template->fieldgroup as $field) { if(!$field->type instanceof FieldtypeSeoMaestro) { continue; } $seo = $page->get($field->name); if(!$seo || !$seo->sitemap->include) { return null; } return [ 'priority' => $seo->sitemap->priority, 'changefreq' => $seo->sitemap->changeFrequency, ]; } return null; }; $appendItem = function($url, $lastmod, array $settings) use (&$items, &$seen) { $key = rtrim($url, '/'); if(isset($seen[$key])) { return; } $item = new \SeoMaestro\SitemapItem(); $item->set('loc', $url); $item->set('lastmod', date('c', (int) $lastmod)); $item->set('priority', $settings['priority']); $item->set('changefreq', $settings['changefreq']); $items[] = $item; $seen[$key] = true; }; $appendPaginatedItems = function(Page $page, $totalItems, $perPage, $lastmod) use ($config, $getSeoSettings, $appendItem) { $settings = $getSeoSettings($page); if(!$settings) { return; } $totalPages = (int) ceil($totalItems / $perPage); if($totalPages < 2) { return; } for($pageNum = 2; $pageNum <= $totalPages; $pageNum++) { $appendItem( $page->httpUrl . $config->pageNumUrlPrefix . $pageNum . '/', $lastmod ?: $page->modified, $settings ); } }; $blogPostsPage = $pages->get('template=blog-posts'); if($blogPostsPage->id) { $latestPost = $pages->get("template=blog-post, sort=-modified"); $appendPaginatedItems( $blogPostsPage, $pages->count("template=blog-post"), 8, $latestPost->id ? $latestPost->modified : $blogPostsPage->modified ); } foreach($pages->find('template=blog-category, include=hidden') as $categoryPage) { $latestPost = $pages->get("template=blog-post, blog_categories=$categoryPage, sort=-modified"); $appendPaginatedItems( $categoryPage, $pages->count("template=blog-post, blog_categories=$categoryPage"), 10, $latestPost->id ? $latestPost->modified : $categoryPage->modified ); } $authorsPage = $pages->get('template=blog-authors'); $authorSettings = $authorsPage->id ? $getSeoSettings($authorsPage) : null; if($authorsPage->id && $authorSettings) { $authorRole = $roles->get('blog-author'); foreach($users->find("roles=$authorRole, sort=title") as $author) { $authorSlug = wire('sanitizer')->pageName($author->title); if(!$authorSlug) { continue; } $postCount = $pages->count("template=blog-post, created_users_id=$author"); if(!$postCount) { continue; } $authorUrl = $authorsPage->httpUrl . $authorSlug . '/'; $latestPost = $pages->get("template=blog-post, created_users_id=$author, sort=-modified"); $lastmod = $latestPost->id ? $latestPost->modified : ($author->modified ?: $authorsPage->modified); $appendItem($authorUrl, $lastmod, $authorSettings); $totalPages = (int) ceil($postCount / 10); if($totalPages < 2) { continue; } for($pageNum = 2; $pageNum <= $totalPages; $pageNum++) { $appendItem( $authorUrl . $config->pageNumUrlPrefix . $pageNum . '/', $lastmod, $authorSettings ); } } } $event->return = $items; });
  9. Hello @ all I want to share a new module with you, which makes the creation and validation of forms easy. Take a look at the following example of a simple contact form: // A very simple example of a contactform for demonstration purposes $form = new Form('contactform'); $gender = new Select('gender'); $gender->setLabel('Gender'); $gender->addOption('Mister', '0'); $gender->addOption('Miss', '1'); $form->add($gender); $surname = new InputText('surname'); $surname->setLabel('Surname'); $surname->setRule('required'); $form->add($surname); $name = new InputText('name'); $name->setLabel('Name'); $name->setRule('required'); $form->add($name); $email = new InputText('email'); $email->setLabel('E-Mail'); $email->setRule('required'); $form->add($email); $subject = new InputText('subject'); $subject->setLabel('Subject'); $subject->setRule('required'); $form->add($subject); $message = new Textarea('message'); $message->setLabel('Message'); $message->setRule('required'); $form->add($message); $privacy = new InputCheckbox('privacy'); $privacy->setLabel('I accept the privacy policy'); $privacy->setRule('required')->setCustomMessage('You have to accept our privacy policy'); $form->add($privacy); $button = new Button('submit'); $button->setAttribute('value', 'Send'); $form->add($button); if($form->isValid()){ print_r($form->getValues()); // do what you want } // render the form echo $form->render(); This piece of code creates a simple contact form and validates it according to the validation rules set. Inside the isValid() method you can run your code (fe sending an email) Highlights: 30+ validation types Support for UiKit 3 and Bootstrap 5 CSS framework SPAM protection Highly customizable Hookable methods for further customization Multi-language You can download and find really extensive information on how to use at https://github.com/juergenweb/FrontendForms. Please report errors or suggestions directly in GitHub. Best regards and happy testing ? If you have downloaded the module in the past I recommend you to uninstall the module completely and install the newest version 2.1.14. There are a lot of changes in the new version, so please test carefully.
  10. This week on the dev branch we've got several commits with various core improvements and fixes. @adrian has been using Claude Code to suggest core optimizations (focused mostly on the PageFinder) and so he sent the suggestions to me. (PageFinder is the brains behind the $pages->find() method, and many others). I took the suggestions and coded them into our PageFinder, but didn't want to mess with what was already working well, so put them in a new class named PageFinder2, at least temporarily. If running the latest dev branch, you can enable PageFinder2 by adding the following to your /site/config.php: $config->PageFinder('version', 2); The most significant changes are: using subqueries for subselectors rather than separate independent queries; Reusing PageFinder instances (keeping a pool of typically 1-3 PageFinders rather than creating a new one for each $pages->find() operation); and lots of in_array() calls have been converted to isset() lookups, which should technically be faster (still the case in PHP8?, I'm not sure). There were some other changes as well. Theoretically these changes should make PageFinder even faster than it already is. I did quite a bit of testing and found that for the most part it performs the same as PageFinder v1. But then I came across a rather complex selector that translated to a much faster PageFinder operation, nearly twice as fast, and that convinced me it was worthwhile. While PageFinder v2 is not consistently faster than v1, there are some situations where it can be a lot faster. I'm not totally clear on what those situations are just yet, but I'll be doing more testing. In other situations it also can use a lot fewer queries, though that doesn't necessarily translate to a performance difference. But on the whole, all of Claude's suggestions were quite good, regardless of performance improvements. I was pretty impressed with what Claude Code had suggested, so decided to install it on my computer too. I've found it's particularly good at finding bugs. I'll ask it to do a code review on a core file, and it always has good suggestions. It uses ProcessWire terminology too. For instance it pointed me to an object that wasn't properly "wired to the ProcessWire instance", and that's something you'd only ever hear in ProcessWire land. Claude code also helped with improvements to our DatabaseQuery* classes, PagesVersions module, Wire base class, NullPage class, and minor updates to the PagesLoader* classes. I'm not having it write any code just yet, but am having it suggest where improvements can be made. I like to code. I asked it how it knew so much about ProcessWire, and it said that it stays up-to-date with the forums, the website, API docs, and GitHub repo. Thanks to @adrian and @Jan V. for recommending it to me (Jan V. uses it to manage this webserver), I can see how it's going to be a big help to ProcessWire with its suggestions and ideas, I'm already learning a lot from it. And if you get a chance to try the updated PageFinder, please let me know how it works for you. Thanks for reading and have a great weekend!
  11. Something I've adopted for my PW sites is putting environment-specific DB credentials and configuration in a separate file, /site/config.local.php: /** * Installer: Database Configuration * */ $config->dbHost = '127.0.0.1'; $config->dbName = 'db'; $config->dbUser = 'default'; $config->dbPass = 'default'; $config->dbPort = '3306'; /** * Installer: HTTP Hosts Whitelist * */ $config->httpHosts = array( 'my-local-domain.test'); And then including that file into /site/config.php: /*** INSTALLER CONFIG ********************************************************************/ // DB Config, Hosts Whitelist include dirname(__FILE__) . '/config.local.php'; I find this makes it easier to push changes when using version control (i.e. git) across different environments, and keep the credentials and other sensitive information excluded from the repo. On some managed hosts like Cloudways, you can pull and push DB changes from staging to/from production applications, as well as branch changes from git. Obviously this doesn't help when you make DB changes locally, though. Otherwise, I will either export/import the entire DB via the Database Backups module, or Pages Import/Export (under Admin > Modules, Core modules) depending on the kind of changes. After using Vercel and Netlify for static-based websites, it does make me wish there was a quicker way to push/pull DB changes, but perhaps that extra bit of friction is good so you don't inadvertently disrupt your production data. Definitely something I can improve in my own workflow! Managing local/remote image assets Occasionally I've added this code snippet to /site/ready.php, that allows for your local installation to pull down the asset from prod, which can save you some time. I'm also generally curious to know more about other folks' deployment strategies – could be a potentially useful thing to collect and make more visible in the PW docs, too.
  12. A module helping you to manage SEO related tasks like a boss! Automatically generates and maintains a XML sitemap from your pages. Includes a Fieldtype and Inputfield to manage sitemap settings and meta data for pages (Title, Description, Canonical URL, Opengraph, Twitter, Structured Data etc.) Multi language support for the sitemap and meta data. Configure default values for meta data on template level and let pages inherit or overwrite them individually. Map existing fields to meta data, reducing the need to duplicate content. Live preview for content editors how the entered meta data appears on Google. Live preview for content editors how the entered Opengraph data looks like when sharing a page with Facebook. Check out the README on GitHub for more details, including usage instructions. The module is currently released as beta and needs testing! Please report any issues on GitHub or in this forum thread, if you find time to give it a try ? Examples Here is an example of rendered meta data you will get from a single SeoMaestro field: <title>Sed dictum eros quis massa semper rutrum. | acme.com</title> <meta name="description" content="Si lobortis singularis genitus ibidem saluto. Dolore ad nunc, mos accumsan paratus duis suscipit luptatum facilisis macto uxor iaceo quadrum. Demoveo, appellatio elit neque ad commodo ea. Wisi, iaceo, tincidunt at commoveo rusticus et, ludus."> <meta name="keywords" content="Foo,Bar"> <link rel="canonical" href="https://acme.com/en/about/"> <meta property="og:title" content="Sed dictum eros quis massa semper rutrum."> <meta property="og:description" content="Si lobortis singularis genitus ibidem saluto. Dolore ad nunc, mos accumsan paratus duis suscipit luptatum facilisis macto uxor iaceo quadrum. Demoveo, appellatio elit neque ad commodo ea. Wisi, iaceo, tincidunt at commoveo rusticus et, ludus."> <meta property="og:image" content="https://acme.com/site/assets/files/1001/og-image.jpg"> <meta property="og:image:type" content="image/jpg"> <meta property="og:image:width" content="1600"> <meta property="og:image:height" content="1200"> <meta property="og:image:alt" content="Lorem Ipsum"> <meta property="og:type" content="website"> <meta property="og:url" content="https://acme.com/en/about/"> <meta property="og:locale" content="en_EN"> <meta name="twitter:card" content="summary"> <meta name="twitter:creator" content="@schtifu"> <meta name="twitter:site" content="@schtifu"> <script type="application/ld+json"> { "@context": "https://schema.org", "@type": "BreadcrumbList", "itemListElement": [ { "@type": "ListItem", "position": 1, "name": "About", "item": "https://acme.com/en/about/" } ] } </script> <meta name="generator" content="ProcessWire"> <link rel="alternate" href="https://acme.com/en/about/" hreflang="en"> <link rel="alternate" href="https://acme.com/en/about/" hreflang="x-default"> <link rel="alternate" href="https://acme.com/de/ueber/" hreflang="de"> <link rel="alternate" href="https://acme.com/fi/tietoja/" hreflang="fi"> And some screenshots of the UI:
  13. The toughest challenge so far? Being our own client. Actually, the website we launched back then was just a placeholder, and the plan was to quickly replace it with a proper portfolio website. But as is often the case, it ended up taking a little longer than expected. A year later, we’ve finally done it: Our new website is live! https://konkat.studio/ The goal of the new website is to showcase our work and better communicate our services. The site is bilingual and was built using ProcessWire and PageGrid. More on that later. In addition to the website, we’ve also evolved our visual identity and logo. KONKAT (from concatenation) stands for linking individual elements into a functional whole. Our new branding makes this connection visible. In our case, we combine strategy, design, and technology into a unified process. The logo mark communicates this as well; as most of you probably know, the += operator in JavaScript joins elements and assigns the result. It took us some iterations to get the design right, but once the design was done, development was pretty straightforward. Most of the time was spent preparing the content for the projects, and that is also where PageGrid was super useful since it allowed us to design the layout and content of each project individually. Backend view: Managing project content and layouts with PageGrid. PageGrid also significantly sped up development, as we built all other pages using only its core blocks. For the projects overview, for instance, we used the datalist block to automatically generate the listing from our project pages, working perfectly out of the box without any custom logic. We also added some custom code where it made sense, e.g. the scroll animation on the homepage was just a bit easier to achieve with custom code (it uses native CSS sticky). Backend view: Using Pagegrid's inline editing to update some text on the english version auf our services page. Another great thing is that PageGrid takes care of lazy loading images and videos (using the famous lazysizes js plugin) and is caching its content automatically. As a result, we got a 100 on the Google Lighthouse test on desktop and 99 on mobile without any extra optimizations (we are not using Markup Cache or ProCache for this site). Backend view: Editing a thumbnail on the homepage If you have any further questions regarding our workflow or process, feel free to ask. I will do my best to answer them. Also, please let us know if you find any bugs, since the website is brand new, there are probably some we haven't caught yet! We also welcome any feedback you may have. Best, Jan & Diogo (KONKAT)
  14. On a side note, because of the ProcessWire 'way' of handling images, it helps to think about the purpose of the images your page will have. As teppo flagged. So I usually have 1 x carousel images or a hero image field 1 x general images field i might display in the content 1 x something else The reason for doing this is that you can keep all your images and image field settings appropriate for the image purpose etc. Especially if you have clients adding or removing images. I find having just one image field can be an issue if they accidentally remove something or add an image which should not be part of the hero banner etc. Later on you might want to also look at tagging images and calling them onto your front-end pages https://processwire.com/modules/textformatter-image-tags/ But I'd recommend getting used to the basics first. An example of how I use image tagging might be. I have a product field with a lot of general images one of them in particular is used as a preview thumb on a product overview page so i'll tag that image 'preview' or whatever you want really. Lets us know how it goes
  15. I recommend not subscribing to a Plaid plan at kimi.com. I wanted to cancel my subscription, but there was no such option, so I had to delete the account, as that was the only option I could find. I wrote to support, but they never replied. I removed my saved credit card on Stripe's interface, but I still receive a message every other day: "...payment to MOONSHOT AI PTE. LTD. was unsuccessful We attempted to charge your card for your MOONSHOT AI PTE. LTD. subscription again, but were unsuccessful. Please update your billing information to continue your subscription." For a theoretically deleted account...
  16. There may be a time where you need to create a page reference field using the Select inputfield and it's selecting repeater pages. Let's say I have a repeater field called "order_line_items" and I want to create a page reference field called "order_line_item" that allows me to select a repeater item (which is a page) of the "order_line_items" repeater field. Repeater pages are a bit different from regular pages in that their "parent" is a container admin page associated with the page in which it exists (dig into /admin/repeaters/order_line_items/ in your page tree to see what I mean). So when you are configuring your page reference field, you can't really choose a Parent. However when configuring your field, your instinct would be to choose the Template of "repeater_order_line_items". Then because you need extra precision in what pages are actually available for selection (rather than all of them across all pages), your instinct will be to implement custom PHP code: $wire->addHookAfter('InputfieldPage::getSelectablePages', function($event) { if($event->object->hasField == 'order_line_item') { $event->return = $event->pages->find('your selector here'); } }); The problem with that approach is that even though you have defined the custom PHP code and the select field correctly shows the selectable repeater pages in the select field, behind-the-scenes, ProcessWire has still loaded EVERY SINGLE REPEATER that has the "repeater_order_line_items" template (you can see this is TracyDebugger's pages loaded list)! Your site will definitely be slower as a result, dramatically so if you have thousands or tens of thousands of repeater pages of that template. I hit this issue years ago (2018) and I thought it was a bug. I discussed it with Ryan and it's technically not a bug, but kind of the way ProcessWire works, which is beyond this tutorial. While you can circumvent this using the PageAutocomplete field, I don't like the ergonomics of that field in certain situations. I want the good-old select field. The solution to this is to NOT select anything for the "Template" when configuring your field. So in my example, I chose "repeater_order_line_items", but instead, it should be left blank. Now the field will just rely on the code portion and all the unnecessary page loads will be eliminated.
  17. Hi, I am trying to collect the page titles of a parent and its children, but this code won;t work: <?php // collect titles for parent + children $titles = [$page->title]; // parent title foreach($page->children as $child) { $titles[] = $child->title; // child titles } // build a selector value like: title="A|B|C" $titleSelector = implode('|', array_unique($titles)); // now find all Championship_results whose winner_name page // has a title matching ANY of those titles, sorted by year descending $champwinx = $pages->find( "template=Championship_results, winner_name.title=$titleSelector, sort=-year" ); ?> It produces this error: any suggestions re a fix appreciated Error: Exception: Unknown Selector operator: '[empty]' -- was your selector value properly escaped? field='G', value='Miss|Temple-Dobell', selector: 'template=Championship_results, winner_name.title=Ravenscroft, G (Miss)|Temple-Dobell, G (Mrs), sort=-year' (in wire/core/Selectors.php line 222)
  18. @mattgs Like Adrian, I also consider myself very environmentally conscious. I've not spent much time learning AI in part because I thought it was problematic for a lot of reasons. But I don't think we're likely to stop these AI companies so that's why I thought I should try things out with a company that seems to have more ethics than the others. Anthropic seems to have a mission for AI safety and sustainability. I hope it's legit. And as far as I can tell, the other companies don't, which I find concerning. But I'm also not as up-to-speed as you are on the on the issues you brought up, so I'll have to look closer as well as check out the video you mentioned (do you have a link?). I'm also aware that a project like ProcessWire gets executed millions upon millions of times every month (or day?) throughout the world, and every execution consumes energy. So I've always been very interested in optimization and making ProcessWire use as little time and energy as possible to do its work. The updates that we've been focusing on here are aimed directly at that. So perhaps AI is using a little energy to find optimizations and bugs in PW, but that single brief code review session reduces the energy usage of every ProcessWire execution going forward. This is a case where AI is likely saving a lot more energy than it consumes, indirectly by making ProcessWire use less energy. Some of the optimizations and bugs its found have been there since the beginning, and likely would have never been identified otherwise.
  19. I created a companion module for Fluency to translate all fields on a page at once: https://github.com/robertweiss/ProcessTranslatePage You can find the corresponding conversation about it in the Fluency thread (link below), but I decided to add the module to the official list so others who have a need for this have an easier time finding it.
  20. The more you use it, the more you’ll be blown away. At least that’s how it has gone for me and most devs I know. Since Opus 4.x, Claude has completely transformed my workflow 🙂 I enjoy “manual” coding too, but AI coding has plus sides. Going from rough idea to usable feature is blazing fast, testing and prototyping has been so much fun, and AI tends to find issue (and opportunities!) humans would never spot. Also with Claude taking some of the load off my shoulders I often find myself working on multiple things at the same time… though I guess not everyone will see that as a good thing 🫣 Oh, and docs and tests! Claude is great for that stuff. It needs human guidance for both, though, as it tends to get confused about what actually matters. AI docs are often easy to spot: many words, little (or no) meaning. Admittedly sometimes this way of doing things tends to feel less like coding and more like managing a team of skilful but a little naive devs that often need help with ideation, architecture, testing, and just plain common sense 😅
  21. Think I got it working, but not sure which of these steps were needed. Posting in case others find it helpful. I uncommented RewriteBase / in .htaccess I truncated the cache in the database with Phpadmin6 (I'd avoid this one if the others work first!) Using Tracy Debugger > Clear sessions, cookies & module refresh. A normal browser cache clear didn't work for me.
  22. @Ivan Gretsky I'm always a little reluctant to make a blanket statement like "avoid markup in page classes", but I'm referring to what I think works best with the projects I work on. The files in /site/templates/ are the view layer, as nearly all code in there is aimed at generating markup/output. Even if something isn't directly generating markup, it's still finding and preparing things for output. Most markup comes from "partials", which are files that I put in /site/templates/parts/, or if exclusive to a particular template, then /site/templates/[template]/. And then I either include() them, or files()->render() them from the site's template files. I primarily use Markup Regions. The _main.php establishes the base markup: <?php namespace ProcessWire; /** @var Page $page **/ ?><!DOCTYPE html> <html> <head id="html-head"> <?php include('./parts/html-head.php');?> </head> <body id="html-body"> <header id="header"> <?php include('./parts/header.php');?> </header> <h1 id="headline"><?=$page->title?></h1> <main id="content"> <?=$page->body?> </main> <aside id="sidebar" pw-optional> </aside> <footer id="footer"> <?php include('./parts/footer.php');?> </footer> </body> </html> Below is a template file for the /products/ page which lists product pages, supports pagination, and uses URL segments for sorting: <?php namespace ProcessWire; // products.php /** @var ProductsPage|CategoryPage $page */ $products = findProducts($page); $body = input()->pageNum === 1 ? $page->body : ''; $headline = $page->get('headline|title'); ?> <h1 id="headline"><?=$headline?></h1> <main id="content"> <?=$body?> <?php include('./parts/sorts.php'); // no expects ?> <?php include('./parts/products-list.php'); // expects $products ?> </main> <aside id="sidebar"> <?php include('./parts/categories-list.php'); // no expects ?> </aside> The category template file works exactly the same way, except that it lists products for the category rather than listing all products. The same code works either way, so "category.php" just includes "products.php": <?php namespace ProcessWire; // category.php include('./products.php'); There's that findProducts() function above in the products.php template file -- I usually have helper functions in a /site/templates/_func.php, /site/templates/_products.php, or /site/templates/products/func.php (assuming exclusive for "products"). Another place would be for the ProductsPage and CategoryPage to have findProducts() methods, but usually I don't want the page classes getting involved with detecting stuff about the current request (sort, pageNum, etc.) so like these in a simple function library file: <?php namespace ProcessWire; // file site/templates/_func.php included by _init.php function getSorts(): array { return [ 'name' => 'A-Z', '-name' => 'Z-A', 'price' => 'Price (low-high)', '-price' => 'Price (high-low)', 'created' => 'Date added (oldest)', '-created' => 'Date added (newest)' ]; } function getSort(): string { $sorts = getSorts(); $sort = input()->urlSegment('sort-(*)'); if(empty($sort)) $sort = 'name'; if(!isset($sorts[$sort])) wire404('Invalid sort'); return $sort; } function findProducts($page, $limit = 20): PageArray { $sort = getSort(); $find = "template=product, sort=$sort, limit=$limit"; if($page instanceof CategoryPage) $find .= ", categories=$page"; return pages()->find($find); } Here's an example of a ./parts/products-list.php file: <?php namespace ProcessWire; // file: parts/products-list.php /** @var PageArray|ProductPage[] $products */ $subhead = $products->getPaginationStr('Products'); $pagination = files()->render('parts/pagination.php', [ 'items' => $products ]); ?> <h3><?=$subhead?></h3> <?=$pagination?> <ul class="products-list"> <?php foreach($products as $product): ? <?php include('./parts/products-item.php'); // expects $product ?> <?php endforeach; ?> </ul> And the parts/products-item.php, though in reality there would likely be more to it: <?php namespace ProcessWire; // file: parts/products-item.php /** @var ProductPage $product */ ?> <li class="products-item"> <h3><?=$product->title?></h3> <p><?=$product->summary?></p> <p><a href="<?=$product->url?>">View Details</a></p> </li> To complete it, here's the parts/sorts.php file: <?php namespace ProcessWire; // file: parts/sorts.php $currentSort = getSort(); $url = page()->url; $sorts = []; foreach(getSorts() as $sort => $label) { if($sort != $currentSort) $label = "<a href='{$url}sort-$sort/'>$label</a>"; $sorts[] = $label; } echo "<p class='sorts'>Sort by: " . implode(' / ', $sorts) . "</p>"; If I start needing to output products in more places in the site, then I'll usually do fewer include()'s and move the rendering to dedicated functions. That way these things render in their own variable namespace and don't bleed variables or overwrite variables in the main rendering. So this would also go in that _func.php (or _products.php or ./products/func.php) mentioned above, and the include() calls in template fiels would be replaced with render...() calls: function renderProducts(PageArray $products): string { return files()->render('parts/products-list.php', [ 'products' => $products ]); } function renderCategories(): string { return files()->render('parts/categories-list.php'); } function renderPagination(PageArray $items) { return files()->render('parts/pagination.php', [ 'items' => $items ]); } So if using render() functions then the <main> with the include('./parts/products-list.php'); would get replaced with this: <main id="content"> <?=$body?> <?=renderProducts($products)?> </main> Ah yes, I hadn't thought about that in a long time. I can't remember if that was implemented yet or not. I'll find out. If not yet implemented I'll have to implement it, it should be fairly simple.
  23. Version 3.0.161 on the dev branch continues with the updates optimizing our support for the new selector operators introduced in last week's blog post for 3.0.160. Last week I was still kind of figuring it out and the code still needed some refactoring and optimization. This week several parts have been rewritten and it's been improved quite a bit. Though the end result is still very similar to what was demonstrated last week, but now it's a lot more performant and solid. One thing new this week that's also kind of fun: you can now use more than one operator in any "field=value" selector expression, at least from the API side. And it works anywhere that you might use a selector, whether querying the database or something in memory. I think the best way to explain it is with an example. Let's say that you want to find all pages with a title containing the phrase "hello world" — you'd do this using the "contains text/phrase" operator: *= $pages->find("title*=hello world"); But let's also say that if you don't find any matches, you want to fallback to find any pages that contain the words "hello" and "world" anywhere in the title, in any order. We'd use the "contains all words" operator "~=" to do that. So now you can add that operator to the existing one, and it'll fallback to it if the first operator fails to match. So we'll append the "contains all words" operator to the previous one: ~= $pages->find("title*=~=hello world"); Cool huh? But maybe we still aren't finding any matches, so we want to fallback to something even broader. So if the phrase match fails, and the words match fails, now we want to fallback to find any pages that contain the world "hello" OR "world", rather than requiring them both. For that we can use our new "contains any words" operator: ~|= $pages->find("title*=~=~|=hello world"); This example is getting a bit contrived now, but let's say that if we still haven't found a match, we want it to find any pages that have any words starting with "hello" or "world", so it would find pages with words like "helloes", "worlds", "worldwide", etc. That's a job for our new "contains any partial words" operator: ~|*= $pages->find("title*=~=~|=~|*=hello world"); Okay last one I promise—you probably wouldn't stack this many in real life, but stay with me here. Let's say the query still didn't find anything, and as a final fallback, we want it to find any words LIKE "hello" or "world", so that those terms can match anywhere in words, enabling us to find pages with words like in the previous example, but also words like "phellogen", "othello", "underworld", "otherworldly", etc., and that's a job for our new "contains any words like" operator: ~|%= $pages->find("title*=~=~|=~|*=~|%=hello world"); So that looks like a pretty complex operator there, but as you've seen by following the example, it's just these 5 appended operators to each other: *= Contains phrase ~= Contains all whole words ~|= Contains any whole words ~|*= Contains any partial words ~|%= Contains any words like I think a more likely scenario in a site search is that you might stack two operators, such as the *= followed by the ~|*=, or whatever combination suits your need. By the way, you can do this with any operators, not just the text searching ones. But if you didn't read the blog post last week, also be sure to check out the other new operators in addition to those above: *+=, **=, **+=, ~*=, ~+=, ~~=, ~%=, #=. I think these new operators help out quite a bit with ProcessWire's text searching abilities. But there's one thing that's kind of a common need in search engines that's not easily solved, and that's the handling of singular vs. plural. At least, that's a common issue when it comes to English (I'm assuming so for other languages, but not positive?) MySQL fulltext indexes can't differentiate between singular and plural versions of words, so they index and match them independently as completely different words. This can be unexpected as clients might type in "goose" and expect it's also going to match pages with "geese". I've already got something in the works for this, so stay tuned. Thanks for reading and hope you have a great weekend!
  24. I have added a hook recorder and this is what I got when logging in with a non-existing email address: ProcessWire\ProcessLogin::execute ProcessWire\ProcessLogin::buildLoginForm ProcessWire\ProcessLogin::loginFormProcessReady ProcessWire\ProcessLogin::loginFailed ProcessWire\ProcessLogin::loginFormProcessed ProcessWire\ProcessLogin::renderLoginForm ProcessWire\ProcessLogin::headline ProcessWire\ProcessLogin::getLoginLinks ProcessWire\ProcessLogin::executed So to me it looks like loginFormProcessReady should work. This is the whole hook recorder log, which looks like InputfieldForm::processInput should also be an option? ProcessWire\Fields::load ProcessWire\Fieldgroups::load ProcessWire\Templates::load ProcessWire\Fieldgroup::setQuietly ProcessWire\Fieldgroup::callUnknown ProcessWire\Fieldgroup::setQuietly ProcessWire\Fieldgroup::callUnknown ProcessWire\Fieldgroup::setQuietly ProcessWire\Fieldgroup::callUnknown ProcessWire\Fieldgroup::setQuietly ProcessWire\Fieldgroup::callUnknown ProcessWire\Fieldgroup::setQuietly ProcessWire\Fieldgroup::callUnknown ProcessWire\Session::init ProcessWire\ProcessWire::init ProcessWire\Pages::find ProcessWire\Pages::find ProcessWire\PageFinder::find ProcessWire\PageFinder::getQuery ProcessWire\FieldgroupsArray::changed ProcessWire\FieldsArray::changed ProcessWire\FieldsArray::changed ProcessWire\FieldgroupsArray::changed ProcessWire\Fieldgroup::setQuietly ProcessWire\Fieldgroup::callUnknown ProcessWire\TemplatesArray::changed ProcessWire\TemplatesArray::changed ProcessWire\FieldgroupsArray::changed ProcessWire\FieldsArray::changed ProcessWire\FieldsArray::changed ProcessWire\FieldgroupsArray::changed ProcessWire\Fieldgroup::setQuietly ProcessWire\Fieldgroup::callUnknown ProcessWire\TemplatesArray::changed ProcessWire\TemplatesArray::changed ProcessWire\FieldgroupsArray::changed ProcessWire\FieldgroupsArray::changed ProcessWire\Fieldgroup::setQuietly ProcessWire\Fieldgroup::callUnknown ProcessWire\TemplatesArray::changed ProcessWire\TemplatesArray::changed ProcessWire\FieldgroupsArray::changed ProcessWire\FieldsArray::changed ProcessWire\FieldsArray::changed ProcessWire\FieldsArray::changed ProcessWire\FieldsArray::changed ProcessWire\FieldsArray::changed ProcessWire\FieldsArray::changed ProcessWire\FieldsArray::changed ProcessWire\FieldsArray::changed ProcessWire\FieldsArray::changed ProcessWire\FieldsArray::changed ProcessWire\FieldgroupsArray::changed ProcessWire\Fieldgroup::setQuietly ProcessWire\Fieldgroup::callUnknown ProcessWire\TemplatesArray::changed ProcessWire\TemplatesArray::changed ProcessWire\Pages::find ProcessWire\PageFinder::find ProcessWire\PageFinder::getQuery ProcessWire\Pages::find ProcessWire\PageFinder::find ProcessWire\PageFinder::getQuery ProcessWire\Pages::find ProcessWire\PageFinder::find ProcessWire\PageFinder::getQuery ProcessWire\FieldgroupsArray::changed ProcessWire\FieldgroupsArray::changed ProcessWire\Fieldgroup::setQuietly ProcessWire\Fieldgroup::callUnknown ProcessWire\TemplatesArray::changed ProcessWire\TemplatesArray::changed ProcessWire\Pages::find ProcessWire\PageFinder::find ProcessWire\PageFinder::getQuery ProcessWire\UserPage::hasPagePermission ProcessWire\Pages::find ProcessWire\PageFinder::find ProcessWire\PageFinder::getQuery ProcessWire\ProcessPageView::execute ProcessWire\PagesRequest::getPage ProcessWire\PagesRequest::getPageForUser ProcessWire\DefaultPage::viewable ProcessWire\PagesRequest::getLoginPageOrUrl ProcessWire\Pages::find ProcessWire\ProcessPageView::ready ProcessWire\ProcessWire::ready ProcessWire\Config::InputfieldWrapper ProcessWire\Config::callUnknown ProcessWire\JqueryUI::use ProcessWire\PageFrontEdit::getPage ProcessWire\FieldgroupsArray::changed ProcessWire\Fieldgroup::setQuietly ProcessWire\Fieldgroup::callUnknown ProcessWire\FieldgroupsArray::changed ProcessWire\Fieldgroup::setQuietly ProcessWire\Fieldgroup::callUnknown ProcessWire\FieldgroupsArray::changed ProcessWire\Fieldgroup::setQuietly ProcessWire\Fieldgroup::callUnknown ProcessWire\FieldgroupsArray::changed ProcessWire\Fieldgroup::setQuietly ProcessWire\Fieldgroup::callUnknown ProcessWire\TemplatesArray::changed ProcessWire\TemplatesArray::changed ProcessWire\TemplatesArray::changed ProcessWire\TemplatesArray::changed ProcessWire\TemplatesArray::changed ProcessWire\Pages::find ProcessWire\PageFinder::find ProcessWire\PageFinder::getQuery ProcessWire\Pages::find ProcessWire\PageFinder::find ProcessWire\PageFinder::getQuery ProcessWire\Pages::find ProcessWire\PageFinder::find ProcessWire\PageFinder::getQuery ProcessWire\Pages::find ProcessWire\Pages::find ProcessWire\Pages::find ProcessWire\Pages::find ProcessWire\PageFinder::find ProcessWire\PageFinder::getQuery ProcessWire\Pages::find ProcessWire\PageFinder::find ProcessWire\PageFinder::getQuery ProcessWire\TemplateFile::render ProcessWire\DefaultPage::render ProcessWire\PageRender::renderPage ProcessWire\TemplateFile::render ProcessWire\ProcessController::execute ProcessWire\UserPage::hasPagePermission ProcessWire\Pages::find ProcessWire\UserPage::hasPagePermission ProcessWire\Pages::find ProcessWire\ProcessLogin::execute ProcessWire\ProcessLogin::buildLoginForm ProcessWire\ProcessLogin::loginFormProcessReady ProcessWire\InputfieldForm::processInput ProcessWire\InputfieldText::processInput ProcessWire\InputfieldText::processInput ProcessWire\InputfieldSubmit::processInput ProcessWire\InputfieldHidden::processInput ProcessWire\InputfieldHidden::processInput ProcessWire\InputfieldHidden::processInput ProcessWire\InputfieldHidden::processInput ProcessWire\Pages::find ProcessWire\PageFinder::find ProcessWire\PageFinder::getQuery ProcessWire\ProcessLogin::loginFailed ProcessWire\ProcessLogin::loginFormProcessed ProcessWire\ProcessLogin::renderLoginForm ProcessWire\ProcessLogin::headline ProcessWire\InputfieldForm::render ProcessWire\InputfieldForm::renderInputfield ProcessWire\InputfieldText::render ProcessWire\InputfieldForm::renderInputfield ProcessWire\InputfieldText::render ProcessWire\InputfieldForm::renderInputfield ProcessWire\InputfieldSubmit::render ProcessWire\InputfieldForm::renderInputfield ProcessWire\InputfieldHidden::render ProcessWire\InputfieldForm::renderInputfield ProcessWire\InputfieldHidden::render ProcessWire\InputfieldForm::renderInputfield ProcessWire\InputfieldHidden::render ProcessWire\InputfieldForm::renderInputfield ProcessWire\InputfieldHidden::render ProcessWire\ProcessLogin::getLoginLinks ProcessWire\Pages::find ProcessWire\ProcessLogin::executed ProcessWire\AdminThemeUikit::getExtraMarkup ProcessWire\AdminThemeUikit::getExtraMarkup ProcessWire\AdminThemeUikit::getUikitCSS ProcessWire\AdminThemeUikit::getExtraMarkup ProcessWire\AdminThemeUikit::getExtraMarkup ProcessWire\AdminThemeUikit::getExtraMarkup ProcessWire\AdminThemeUikit::renderBreadcrumbs ProcessWire\AdminThemeUikit::getExtraMarkup ProcessWire\AdminThemeUikit::getExtraMarkup ProcessWire\Pages::find ProcessWire\PageFinder::find ProcessWire\PageFinder::getQuery ProcessWire\Pages::find ProcessWire\Pages::find ProcessWire\PageFinder::find ProcessWire\PageFinder::getQuery ProcessWire\ProcessPageView::finished ProcessWire\ProcessWire::finished ProcessWire\DefaultPage::viewable ProcessWire\DefaultPage::rootParent ProcessWire\DefaultPage::viewable ProcessWire\DefaultPage::rootParent ProcessWire\DefaultPage::rootParent ProcessWire\DefaultPage::rootParent ProcessWire\PageFinder::find ProcessWire\PageFinder::getQuery ProcessWire\DefaultPage::viewable ProcessWire\PageFinder::find ProcessWire\PageFinder::getQuery ProcessWire\DefaultPage::viewable ProcessWire\FieldsArray::changed ProcessWire\FieldsArray::changed ProcessWire\FieldsArray::changed ProcessWire\DefaultPage::viewable ProcessWire\DefaultPage::editable ProcessWire\UserPage::hasPagePermission ProcessWire\Pages::find ProcessWire\PageFinder::find ProcessWire\PageFinder::getQuery ProcessWire\DefaultPage::addable ProcessWire\UserPage::hasPagePermission ProcessWire\Pages::find ProcessWire\DefaultPage::publishable ProcessWire\DefaultPage::editable ProcessWire\UserPage::hasPagePermission ProcessWire\Pages::find ProcessWire\DefaultPage::listable ProcessWire\DefaultPage::moveable ProcessWire\DefaultPage::editable ProcessWire\UserPage::hasPagePermission ProcessWire\Pages::find ProcessWire\DefaultPage::sortable ProcessWire\DefaultPage::editable ProcessWire\UserPage::hasPagePermission ProcessWire\Pages::find ProcessWire\DefaultPage::deleteable ProcessWire\DefaultPage::trashable ProcessWire\DefaultPage::restorable ProcessWire\DefaultPage::rootParent ProcessWire\Pages::find ProcessWire\PageFinder::find ProcessWire\PageFinder::getQuery ProcessWire\PageArray::changed ProcessWire\DefaultPage::viewable ProcessWire\User::hasPagePermission ProcessWire\Pages::find ProcessWire\DefaultPage::editable ProcessWire\User::hasPagePermission ProcessWire\Pages::find ProcessWire\DefaultPage::addable ProcessWire\User::hasPagePermission ProcessWire\Pages::find ProcessWire\DefaultPage::publishable ProcessWire\DefaultPage::editable ProcessWire\User::hasPagePermission ProcessWire\Pages::find ProcessWire\DefaultPage::listable ProcessWire\User::hasPagePermission ProcessWire\Pages::find ProcessWire\User::hasPagePermission ProcessWire\Pages::find ProcessWire\DefaultPage::moveable ProcessWire\DefaultPage::editable ProcessWire\User::hasPagePermission ProcessWire\Pages::find ProcessWire\DefaultPage::sortable ProcessWire\DefaultPage::editable ProcessWire\User::hasPagePermission ProcessWire\Pages::find ProcessWire\DefaultPage::deleteable ProcessWire\DefaultPage::trashable ProcessWire\DefaultPage::restorable ProcessWire\DefaultPage::rootParent ProcessWire\PageArray::changed ProcessWire\PageArray::changed ProcessWire\DefaultPage::viewable ProcessWire\DefaultPage::editable ProcessWire\DefaultPage::addable ProcessWire\DefaultPage::publishable ProcessWire\DefaultPage::listable ProcessWire\DefaultPage::moveable ProcessWire\DefaultPage::editable ProcessWire\User::hasPagePermission ProcessWire\Pages::find ProcessWire\PageFinder::find ProcessWire\PageFinder::getQuery ProcessWire\DefaultPage::sortable ProcessWire\DefaultPage::editable ProcessWire\User::hasPagePermission ProcessWire\DefaultPage::deleteable ProcessWire\DefaultPage::trashable ProcessWire\DefaultPage::restorable ProcessWire\DefaultPage::rootParent ProcessWire\WireCache::log ProcessWire\WireCache::log ProcessWire\WireDateTime::relativeTimeStr ProcessWire\WireDateTime::relativeTimeStr ProcessWire\WireDateTime::relativeTimeStr ProcessWire\WireDateTime::relativeTimeStr ProcessWire\Session::changed ProcessWire\Session::changed ProcessWire\WireCache::log ProcessWire\WireCache::log ProcessWire\Fields::load ProcessWire\Fieldgroups::load ProcessWire\Templates::load ProcessWire\Fieldgroup::setQuietly ProcessWire\Fieldgroup::callUnknown ProcessWire\Fieldgroup::setQuietly ProcessWire\Fieldgroup::callUnknown ProcessWire\Fieldgroup::setQuietly ProcessWire\Fieldgroup::callUnknown ProcessWire\Fieldgroup::setQuietly ProcessWire\Fieldgroup::callUnknown ProcessWire\Fieldgroup::setQuietly ProcessWire\Fieldgroup::callUnknown ProcessWire\Session::init
  25. Maybe I'm not doing it right.... $this->addHookAfter('LazyCron::every5Minutes', function(HookEvent $event) { PageRender::renderPage(bfd_mailer_cron.php); echo "mailer sent"; }; On the other hand, I'm trying with an OVH cron job. As soon as I enter this on the mailer page $features = $pages->find('parent=/events/|/the-eyes/, bfd_day.name=$todayday, bfd_month.name=$todaymonth'); the job doesn't work. I presume it doesn't understand the ProcessWire api.... And then I'm not sure if this is the right way to do what I actually want, sending a ProcessWire page once a day at 10am to one or several email adresses. It used to work with a OVH cron job for years but I guess there's been a few changes between PHP 5 and 8.2 that are in the way.
×
×
  • Create New...