Jump to content

Leaderboard


Popular Content

Showing content with the highest reputation since 12/14/2018 in all areas

  1. 31 points
    We recently rebuilt the Architekturführer Köln (architectural guide Cologne) as a mobile-first JavaScript web app, powered by VueJS in the frontend and ProcessWire in the backend. Concept, design and implementation by schwarzdesign! The Architekturführer Köln is a guidebook and now a web application about architectural highlights in Cologne, Germany. It contains detailled information about around 100 objects (architectural landmarks) in Cologne. The web app offers multiple ways to search through all available objects, including: An interactive live map A list of object near the user's location Filtering based on architect, district and category Favourites saved by the user The frontend is written entirely in JavaScript, with the data coming from a ProcessWire-powered API-first backend. Frontend The app is built with the Vue framework and compiled with Webpack 4. As a learning exercise and for greater customizability we opted to not use Vue CLI, and instead wrote our own Webpack config with individually defined dependencies. The site is a SPA (Single Page Application), which means all internal links are intercepted by the Vue app and the corresponding routes (pages) are generated by the framework directly in the browser, using data retrieved from the API. It's also a PWA (Progressive Web App), the main feature of which is that you can install it to your home screen on your phone and launch it from there like a regular app. It also includes a service worker which catches requests to the API and returns cached responses when the network is not available. The Architekturführer is supposed to be taken with you on a walk through the city, and will keep working even if you are completely offline. Notable mentions from the tech stack: Vue Vue Router for the SPA functionality VueX for state management and storage / caching of the data returned through the API Leaflet (with Mapbox tiles) for the interactive maps Webpack 4 for compilation of the app into a single distributable Babel for transpilation of ES6+ SASS & PostCSS with Autoprefixer as a convenience for SASS in SFCs Google Workbox to generate the service worker instead of writing lots of boilerplate code Bootstrap 4 is barely used here, but we still included it's reboot and grid system Backend The ProcessWire backend is API-only, there are no server-side rendered templates, which means the only PHP template is the one used for the API. For this API, we used a single content type (template) with a couple of pre-defined endpoints (url segments); most importantly we built entdpoints to get a list of all objects (either including the full data, or only the data necessary to show teaser tiles), as well as individual objects and taxonomies. The API template which acts as a controller contains all the necessary switches and selectors to serve the correct response in <100 lines of code. Since we wanted some flexibility regarding the format in which different fields were transmitted over the api, we wrote a function to extract arbitrary page fields from ProcessWire pages and return them as serializable standard objects. There's also a function that takes a Pageimage object, creates multiple variants in different sizes and returns an object containing their base path and an array of variants (identified by their basename and width). We use that one to generate responsive images in the frontend. Check out the code for both functions in this gist. We used native ProcessWire data wherever possible, so as to not duplicate that work in the frontend app. For example: Page names from the backend translate to URLs in the frontend in the form of route parameters for the Vue Router Page IDs from ProcessWire are included in the API responses, we use those to identify objects across the app, for example to store the user's favourites, and as render keys for object lists Taxonomies have their own API endpoints, and objects contain their taxonomies only as IDs (in the same way ProcessWire uses Page References) Finally, the raw JSON data is cached using the cache API and this handy trick by @LostKobrakai to store raw JSON strings over the cache API. Screenshots
  2. 27 points
    This version on the dev branch contains 26 commits (relative to 3.0.142) and is focused primarily in resolving reported issues, and we managed to cover 18 of them in this version. Thanks for the reports and help in our GitHub issues repo. This version represents about 2 weeks of work, and ProcessWire Weekly #284 has good coverage of those that occurred last week. More details about this week's updates can be also be found in the dev branch commit log. There are also some other minor additions and improvements in 3.0.143 as well. My favorite are the improvements to our logs system. It now collapses identical log entries that occur near each other. That means a single recurring log entry (like an error message) won't repeat indefinitely in the log and take up a lot of space. Now it just adds a counter to one log entry and updates the timestamp, rather than duplicating the entire log entry... Much more efficient. When you view a log in Setup > Logs, it identifies these collapsed log entries for you. In addition, the output in the "errors" and "exceptions" logs now have improved readability, isolating error messages and filenames from stack traces. Lastly, the ajax navigation in Setup > Logs now shows logs in newest-to-oldest (modification date) rather than alphabetical, which I find a lot more useful. The log at the top of the list is always the one most recently updated. For core updates in coming weeks, I'm primarily focused on preparing the current dev branch to merge to the master branch, as it's been awhile since the last merge and the master branch is itching for a new version. Most of you reading this already run on the dev branch, but there are many out there that also stick to the master branch, and with all the new stuff on the dev branch, I'd like to get our master up-to-date with this as well. Thanks for reading, have a great weekend!
  3. 23 points
    This latest version of ProcessWire on the dev branch adds a new Inputfield module called “Toggle” that is an alternative to the existing Checkbox Inputfield. It also adds a nicer way to make column width adjustments to your fields when editing a template. This post covers all the details with screenshots and a short video: https://processwire.com/blog/posts/pw-3.0.139/
  4. 20 points
    In this tutorial I want to write about handling special cases and change requests by clients gracefully without introducing code bloat or degrading code quality and maintainability. I'll use a site's navigation menu as an example, as it's relatable and pretty much every site has one. I'll give some examples of real situations and change requests I encountered during projects, and describe multiple approaches to handling them. However, this post is also about the general mindset I find useful for ProcessWire development, which is more about how to handle special cases and still keep your code clean by making the special case a normal one. The problem: Special cases everywhere Since ProcessWire has a hierarchical page tree by default, as a developer you'll usually write a function or loop that iterates over all children of the homepage and displays a list of titles with links. If the site is a bit more complex, maybe you additionally loop over all grandchildren and display those in drop-down menus as well, or you even use a recursive function to iterate over an arbitrary amount of nested child pages. Something like this: function buildRecursiveMenu(Page $root): string { $markup = ['<ul class="navigation">']; foreach ($root->children() as $child) { $link = '<a class="navigation__link" href="' . $child->url() . '">' . $child->title . '</a>'; $children = $child->hasChildren() ? buildRecursiveMenu($child) : ''; $markup[] = "<li class="navigation__item">{$link}{$children}</li>"; } $markup[] = '</ul>'; return implode(PHP_EOL, $markup); } But then the requests for special cases come rolling in. For example, those are some of the requests I've gotten from clients on my projects (by the way, I'm not saying the clients were wrong or unreasonable in any of those cases - it's simply something I needed to handle in a sensible way): The homepage has the company's name as it's title, but the menu link in the navigation should just say "Home". The first page in a drop-down menu should be the top-level page containing the drop-down menu. This was requested because the first click on the top-level item opens the sub-navigation instead of navigating to that page (espcially on touch devices, such as iPads, where you don't have a hover state!), so some visitors might not realize it's a page itself. Some top-level pages should be displayed in a drop-down menu of another top-level page, but the position in the page tree can't change because of the template family settings. The menu needs to contain some special links to external URLs. For one especially long drop-down menu, the items should be sorted into categories with subheadings based on a taxonomy field. In general, my solutions to those requests fall into three categories, which I'll try to elaborate on, including their respective benefits and downsides: Checking for the special case / condition in the code and changing the output accordingly (usually with hard-coded values). Separating the navigation menu from the page tree completely and building a custom solution. Utilizing the Content Management Framework by adding fields, templates and pages that represent special states or settings. Handling it in the code This is the simplest solution, and often the first thing that comes to mind. For example, the first request (listing the homepage as "Home" instead of it's title in the navigation) can be solved by simply checking the template or ID of the current page inside the menu builder function, and changing the output accordingly: // ... $title = $child->template->name === 'home' ? 'Home' : $child->title; $link = '<a class="navigation__link" href="' . $child->url() . '">' . $title . '</a>'; // ... This is definitely the fastest solution. However, there are multiple downsides. Most notably, it's harder to maintain, as each of those special cases increases the complexity of the menu builder function, and makes it harder to change. As you add more special conditions, it becomes exponentially harder to keep changing it. This is the breeding ground for bugs. And it's much harder to read, so it takes longer for another developer to pick up where you left (or, as is often cited, for yourself in six months). Also, now we have a hard-coded value inside the template, that only someone with access to and knowledge of the template files can change. If the client want's the link to say "Homepage" instead of "Home" at some point, they won't be able to change it without the developer. Also, each special case that is hidden in the code makes it harder for the client to understand what's going on in terms of template logic - thus increasing your workload in editorial support. That said, there are definitely some times where I would go with this approach. Specifically: For smaller projects that you know won't need to scale or be maintained long-term. If you are the only developer, and/or only developers will edit the site, with no "non-technical" folk involved. For rapid prototyping ("We'll change it later") Building a custom solution My initial assumption was that the main navigation is generated based on the page tree inside ProcessWire. But of course this isn't set in stone. You can just as easily forgo using the page tree hierarchy at all, and instead build a custom menu system. For example, you could add a nested repeater where you can add pages or links on a general settings page, and generate the menu based on that. There are also modules for this approach, such as the Menu Builder by @kongondo. This approach is not the quickest, but gives the most power to the editors of your site. They have full control over which pages to show and where. However, with great power comes great responsibility, as now each change to the menu must be performed manually. For example, when a new page is added, it won't be visible in the menu automatically. This is very likely to create a disconnect between the page tree and the menu (which may be what you want, after all). You may get ghost pages that are not accessible from the homepage at all, or the client may forgot to unpublish pages they don't want to have any more after they've removed them from the menu. I would only go with this approach if there are so many special cases that there hardly is a "normal case". However, even then it might not be the best solution. The direct relationship between the page tree, the menu structure and page paths are one of the strongest features of ProcessWire in my opinion. If many pages need to be placed in special locations without much structure in regards to what templates go where, maybe you only need to loosen up the template family settings. I have built one site without any template family restrictions at all - any page of any template can go anywhere. It's definitely a different mindset, but in this case it worked well, because it allowed the client to build custom sections with different page types grouped together. It's a trade-off, as it is so often, between flexibility and workload. Weigh those options carefully before you choose this solution! Utilizing the CMF This is the middle ground between the two options above. Instead of building a completely custom solution, you keep with the basic idea of generating a hierarchical menu based on the page tree, but add fields and templates that allow the editor to adjust how and where individual pages are displayed, or to add custom content to the menu. of course, you will still write some additional code, but instead of having hard-coded values or conditions in the template, you expose those to the client, thereby making the special case one of the normal cases. The resulting code is often more resilient to changing requirements, as it can not one handle that specific case that the client requested, but also every future change request of the same type. The key is to add fields that enable the client to overwrite the default behaviour, while still having sensible defaults that don't require special attention from the editor in most cases. I'll give some more examples for this one, as I think it's usually the best option. Example 1: Menu display options This is probably the first thing you thought of for the very first change request I mentioned (displaying the homepage with a different title). Instead of hard-coding the title "Home" in the template, you add a field menu_title that will overwrite the normal title, if set. This is definitely cleaner than the hard-coded value, since it allows the client to overwrite the title of any page in the menu. I'll only say this much in terms of downsides: Maybe the menu title isn't really what the client wanted - instead, perhaps they feel limited because the title is also displayed as the headline (h1) of the page. In this case, the sensible solution would be an additional headline field that will overwrite the h1, instead of the menu_title field. Which fields are really needed is an important consideration, because you don't want to end up with too many. If each page has fields for the title, a headline, a menu title and an SEO-title, it's much more complicated than it needs to be, and you will have a hard time explaining to the client what each field is used for. Another example in this category would be an option to "Hide this page in the menu". This could be accomplished by hiding the page using the inbuilt "hidden" status as well, but if it's hidden it won't show up in other listings as well, so separating the menu display from the hidden status might be a good idea if your site has lots of page listings. Example 2: "Menu link" template One solution that is quite flexible in allowing for custom links to pages or external URLs is creating a menu-link template that can be placed anywhere in the page tree. This templates can have fields for the menu title, target page and/or external target URL. This way, you can link to another top-level page or an external service inside a drop-down menu, by placing a Menu Link page at the appropriate position. This is also a clean solution, because the navigation menu will still reflect the page tree, making the custom links visible and easily editable by the editors. A minor downside is that those templates are non-semantical in the sense that they aren't pages with content of their own. You'll need to make sure not to display them in listings or in other places, as they aren't viewable. It may also require loosening up strict family rules - for example, allowing for Menu Link pages to be placed below the news index page, which normally can only hold news pages. Example 3: Drop-down menu override This one is a more radical solution to override drop-down menus. You add a repeater field to top-level pages, similar to the one mentioned as a custom solution, where you can add multiple links to internal pages or URLs. If the repeater is empty, the drop-down menu is generated normally, based on the sub-pages in the page tree. But if the repeater contains some links, it completely overrides the drop-down menu. It's similar to the fully custom solution in that as soon as you override a sub-menu for a top-level page, you have to manually manage it in case the page structure changes. But you can make that decision for each top-level page individually, so you can leave some of them as is and only have to worry about the ones that you have overwritten. Again, this offers sensible defaults with good customizability. A downside is that the mixed approach may confuse the client, if some changes to the page tree are reflected in the drop-down menu directly, while others don't seem to have any effect (especially if you have multiple editors working on a site). Finding the right solution So how do you choose between the approaches? It depends on the client, the requirements, and on what special cases you expect and want to handle. Sometimes, a special request can be turned down by explaining how it would complicate editorial workflows or have a negative impact on SEO (for example, if you risk having some pages not accessible from the homepage at all). Also, make sure you understand the actual reason behind a change request, instead of just blindly implementing the suggestion by the client. Often, clients will suggest solutions without telling you what the actual problem is they're trying to solve. For example: In one case, I implemented the drop-down override mentioned in example three. However, what the client really wanted was to have the top-level page as the first item in the drop-down menu (see the example requests mentioned above). So they ended up overwriting every single drop-down menu, making the menu harder to maintain. In this case, it would have been better to go with a more specialized solution, such as adding a checkbox option, or even handling it in the code, since it would have been consistent throughout the menu. Another example was mentioned above: If the client requests an additional "Menu title" field, maybe what they really need is a "Headline" field. I recommend reading Articulating Design Decisions by Tom Greever; it includes some chapters on listening to the client, finding out the real reason behind a change request, and responding appropriately. It's written from a design perspective, but is applicable to development as well, and since UX becomes more important by the day, the lines between the disciplines are blurred anyway. Conclusion I realize now this reads more like a podcast (or worse, a rant) than an actual tutorial, but hopefully I got my point across. ProcessWire is at is greatest if you utilize it as a Content Management Framework, creating options and interfaces that allow for customizability while retaining usability for the client / editor. I usually try to hit a sweet spot where the editors have maximum control over the relevant aspects of their site, while requiring minimal work on their part by providing sensible defaults. Above, I listed some examples of requests I've gotten and different solutions I came up with to handle those with custom fields or templates. Though in some cases the requirements call for a custom solution or a quick hack in the template code as well! What are some of the special requests you got? How did you solve them? I'd love to get some insights and examples from you. Thanks for reading!
  5. 20 points
    I've been in Minnesota all week for a family reunion and only a few minutes at the computer every day, so I don't have a core update or anything worthwhile to write about this week. But now I'm headed back home so will be back to a regular schedule next week. Hope that you have a great weekend!
  6. 19 points
    The Page Hit Counter module for ProcessWire implements a simple page view counter in backend. Page views of visitors are automatically tracked on defined templates, with monitoring of multiple page views. This gives you a quick overview of how many visitors have read a news or a blog post, for example, without first having to open complex tools such as Google Analytics. This module quickly provides simple information, e.g. for editors. Or, for example, to sort certain news by most page views. For example for "Trending Topics". Works with ProCache and AdBlockers. With a lightweight tracking code of only ~320 bytes (gzipped). And no code changes necessary! In addition GDPR compliant, since no personal data or IP addresses are stored. Only session cookies are stored without information. In addition, there are some options, for example filtering IP addresses (for CronJobs) and filtering bots, spiders and crawlers. You can also configure the lifetime of the session cookies. Repeated page views are not counted during this period. It is also possible to exclude certain roles from tracking. For example, logged in editors who work on a page are not counted as page views. Sort by hits and access page views (hit value) Each trackable template has an additional field called phits. For example, you want to output all news sorted by the number of page views. // It is assumed that the template, e.g. with the name "news", has been configured for tracking. $news = $pages->find("template=news, sort=-phits"); To output the page views of a tracked page, use: echo $page->phits; Example: Reset counter per API $modules->get("PageHitCounter")->resetPageViews("template=whatever", false); Example: Tracking a page hit via API and jQuery If you want to track a template that does not represent a full page to automatically inject a tracking script, you can define allowed API templates in the module that you can track. Below is an example of how you can track a click on news tag using jQuery. This will allow you to find out which keywords are clicked the most. For example, you can sort and display a tag cloud by the number of hits. Suppose your keywords have the template "news_tag". The template "news_tag" was also configured in the Page Hit Counter Module as a trackable API template. Example PHP output of keywords / tags: // Required: the data attribute "data-pid" with the ID of the template to be tracked. echo $pages->find("template=news_tag, sort=-phits")->each("<a href='{url}' class='news_tag' data-pid='{id}'>{title}</a>"); Example Tracking Script with jQuery: /** * Required: Data attribute "data-pid" with the ID of the news tag template * Required: Send the POST request to the URL "location.pathname.replace(/\/?$/, '/') + 'phcv1'" * Required: The POST parameter "pid" with the ID of the template */ $(function(){ if($('a.news_tag').length > 0) { $('a.news_tag').each(function(){ var tPID = $(this).data("pid"); if(tPID) { $(this).on("click", function(){ $.post(location.pathname.replace(/\/?$/, '/') + 'phcv1', {pid: tPID}); }); } }); } }); So simply every click on a tag is counted. Including all checks as for automatic tracking. Like Bot Filtering, Session Lifetime, etc. Notice: Tracking with URL segments If the option "Allow URL Segments" is activated on a template, the hits are only counted if the base URL of the page is called. If you want the hit to be counted even when a segment is requested, you MUST configure the segments in the template configuration. How to do this can be found here. If you use dynamic segments, configure them as RegEx. There is currently no other option. The problem is that the Page Hit Counter hooked into the PageNotFound process. If URL segments are allowed but not defined, a 404 is never triggered. This means that the Page Hit Counter cannot be called. _______________________________________________________ Background: This module is the result of a customer requirement, where the editors are overwhelmed with analytics or no tracking tools were allowed to be used. However, a way had to be found to at least count page views in a simple form for evaluations. Furthermore, by using ProCache, a way had to be found to count views of a page without clearing the cache. _______________________________________________________ Pros Automatic Page View Tracking Lightweight tracking code, only ~320 bytes (gzipped) No code or frontend changes necessary Works with ProCache! Even if no PHP is executed on the cached page, the tracking works Works with browser AdBlockers No cache triggers (for example, ProCache) are triggered. The cache remains persistent GDPR compliant, session-based cookie only, no personal information Filtering of IPs and bots possible Exclude certain roles from tracking Ability to reset Page Views Works with all admin themes Counter database is created as write-optimized InnoDB API to track events for templates that are not viewable No dependencies on libraries, pure VanillaJS (Automatic tracking script) Works in all modern browsers Pages are sortable by hits Cons Only for ProcessWire version 3.0.80 or higher (Requires wireCount()) Only for PHP version 5.6.x or higher No support for Internet Explorer <= version 9 (Because of XMLHttpRequest()) No historical data, just simple summation (Because of GDPR) Segment URLs can only be counted if the segments are defined Planned Features / ToDos API access to hit values Since version 1.2.1 Possibility to sort the pages by hits (Request by @Zeka) Since version 1.2.0 Don't track logged in users with certain roles (Request by @wbmnfktr) Since version 1.1.0 Possibility to reset the counter for certain pages or templates (Request by @wbmnfktr) Since version 1.1.0 Better bot filter Since version 1.1.0 Disable session lifetime, don't store cookies to track every page view (Request by @matjazp) Since version 1.2.1 Option to hide the counter in the page tree (Request by @matjazp) Since version 1.2.1 Option to hide the counter in the page tree on certain templates Since version 1.2.1 API to track events for templates that are not viewable Since version 1.2.2 Changelog 1.2.6 Bug-Fix: Set the counter of a cloned page to 0 Enhancement: The function for resetting counters is now available in the module as a public function to reset counters via own scripts on the API side (Request by @VeiJari) Enhancement: Documentation improvement API reset 1.2.5 Bug-Fix: When counting 404 hits, cookies are no longer set. The session lifetime is deactivated for the 404 page Enhancement: Documentation improvement regarding URL segments 1.2.4 Bug-Fix: Resetting the counters on system pages (e.g. 404) does not work (Reported by wbmnfktr) Bug-Fix: Tracking endpoint is logged as 404 if module "Jumplinks" is installed (Reported by wbmnfktr) Enhancement: Corrected few typos (Merged from Sergio #6 – THX!) 1.2.3 Bug-Fix: Tracking script triggers 404 if pages are configured without slash (#3) Reported by @maxf5 Enhancement: Reduction of the tracking script size if it's gzipped (~320 bytes) Enhancement: Documentation improvement Enhancement: Corrected few typos 1.2.2 New feature: API to track events for templates that are not viewable Enhancement: Documentation improvement 1.2.1 API access to hit values Use $page->phits Bug-Fix: No tracking on welcomepage (Reported by wbmnfktr; Thx to matjazp) Bug-Fix: Tracking script path on subfolders (Reported by matjazp) Bug-Fix: Tracking on pages with status "hidden" Enhancement: Change database engine to InnoDB for phits field Enhancement: Option to disable session lifetime set session lifetime to 0, no cookies Enhancement: Better installation check Enhancement: AJAX Request asyncron Enhancement: Reduction of the tracking script size by ~20% Enhancement: Option to hide the counter in the page tree You can output the counter with the field name "phits" Enhancement: Option to hide the counter in the page tree on certain templates Enhancement: Option for activate general IP validation Enhancement: Reduction of tracking overhead up to ~30ms Enhancement: Better bot list for detection 1.2.0 New feature: Sort pages by hits – New field phits Migrate old counter data to new field 1.1.0 New feature: Exclude tracking of certain roles New feature: Reset Page Views Better bot filter and detection 1.0.0 Initial release Notes By default, the page views are stored as INT in the database. This allows a maximum counter value of 4.2 billion views (4,294,967,295) per page. If you need more, change the type to BIGINT directly in the database. But I recommend to use Google Analytics or similar tools if you have such a large number of users. _______________________________________________________ Download GitHub: ProcessWire Page Hit Counter (Version 1.2.6) PW Module Directory: ProcessWire Page Hit Counter (Version 1.2.6) Install via ProcessWire (Classname): PageHitCounter _______________________________________________________ Update information If you have used version 1.2.1 from the DEV branch, please replace it completely with the new master version.
  7. 19 points
    MarkupMenu is a markup module for generating menu trees. When provided a root page as a starting point, it generates a navigation tree (by default as a HTML "<ul>" element wrapped by a "<nav>" element) from that point onwards. If you've also provided it with current (active) page, the menu will be rendered accordingly, with current item highlighted and items rendered up to that item and its children (unless you disable the "collapsed" option, in which case the full page tree will be rendered instead). Modules directory: https://modules.processwire.com/modules/markup-menu/ GitHub repository: https://github.com/teppokoivula/MarkupMenu Usage As a markup module, MarkupMenu is intended for front-end use, but you can of course use it in a module as well. Typically you'll only need the render() method, which takes an array of options as its only argument: echo $modules->get('MarkupMenu')->render([ 'root_page' => $pages->get(1), 'current_page' => $page, ]); Note: if you omit root_page, site root page is used by default. If you omit current_page, the menu will be rendered, but current (active) page won't be highlighted etc. A slightly more complex example, based on what I'm using on one of my own sites to render a (single-level) top menu: echo $modules->get('MarkupMenu')->render([ 'current_page' => $page, 'templates' => [ 'nav' => '<nav class="{classes} menu--{menu_class_modifier}" aria-label="{aria_label}">%s</nav>', 'item_current' => '<a class="menu__item menu__item--current" href="{item.url}" tabindex="0" aria-label="Current page: {item.title}">{item.title}</a>', ], 'placeholders' => [ 'menu_class_modifier' => 'top', 'aria_label' => 'Main navigation', ], 'include' => [ 'root_page' => true, ], 'exclude' => [ 'level_greater_than' => 1, ], ]); Note: some things you see above may not be entirely sensible, such as the use of {menu_class_modifier} and {aria_label} placeholders. On the actual site the "nav" template is defined in site config, so I can define just these parts on a case-by-case basis while actual nav markup is maintained in one place. Please check out the README file for available render options. I'd very much prefer not to keep this list up to date in multiple places. Basically there are settings for defining "templates" for different parts of the menu (list, item, etc.), include array for defining rules for including in the menu and exclude array for the opposite effect, classes and placeholders arrays for overriding default classes and injecting custom placeholders, etc. 🙂 MarkupMenu vs. MarkupSimpleNavigation TL;DR: this is another take on the same concept. There are many similarities, but also some differences – especially when it comes to the supported options and syntax. If you're currently using MarkupSimpleNavigation then there's probably no reason to switch over. I'd be surprised if someone didn't draw lines between this module and Soma's awesome MarkupSimpleNavigation. Simply put I've been using MSN (...) for years, and it's been great – but there are some issues with it, particularly in the markup generation area, and it also does some things in a way that doesn't quite work for me – the xtemplates thing being one of these. In some ways less about features, and more about style, I guess 🙂 Anyhow, in MarkupMenu I've tried to correct those little hiccups, modernise the default markup, and allow for more flexibility with placeholder variables and additional / different options. MarkupMenu was built for ProcessWire 3.0.112+ and with PHP 7.1+ in mind, it's installable with Composer, and I have a few additional ideas (such as conditional placeholders) still on my todo list. One more small(ish) difference is that MarkupMenu supports overriding default options via $config->MarkupMenu. I find myself redefining the default markup for every site, which until now meant that each site had a wrapper function for MarkupSimpleNavigation (to avoid code / config repetition), and this way I've been able to leave that out 🙂 Requirements ProcessWire >= 3.0.112 PHP >= 7.1.0 If you're working on an earlier version of ProcessWire or PHP, use MarkupSimpleNavigation instead.
  8. 17 points
    Version 3.0.135 of ProcessWire on the dev branch focuses on adding additional layers of security via .htaccess updates, adds clarity to debug mode, and improves upon the installer— https://processwire.com/blog/posts/pw-3.0.135/
  9. 15 points
    This module is (or will be) the successor of RockGrid. It uses http://tabulator.info/ as grid library that is completely open sourced (MIT, no commercial extensions) and as far as I've seen so far at least as powerful as ag-grid, which RockGrid is using. GOALS: Support various data sources (RockFinder, JS, PHP, SQL) Supported only in PW Backend + Uikit Easy setup of new fields INSTALLATION: Just install the main module. RockMarkup is a required dependency. Please also have a look at the RockMarkup examples before jumping into RockTabulator! DEMO: STATUS: The module is early alpha and can be downloaded from github: https://github.com/BernhardBaumrock/RockTabulator It took me two weeks of rewriting RockMarkup/RockTabulator to get where I am now and I think I'm quite happy with the setup. There might be some breaking changes though in the near future. At the moment the main features that should be working properly are: Data sources: SQL, PHP, JS Sandbox for easy setup Multilang This means basically everything that is possible with Tabulator is also possible with RockTabulator. Though, there are no custom helpers/plugins yet. That's the next step and whoever is willing to help, please give me a shout 🙂 If you have any questions or ideas please let me know 🙂
  10. 14 points
    Thanks to @Macrura for the idea behind this module. Page Field Info Adds information about options in Page Reference fields. Supports InputfieldSelect and inputfields that extend InputfieldSelect: InputfieldSelect InputfieldRadios InputfieldSelectMultiple InputfieldCheckboxes InputfieldAsmSelect Requires ProcessWire >= 3.0.61 and AdminThemeUikit. Screenshots Field config Example of changes to inputfield Example of info field filled out in Page Edit Installation Install the Page Field Info module. Configuration In the Input tab of the settings for a Page Reference field... Tick the "Add info tooltips to options" checkbox to enable tooltips for the options in the field. Tooltips are not possible for Select or AsmSelect inputfield types so for those types you would want to tick the next option. Tick the "Append info about selected options" checkbox to append information about the selected options to the bottom of the inputfield. If the Page Reference field is a "multiple pages" field then the info for each selected option will be prefixed with the option label (so the user will know what option each line of info relates to). In the "Info field" dropdown select a text field that will contain information about the page, to be used in the tooltips and appended info. Of course this field should be in the template(s) of the selectable pages for the Page Reference field. Hook In most cases the "Info field" will supply the text for the tooltips and appended info, but for advanced usages you can hookPageFieldInfo::getPageInfo() to return the text. For example: $wire->addHookAfter('PageFieldInfo::getPageInfo', function(HookEvent $event) { $page = $event->arguments(0); // The page $inputfield = $event->arguments(1); // InputfieldPage $field = $event->arguments(2); // The Page Reference field $info = $event->return; // Text from the info field, if any // Set some custom text as the $event->return... }); https://github.com/Toutouwai/PageFieldInfo https://modules.processwire.com/modules/page-field-info/
  11. 14 points
    ProcessWire 3.0.125 has several useful new $sanitizer methods and options, as well as new ways to access them directly from the $input API variable. This makes dealing with user input even easier than it was before. This version also brings updates to our translation functions and improvements to our API documentation: https://processwire.com/blog/posts/pw-3.0.125/
  12. 14 points
    There is already some information on how to do this but it's a bit scattered, so here's a quick tutorial... This is how $session->CSRF->hasValidToken() handles AJAX requests, by getting the token name and value from request headers: if($this->config->ajax && isset($_SERVER["HTTP_X_$tokenName"]) && $_SERVER["HTTP_X_$tokenName"] === $tokenValue) { $valid = true; } else if($this->input->post($tokenName) === $tokenValue) { $valid = true; } So all we need to do is to pass the name and value to our JavaScript and set the right headers with our AJAX request. We could instead of setting headers put the name and value pair into the body of a POST request, as the second conditional doesn't care if the POST request is received via AJAX or not. But this limits the request to POST and the body data type to either multipart/form-data or application/x-www-form-urlencoded (because that's all that PHP's $_POST superglobal will handle). Admittedly this might be 99% of use cases, but for example if a GET request invokes significant processing then CSRF protection could be used to prevent a DOS attack on the endpoint. So, for the sake of a bit more flexibility, and to be more in keeping with what PW expects, we'll use request headers. I find it neater to write the token name and value into HTML and retrieve that using JavaScript DOM functions, instead of writing JavaScript with PHP 😝. So somewhere, maybe in init.inc.php, get the token name and value: $csrfTokenName = $session->CSRF->getTokenName(); $csrfTokenValue = $session->CSRF->getTokenValue(); Then in a global footer template: <div class="js-csrf-token" style="display:none;" data-csrf-token-name="<?php echo $csrfTokenName; ?>" data-csrf-token-value="<?php echo $csrfTokenValue; ?>"></div> Now to retrieve the token in JavaScript: function getCsrfToken() { const csrfTokenNode = document.getElementsByClassName('js-csrf-token')[0]; return { name: csrfTokenNode.getAttribute('data-csrf-token-name'), value: csrfTokenNode.getAttribute('data-csrf-token-value'), } } Then in any AJAX request: const csrfToken = getCsrfToken(); let xhr = new XMLHttpRequest(); // ... // open the request and do some configuration // ... xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); // tell the server and PW that it's an AJAX request xhr.setRequestHeader('X-' + csrfToken.name, csrfToken.value); // create a header with the token // ... // send the request // ... And that's all, in the endpoint just do... if ($session->CSRF->hasValidToken()) { // works for AJAX! } This has used the default CSRF token created for every session, but to create and use a different token just specify an id: $csrfTokenName = $session->CSRF->getTokenName('myAjaxToken'); // this creates a token if one with the given id doesn't exist $csrfTokenValue = $session->CSRF->getTokenValue('myAjaxToken'); and... if ($session->CSRF->hasValidToken('myAjaxToken') { // ... }
  13. 14 points
    Pages At Bottom Keeps selected pages at the bottom of their siblings. A "bottom page" will stay at the bottom even if it is drag-sorted to a different location or another page is drag-sorted below it (after Page List is refreshed the bottom page will still be at the bottom). Newly added sibling pages will not appear below a bottom page. The module also prevents the API methods $pages->sort() and $pages->insertAfter() from affecting the position of bottom pages. Note: the module only works when the sort setting for children on the parent page/template is "Manual drag-n-drop". Why? Because you want some pages to always be at the bottom of their siblings for one reason or another. And someone requested it. 🙂 Usage Install the Pages At Bottom module. Select one or more pages to keep at the bottom of their siblings. If you select more than one bottom page per parent then their sort order in the page list will be the same as the sort order in the module config. https://github.com/Toutouwai/PagesAtBottom https://modules.processwire.com/modules/pages-at-bottom/
  14. 13 points
    This week I was catching up with client work after traveling last week, but some of that work overlapped with a focus on WireMail modules. As a result, this week I’ve released two new WireMail modules, and also have information in this blog post on how you can configure two existing WireMail modules (WireMailSmtp and WireMailPHPMailer) to use Gmail as the SMTP sender— https://processwire.com/blog/posts/wiremail-modules-and-gmail/
  15. 13 points
    This week we take a closer look at the upcoming ProMailer module with a lot more details and screenshots. Plus an update on next steps with the new website and development schedule in the weeks ahead— https://processwire.com/blog/posts/promailer-preview/
  16. 11 points
    Hello Everyone! I'm new here, just signed up. I've discovered PW few days ago (what a shame...) and I'm angry for myself that I lost several years of my life... I'm a homegrown webdeveloper since 2005, started from pure HTML, then e107, Textpattern and from 2008 I work(ed) only with WordPress. I've made over a hundred of sites. Most of them are simple blogs but my main site is quite large (with over a 10k entries and over 100k of media). It's outdated a little bit so I was planning to modify and refresh it lately but then... Gutenberg happened. Gutenberg as an editor is unintuitive, buggy, produces massive problems and it's incompatibile with most of the things I'm using (it seems to be the same story for hundreds of websites built on WP). And though the whole WP community is frustrated and angry, it's been forced with WP 5.0 as the beginning of rebuilding WP as a platform into... hmm... next Wix? I was trying to discuss - my comments were deleted and my account has been blocked (as many others). After 8 years of developing WP... Well... I've frozen the site for updates and started looking elswhere. I have few advanced demands so tried Drupal, then played a little with Craft, Codeigniter and Laravel (I'm not a programmer per se). And after few recommendations I've finally found You. Guys... I'm so thrilled and emotional. I can't stop reading and following tutorials on localhost. PW is THE TOOL I was searching my whole life! I'm going to study everything very carefully and next year I'm going to rebuild slowly (there's no rush) my main site with PW. Back to Gutenberg: I understand where it aims to be and what it tries to accomplish BUT this is wrong on so many levels. As far as I can see, a barebone PW installation is far more flexible and useful then WP with 30 plugins. And obviously you know it perfectly. So I'm begging you: no Gutenberg here... 😉 Cheers to Ryan and Everyone! Greetings from Poland!
  17. 10 points
    I am back again with another crazy module that might help someone. This time I am using the native Tfa class to add FIDO/U2F support. this includes Yubikeys 🙂 Sadly I cant seem to find a way to do multiple keys at once. and it seems you can only have one Tfa method at a time so not ideal but it was a fun challenge to code and maybe someone will a use for it Github: https://github.com/adamxp12/Processwire-TfaU2F ProcessWire Modules: https://modules.processwire.com/modules/tfa-u2-f/ The code is not the neatest and has limited comments but if you understand the Tfa class it should be quite easy to break apart. And here is a demo of signing in using the Yubikey as the 2nd factor
  18. 10 points
    When I installed Processwire for the first time, I noticed that Admin sessions were not lasting for a long time and it was very common to be logged out. Reading this forum I found useful information, but I could not solve this issue. I tried to modify site/config.php adding $config->sessionExpireSeconds with different values, but without success (for your awareness sessionExpireSeconds default value is 86400 seconds, ie 1 day .. so quite enough!). OK, I had no time to go deeper, so I kept doing what I had to do ... being happily logged out … time to time. Few weeks later while studying Session.php in wire/core/ I got the hint (please read the commented text) which helped me to understand how to solve it: if(ini_get('session.save_handler') == 'files') { if(ini_get('session.gc_probability') == 0) { // Some debian distros replace PHP's gc without fully implementing it, // which results in broken garbage collection if the save_path is set. // As a result, we avoid setting the save_path when this is detected. } else { ini_set("session.save_path", rtrim($this->config->paths->sessions, '/')); } } Uh! Yes, my VPS uses Debian 9 ! So I quickly did a test: echo ini_get('session.gc_probability'); echo ini_get('session.gc_divisor'); echo ini_get('session.gc_maxlifetime'); The session.gc_maxlifetime was rightly set to 86400, but session.gc_probability was 0 ! As you can easily get from Session.php routine, if probability is 0 bye bye PHP's session garbage collection and of course PW will not set the save_path. But why probability is set to 0 ?? This is due to the fact Debian/Ubuntu Linux overrides standard PHP’s session garbage collection by setting session.gc_probability to 0. As a consequence PHP’s standard session garbage collection will never run. Instead Debian/Ubuntu sets a specific Cron job for garbage collection with a duration of 1440 seconds, ie 24 minutes. This determines the max session duration. If you try to change session.gc_maxlifetime through $config->sessionExpireSeconds=86400 or ini_set('session.gc_maxlifetime', 86400) it will have no effect and sessions will be deleted at intervals of 24 minutes (or within 54 minutes). The Cron job garbage collection duration is defined at php.ini level, and runtime changes to session.gc_maxlifetime will not affect the Cron job timeout. Uh! Of course the simplest solution would be to change session.gc_maxlifetime in php.ini. However sometimes this is not possible (shared hosting). Another point to consider is that this change may affect multiple applications running on your server. Instead of modifying php.ini, personally I preferred to enable PHP's session garbage collection locally for Processwire only. Majority of the code is already written in PW Session.php, we have just to take benefit of it. Let's go step by step. Let's open site/config.php and do the following modifications (I choose to implement 14400 seconds session time, ie 4 hours): /** * Reduce inactivity timeout (original 86400 = 1 day) * How many seconds of inactivity before session expires */ $config->sessionExpireSeconds = 14400; /** * Enable Session Garbage Collection * Garbage Collection is disabled in Debian as default (probability set to zero) * Enable session garbage collection with a 1% chance of running on each session_start(); * Chance calculated as gc_probability/gc_divisor * Session path is defined inside wire/core/Session.php and points to site/assets/sessions * Thanks to this modification session now takes into account gc_maxlifetime set in config */ ini_set('session.gc_probability', 1); ini_set('session.gc_divisor', 100); Do not forget to lock site/config.php after modifying it. Here it is! We are done! After we have set gc_maxlifetime (through sessionExpireSeconds), enabled the garbage collection (gc_probability=1), and set its intervention probability (gc_divisor=100 ie 1% chance), we can rely on Session.php where the a session path (session_save_path()) is defined, and session is started (session_start()). In the development session of our browser let's look for our session cookie: if you go to site/assets/sessions you will find your corresponding session: Please note that during development phase, likely having little traffic, the number of sessions may grow and they will not be deleted. This depends from the 1% chance of deletion. If you want to test garbage collection is properly working or just to make sure sessions files are regularly cleaned during development, you can decrease gc_divisor to a lower rate(10=>10% ... 2=>50%). Do not forget to reestablish gc_divisor = 100 when moving to production. I hope some can find that tutorial useful in the future ... and may save several log outs!
  19. 10 points
    Here I'm introducing FlipFall Magazine, our multi-topic blog. It used to run on Wordpress but we recently switched it to ProcessWire. This was done the usual way we do it when converting our sites: install ProcessWire in a subfolder of the original Wordpress site under a hard-to-guess name, set up the new site there, then move content over manually, inspecting and updating each article as needed. After the ProcessWire site was ready, when we un-installed the Wordpress one and moved the ProcessWire site up one level to the document root, and... done. We used the W3CSS framework because it handles the responsive breakpoints so well (no extra work for us 😉), and it tends to default to a clean modern look. We wanted full control over the back end of the site and do customized things without having to hire a developer. As FlipFall has grown, the ability to have in-house back-end control has become even more important. Case example: Ad partners - we now can quite easily, if we wish, place different ads on different topics on this blog at a moment's notice - no need to hire a developer. Changes like this can be implemented in-house right away. The fact that we can easily incorporate this sort of thing is really nice when we're talking with potential ad partners. Helpful features of ProcessWire during this experience: The ability to export and import fields in PW was key here. We had a few other article-style sites I had done in PW recently, so having the same types of templates was very helpful. We were able to export fields and templates from our existing sites and import into FlipFall as a starting base point. No need to re-invent the wheel here! Another helpful ability of PW was when we were dealing with the categories. We built the site without category templates (but with the category page field), then added the templates in later as needed - no disruption. In other words, a template-less page field worked perfectly for the categories until we needed the template, then we just created the template. Not every feature of the site needs to be thought out in absolute full beforehand, some can be added in later as needed. Very extensible and convenient. Internal links within blog posts worked well using the page select option in the link button on the editor. When moving out of staging folder and into document root, the links auto-updated, which was nice. Creating new articles is a breeze, because under PW our fields are now customized for our needs: we created all of the fields we need and none of the ones we don't. Again, this is unlike most blogging CMS's, where they try to guess what you want (and usually get it wrong). Even for people who are solely doing blogs/article sites, I feel that ProcessWire is a much better option than most blog-specific platforms, because of PW's flexibility. The only thing I miss about Wordpress is the ability to auto-schedule post publication, which for the serious blogger is important. For example, there were some occasions on some of our other blogs where we needed to schedule posts to auto-publish at 3:01am Eastern Time to allow a time-sensitive post to come out as early in time of day as possible on publication date (3:01 am US Eastern time ensures it's that same day 12:01 am on Pacific time). We were delighted to see that ProcessWire is much, much lighter on resources of the hosting environment than the same site on Wordpress - we could see this empirically on our web hosting stats before and after the switch.
  20. 10 points
    Here is GoodKidsClothes.com, a blog about kids clothes - news, style tips, sale alerts, and more. GoodKidsClothes.com originally ran on Wordpress, and I moved it to Processwire recently, the new Processwire version is shown above. There was a fair amount to change over, since it had 4 years on Wordpress before switching! I kept the colors, background, etc in line with what it had been before - a soft, friendly look. I wasn't seeking for it to be identical to its previous appearance, just similar but updated/better/more fun. The html I did from scratch, although I used the W3CSS framework. I love W3CSS because they handle all the responsive breakpoints, and the default styling is a clean flat modern look with plenty of great pre-sets. The reason I moved this site over to ProcessWire was not looks but actually functionality: the new Wordpress editor (Gutenburg) had just come out - one of its quirks is that it couldn't keep up with my typing, so I had to literally slow down my typing, which really defeats the purpose of WP as a blogging CMS. (Processwire's editor keeps up with me just fine). Also it was anyway time for me to manually go through and update old articles, put in new affiliate links etc, so I decided to do everything all at once and switch over to ProcessWire. In case anyone is wondering, the switch-over was manual since I was going to examine every article I'd written to either a) update it, b) move it to another of my sites, or c) trash it. This was not time-efficent but this way I wound up with being certain everything was up to date content-wise, plus no unwanted bloat (like extra WP fields) could make its way into my Processwire database. I simply installed Processwire via 1-click Softaculous install in a subdirectory of the original Wordpress site, with the original site still running. Then after I had the Processwire version fully finished (this took several weeks), I simply uninstalled the Wordpress version and moved the Processwire site into the document root. This way I had less than 1 minute downtime. UX/UI The first menu link is an all-abilities-inclusive version of "skip to content". The actual text displayed depends on which page template is being used ( this text is assigned in _init). For example, the Article template will display "Scroll to article", while Search Results template will display "Scroll to results". Link styling in the body of article content is designed for both the desktop and mobile user, with simultaneous underlining and highlighting showing the entire link region to aim for when tapping on mobile. On the home page and some other templates as needed, skip links are available within the page. They offer the option to skip past a series of links such as social sharing links, pager navigation, etc for a) the screen reader user and b) the fully-sighted keyboard-only user (no mouse). These links only become visible to the eye when focus comes upon them via tabbing. Tab through the home page to see it in action - this is the template where the most skip links have been needed. Cookie manager - originally I used a slider for turning Google Analytics tracking on/off but changed to checkbox because I could not work out a way to manipulate slider without mouse. Newsletter - field, and feed One feature of this site is its newsletter, and you'll see here how Processwire shines. The setup was (and still is) that on days when a new blog post relevant to children's clothes is published, subscribers get a brief email notifying them of the new article and linking to it. This is all handled by MailChimp, which I highly recommend. Under the old Wordpress system, I had to use categories to classify which of the posts wound up going into the newsletter (kids clothes) and which posts didn't (other topics like parenting etc). There was always the chance that under default WP behavior, things would be classified incorrectly if I forgot to specify categories. Under Processwire, I've set up the article template to have a field called "Newsletter" which is a simple drop-down choice of "For newsletter" or "omit from Newsletter". There is no default value, and it's a required field, ensuring that I do remember to specify it one way or another. It's such a relief to do it this way! My newsletter feed was easy to customize under Processwire: I created a feed template that selected a) all the pages using the article template that also had b) the "For newsletter" field selected, and those are listed at /newsletter in feed format. Please note that this feed may be empty right now - I omitted my existing articles from newsletter feed as subscribers have already seen them, and haven't had time to write new articles yet. To clarify, I'm expecting the newsletter feed at /newsletter to only ever be read by MailChimp, although it's certainly possible to be used by feed readers or read by humans. XML sitemap Under Processwire, I was able to generate a list of articles in XML format at /sitemap.xml that I can then submit to Google as the XML sitemap for this site. Best of all, unlike web-based crawler-type sitemap generators which generate a static sitemap that you then upload to the document root, my Processwire /sitemap.xml auto-generates each time the page is loaded, so it's always auto-updated - any changes in back office like article deletion, unpublishing, adding new articles etc are reflected automatically in /sitemap.xml. Some advantages of Processwire features when templating 1. _init.php file - my theme was designed for subsequent use in my other sites, so selected pages for use in nav menu (About, Privacy Policy) are automagically "found" in _init.php as follows: $pp = $pages->findOne("template=BN-infopage, sort=created, title*=Privacy"); $ab = $pages->findOne("template=BN-infopage, sort=created, title*=About"); 2. Made use of Processwire's built-in retina-friendly image resizing class, class="hidpi" to ensure social sharing icon links render at a decent resolution on mobile screens. Other info To check my html and to help identify problems that are not visible to the eye, I found it incredibly helpful to use the "audit" feature available on Chromium and other Chrome-based browsers. (F12->Audit-> select options you want). The order of the blogroll looks a little odd at first glance but it's ordered based purely on publication date. However, I updated some articles and they display the last updated date, which makes the blogroll look like it's not in date order even though it's in publication date order. Also some dates (the older article dates) reflect a user-specified date field, to show the article was valid at the time it was written (e.g. time-sensitive info such as reviews, sale alerts, etc). I'd be happy to explain further if anyone's interested. Moving forward as I write more articles, there should not be an issue, since I usually update only on or very soon after the publication date, so we should not expect to see wildly different dates on sequential articles from here on in.
  21. 10 points
    The notion of core in MODX might be different from the one in ProcessWire (maybe?). Btw, I don't know if you've seen this post on MODX to ProcessWire welcome brief? It might be of interest 🙂. One of the philosophies of ProcessWire, one that makes it very extensible is the fact that everything, all its building blocks is a module. Yes, even the admin (backend) is a collection of modules. Modules are just PHP classes. You have your classes (i.e. modules, or the site's custom classes) and these live here: /site/modules/. ProcessWire has its own core classes: the ones designated as modules live here: /wire/modules/. The are other classes, in fact, the main classes of ProcessWire, aka the core, and these live here: /wire/core/. In order for ProcessWire to know about and register your classes, they have to live where ProcessWire expects them to, i.e. /site/modules/. Now, this does not mean that all your classes have to be modules. No; you can have custom classes (i.e. my_class.php) living in /site/modules/my_module/my_class.php or /site/templates/includes/my_class.php (@note: includes here is just arbitrary) which you access from within your registered classes (modules) or from within your template files. Any attempt to access such classes directly will be blocked by ProcessWire for security reasons. This just means you can have helper classes or other libraries you need for your modules which ProcessWire does not need to be aware about (in respect to registering them). The foregoing means that... ..is not necessary in ProcessWire. your /site/ is already outside the core. It survives upgrades and ProcessWire never touches it. It just communicates with it (bidirectional communication). All ProcessWire core variables (e.g. $wire, $config, $fields, $page, $pages, etc) are available everywhere for your use, both in the backend and the frontend. I hasten to add that ProcessWire will not automatically install your modules for you. If it finds a new module, it will just let you know that it has found a new module. If it finds duplicates of the module, i.e. either duplicated under /site/modules/ or under both /site/modules/ and /wire/modules/ , it will alert you to the fact and will ask you to choose which one you are going to use. This means that you can duplicate a core module found in /wire/modules/ under /site/modules/, edit it to suit your requirements and ProcessWire will happily continue serving the copy under /site/modules/ and yes, it will survive upgrades. This is not always a good idea though, as the mother module could get some upgrades which you would be missing on and/or require. So, in ProcessWire, we develop our modules under /site/modules/. Most people who develop modules use a versioning system, invariably git, and host their modules on GitHub, BitBucket, GitLab, etc. Secondly, many people make use of git's branches and work in the dev branch. All development and testing is done under that branch. When ready for production, they merge into master branch. For modules submitted to the ProcessWire modules directory, if the project (code) is hosted in GitHub, ProcessWire (the site) reads the master branch of the project to access the README and the download link. In the past, I have used symlinks to link my module to different ProcessWire versions. For a quick and dirty check, I have occasionally had two wires, e.g. wire and wire27 which I rename to suit my needs RE ProcessWire version. Many of us here develop using the latest ProcessWire dev. I cant' speak for everyone, but I think many then use (if they need to) a ProcessWire 2.7 just for final tests. I doubt many of go back and forth between versions (i.e. between 2.7 and 3). No 🙂. As long as you name your module classes as per ProcessWire requirements work with what suits your needs. The above is just the basics. We have people here who work in teams and have slightly different approaches, but the basics are the same.
  22. 9 points
    This week we’ll take a look at LoginRegisterPro — a new module that provides an all-in-one, self contained module for providing new user registration, secure logins, profile editing, and more. It does this all in a manner that is reliable, efficient, comprehensive and secure. As we continue preparing the ProcessWire core dev branch to become our new master, I've been trying to stay hands-off on new feature additions there as much as possible (till the new master is out), and instead focusing on finishing up modules I've had in development. Last time I told you about the UserActivity module, and this time we’ll look at LoginRegisterPro, which is another module dealing with users; though significantly larger in scale/scope. LoginRegisterPro is a module I've been working on for more than a year, and finally this month have it ready to share. While I don't have it available for download today I do expect to have a beta release as soon as early next week. Read this week’s post for more details— https://processwire.com/blog/posts/login-register-pro/
  23. 9 points
    While working on the comments form of my blog, I thought to add an honeypot field in comments form to reduce spam. 🤨 Honeypot is simply an hidden field added to a form. Honeypot field can have any name and is made invisible normally with a css directive {display: none}. Web users, being unable to see the field, will not fill it, while spam bots majority will not detect its invisibility and will populate the field. Once the form is submitted with a not-empty honeypot field it is very likely we are dealing with spam. 😜 In this post you can find more details about honeypot technique. While studying FieldtypeComments module and in particular CommentForm.php, to my great surprise 👀 I realized that PW already supports honeypot for Comments Form. 🍾🍾 This feature has been introduced with PW 3.0.40. Normally this honeypot field is disabled, so it was enough to understand how to enable it! And as often is the case with PW ... it is super easy. 😎 If in your profile you are directly working with CommentArray, you will just have to enable honeypot passing it as an option to the renderForm() function of CommentArray class, example below: $comments->renderForm(['requireHoneypotField' => 'email2']); And .. we are done! 🍾🍾 If you will look at the html of your Comment Form you will see an additional line CommentFormHP, that's the hidden honeypot field. In case you are using the Uikit 3 Site/Blog Profile, the renderForm() function is called in _uikit.php, ukCommentForm() function. If you wish that honeypot field is applied to every comment form of your site, just add the requireHoneypotField option to the list at the function start: ... 'errorMessage' => __('Your comment was not saved due to one or more errors.') . ' ' . __('Please check that you have completed all fields before submitting again.'), requireHoneypotField' => 'email2', // >>>>> ADD THIS LINE ); ... Otherwise if you wish to add honeypot in comment form on selected templates only, do not modify ukCommentForm(), but pass the option requireHoneypotField when calling the function in your template: ukCommentForm($comments, ['requireHoneypotField' => 'email2']); Now that we enabled it, let test if honeypot works. 🧐 In the browser development section let's select the honeypot field and disable css {display:none} to show it. A new field will appear: If the spam bot is going to fill the field with a value and submit the form, an error is returned and comment will not be submitted 😎 That approach is great as spam comments will not be even saved inside the table field_comments. 🤪 I hope this can be of help if somebody needs to enable this PW comments feature.
  24. 9 points
    Also, there is a counter option built into PW for all textarea fields:
  25. 9 points
    OT, but though to mention it here. I have been working on a site profile (still in alpha >>> it's been ages! other things got in the way) inspired by the Electron API Demos app that will do this and more. Essentially, it teaches you to use the API. On install, you get nothing but the 'blank profile'. You choose a topic and It teaches how to accomplish what is covered in that topic, e.g. the AP to create/edit/delete fields, templates, pages, etc.
  26. 8 points
    I also use different ways for implementing menus, depending on what I find appropriate. Here is a repeater based, one level customizable menu, where hiding fields on conditions makes it possible to switch between "custom" and page picker options:
  27. 8 points
    Here is another snippet that I use to get rid of unwanted table properties: // Remove unwanted attributes from tables CKEDITOR.on('dialogDefinition', function(ev) { var dialogName = ev.data.name; var dialogDefinition = ev.data.definition; if (dialogName == 'table') { var info = dialogDefinition.getContents('info'); info.remove('txtWidth'); info.remove('txtHeight'); info.remove('txtBorder'); info.remove('txtCellPad'); info.remove('txtSummary'); info.remove('txtCellSpace'); info.remove('cmbAlign'); var advanced = dialogDefinition.getContents('advanced'); advanced.remove('advStyles'); advanced.remove('advId'); //Id attribute advanced.remove('advLangDir'); // writing direction advanced.get('advCSSClasses')['default'] = 'uk-table'; //set default class for table } Put this code inside your custom config.js Best regards
  28. 8 points
    Great works guys! Thanks @netcarver and his "crew" @matjazp @adrian and @Robin S.
  29. 8 points
    TemplaneEngineMustache This module adds Mustache as engine to the TemplateEngineFactory. Project page in Github: https://github.com/blue-tomato/TemplateEngineMustache/ Module Page: https://modules.processwire.com/modules/template-engine-mustache/
  30. 7 points
    @pwired — there was a time when I'd agree with you, but there are quite a bit of options for I ❤️ PW devs out there - for flat file CMS at least. So in the end I'd say that ProcessWire and its community are better served by focusing at its one thing.
  31. 7 points
    I have no experience with this Tribe events thing, so can't speak for that, but for the bulk of the content I'd recommend skipping the database export idea and going with the built-in REST API. While the REST API has its quirks, going directly to database for exports is going to be a major pain in the a*s in comparison. Not entirely unlike exporting ProcessWire content with raw SQL... 🙂 It would be best to have a separate copy of the site at hand first, but after that it's basically as simple as installing the ACF to REST API plugin (which adds ACF field data to the REST API results) – and of course making sure that the REST API is enabled in the first place. You should be able to query and export all your pages, articles, and any custom post types with this method. Once you have the data, importing it to ProcessWire is going to be a breeze. (Note: based on some quick googling the Tribe events plugin also provides REST API support. I haven't used this, but it looks promising.) Also, in case that idea won't pan out, you can always rely on existing solutions, such as WP All Export (which provides a GUI for exporting content, including ACF data). Admittedly I haven't worked with this plugin before, but it's the companion plugin for WP All Import, the de facto standard plugin solution for complex imports. WP All Import is a bit of a monster and can feel clunky (hence devs often prefer custom imports for long-running, often-used, scheduled stuff), but for one-off cases it's a really handy tool. -- Edit: in case anyone is wondering, the WP REST API was first announced on June 17th 2013, which would be a week or so after Ryan started this thread. It didn't make it's way into the core until 2015, and even then it was for a very long time considered "a work in progress". It's been more than five years since this thread was started, so it shouldn't come as a big surprise that some things have changed 😅
  32. 7 points
    Hi all, I thought that you'd be interested to know we've now launched our third ProcessWire site, called Home of the Blizzard - The Australasian Antarctic Expedition. It's about Douglas Mawson's famous expedition of 1911-1914. As a part of the move to ProcessWire from our previous CMS it has undergone a redesign as well. It has been done completely in-house. https://mawsonshuts.antarctica.gov.au/ It uses Fancybox for the image galleries and modal menu, Plyr for video, and Modernizr (primarily to check for CSS Grid compatibility). It was actually the first PW site we started working on, but then the SCAR COMNAP and Antarctic Jobs sites became priorities so it got pushed back. It's great to have it live at last. The next PW site we launch should be our main site (http://www.antarctica.gov.au)! That's still a work in progress. 🙂
  33. 7 points
    @szabesz has to be the most generous here on the forums. Look at the given count, and that is since the end of 2015!
  34. 7 points
    Thought I'd share this quickly as it helps me get to grips with whether I'm looking at the dev site or live site when working on any client project. Dev site only has AdminOnSteroids module. I add the below to sites/templates/admin/admin.css and add that url the admin css field in the module. Currently only works with default adminTheme. #masthead{ background: rgb(153, 12, 94) !important; } #logo:before{ content: 'DEV '; color: white; margin-right: 1em; vertical-align: middle; display: inline-block; font-size: 1em; letter-spacing: 0.05em; margin: -15px 0.6em 0em 0; } Simply adds a DEV string and changes the header color so I can see I'm on Dev not live. I guess you could do the opposite if you wanted adding content: "LIVE" instead in the CSS.
  35. 7 points
    Hi, This is my another PW website - http://pkrosifoundation.org No Modules used
  36. 7 points
    I saved a copy at the time - here they are (I think!) 🙂
  37. 7 points
    Just read "headless" on their site and thought this would be an important keyword for processwire.com as well. Maybe something like: I'm neither a native speaker nor a marketing slogan guru, but I hope you get what I'm trying to say. And as a proof, this statement could be linked to this video as discussed here: While I love that video content and know how true every word in it is, I think for new users this might not be obvious. Quite on the contrary, I think a video with one stranger telling something about a great system that he developed in his living room (sorry 😉 ) does not have the professional impression that it deserves. Also 33k views since 2010 is really not a lot - all of us know that these are no fair indicators for the power or greatness of processwire, but new users might not get that. I think it would be great to have a 2minute introduction video in a modern and professional design. I even think that it would be great to let this video have 100% the same text of the first 2min of the ancient video from 2010 to show how revolutionary the platform was back then (all modern CMSs sell that as the new big thing) while on the other hand PW has it for 10 years and has been stable all the time! Maybe someone could help with such a video? @heldercervantes or @Jonathan Lahijani ? One more thing that I like on modern websites about products is a section where the product is compared to other players in the market. Like runcloud does it on their website: Here is how such a page looks like: I think such pages could make a LOT of sense because people already know DroomlaPress and such a comparison page could outline the differences and show the strengsths of processwire in a fair and efficient way. If you try to compare it via google/youtube searches processwire will always lose the game because it's just not focussed on marketing the way the other platforms are. But that could easily be outlined on such pages, e.g. comparing CMS XY to PW: Good luck with the new processwire.com and happy and successful new year to everybody 🙂
  38. 6 points
    Fixed! Not completely sure why, but switching: $mail = new WireMail(); to this: $mail = $this->wire('mail')->new(); in the LoginRegister module did the trick — line 684. Now Gmail's sending my registration emails too 🙂 Gotta say, I am hugely grateful for Processwire. Ryan, and all you fantastic moderators and commenters—this is such a great place to learn, and goddamn if the sky doesn't seem to be the limit.
  39. 6 points
    One last update for the day: master branch of the module is now at 0.5.0. This includes both the rendering features mentioned above, and a slightly more polished theming feature, where each theme can declare custom markup, styles, scripts, strings, etc. More details in the README file.
  40. 6 points
    Use wire('all') Example from our production code: $factory = $modules->get('TemplateEngineFactory'); echo $factory->render('chunks/bodyCta.tpl', array_merge( (array) wire('all'), (array) [ "title" => $title, "text" => $text, "href" => $href ] ));
  41. 6 points
    With ProcessWire you don't generally want to perform your own SQL queries. It's possible, but generally I'd consider that a bad practice. If your content is stored as ProcessWire objects (basically Pages), you should always use the Selector engine for that sort of stuff. Not only does this provide the benefits of a great API (such as $pages->find('template=mailbox, owner=[some_user_field=whatever_value]') or $pages->get('parent=some-parent, name=page-name')->setAndSave('field', 'value')), there's also zero chance of accidentally running into nasty SQL injection vulnerabilities. Of course you can always create custom database tables and perform queries on them using the $database API variable (which is essentially a wrapper for PHP's own PDO class), but in such cases the content won't be editable within ProcessWire's admin, so you're missing a pretty important benefit there. Another alternative would be using RockFinder, which kind of provides the best of both worlds: easy to use syntax for queries combined with the performance of raw SQL and lightweight PHP arrays/objects. So, long story short: if you want a nice GUI and an easy-to-use API for managing your content, ProcessWire is a great choice. If you ultimately just want to use your custom database tables, write a few SQL clauses to update a few rows here and there, and there's no need for a web based interface for managing said items – then perhaps you should just build it from scratch (or use something like Laravel instead).
  42. 6 points
    For the sake of marketing and brand recognition I'd recommend not going with a generic name such as pw + [some commerce related term]. Best known brand names tend to be unique, and they don't really have to have any connection with the company/product (although they can). Trader is a decent idea, but I still think that Padloper is better -- not to mention that it already has some brand value, and an awesome logo 🙂 just my five cents.
  43. 6 points
    You don't need to migrate to a new hosting server just because the domain name for a site changes. Point the domain to your host and update $config->httpHosts in /site/config.php. Depending on the type of hosting you might need to adjust a setting there too, e.g. update the primary domain if it's a cPanel host.
  44. 6 points
    Thanks for noticing it. I usually place those overrides in /site/wire/ (for example /site/wire/JqueryMagnific) so I know which module is an override from the directories structure.
  45. 6 points
    This is all @tpr's hard work - he has just added a search/filter implementation using his awesome filterbox.js - be sure to check it out and Star it! Note that in addition to filtering, if you hit "Enter" it will load the first matched file. Thanks again Roland - this is very cool!
  46. 6 points
    @kongondo and everyone else 🙂 I have just added caching for both the Tracy and PW logs panels so now they only read a log file if it has changed since it was last cached. This should make a dramatic difference to most page loads. Do note that if you have a large log file and a new entry is added, then the load time will still be slow for that first page load when the entry is logged, so you still may want to delete log files (or use the PW log prune function) if you notice a slow load. Hope that helps everyone.
  47. 6 points
    Tiny explanation: The new directive only creates a fresh instance of a PHP class in memory, in this case a DateTime and a DateTimeZone object. The latter is initialized to 'Europe/Berlin' and used as the timezone parameter for initializing the DateTime object, which, in turn, is stored in variable $ct. Nothing affects the system.
  48. 6 points
    Thanks for the updates @ryan! Going forward, what do you think should be done about the sanitizer methods included in the PhpDoc of WireInputData? These sanitizer methods are also the basis for what gets an entry in the API documentation for WireInputData. As noted in this request the sanitizer methods included in the WireInputData PhpDoc were already out of date (i.e. incomplete). The new $santitizer methods added in 3.0.125 are also not included there. Do you think the PhpDoc of WireInputData should just aim to keep pace with new $sanitizer methods as they are added? Or now that multiple methods can be chained together... // access sanitizer on $input as method $q = $input->get->text50_entities('q'); ...in a way that couldn't be handled as lines in the PhpDoc, perhaps it would be better to drop the list of sanitizer methods and instead include some general note about how $sanitizer methods (with a link to the $sanitizer documentation) can be used/chained with $input?
  49. 6 points
    Love it! The only thing I'm not sure about is the emphasis on... I feel that a low maintenance overhead is already strongly implied by reliability and security sections. I like to run updates if only to keep the CK Editor plugin up-to-date, which *does* have semi-regular security patches. It might not appeal to developers for whom updates are bread and butter. I'd rather see something like... Lower maintenance costs, because security, reliability, and a stable API resulting in painless upgrades. Yeah, I once did a major version PW upgrade on a fairly complex 30,000+ page site and *nothing* broke! Zero. Zilch. Nada. It brought tears to my eyes. A lack of breakage that is unheard of in CMS land. Compare with regular minor version breakage for some other CMS 😀
  50. 6 points
    Looking good Ryan. One thing I noticed is the last dropdown menu "Docs" should open the same direction/way as the "Download" menu item but it doesn't. It opens off screen causing horizontal scrollbars. When viewing on Chrome. Edit: it looks like jmartsch beat me to it. Please consider giving the light grey and other muted text more contrast for accessibility. See https://contrastrebellion.com/ for humor. #666 for body copy gives AA, but #565656 gives AAA for example. Chrome's inspector now includes a contrast accessibility checker when you click on the color swatch/square. The curved line represents what passes the AA or AAA rating. After reading the API examples, I want to know more about it. I think we need a link or outlined button below the examples "Get started with the API" or "Learn more" so they can continue reading. Now that I look at it more, maybe each homepage section needs it own call to action button below. At first it wasn't entirely obvious that the headers on the homepage were links. Ex. Blog posts - might need a button below that say's "View all" or something similar. Same for the Processwire Showcase and Shop. - This might actually motivate new users to join the forums as well. Those are just a few of the first things that come to mind when viewing the site. Hope that helps
×
×
  • Create New...