Jump to content

Leaderboard


Popular Content

Showing content with the highest reputation since 07/06/2019 in all areas

  1. 45 points
    Happy new year, everybody 🥬 I've been sitting on this Dashboard module I made for a client and finally came around to cleaning it up and releasing it to the wider public. This is how it looks. ProcessWire Dashboard If anyone is interested in trying this out, please go ahead! I'd love to get some feedback on it. If this proves useful and survives some real-world testing, I'll add this to the module directory. Download You can find the latest release on Github. Documentation Check out the documentation to get started. This is where you'll find information about included panel types and configuration options. Custom Panels My goal was to make it really simple to create custom panels. The easiest way to do that is to use the panel type template and have it render a file in your templates folder. This might be enough for 80% of all use cases. For anything more complex (FormBuilder submissions? Comments? Live chat?), you can add new panel types by creating modules that extend the DashboardPanel base class. Check out the documentation on custom panels or take a look at the HelloWorld panel to get started. I'm happy to merge any user-created modules into the main repo if they might be useful to more than a few people. Disclaimer This is a pre-release version. Please treat it as such — don't install it on production sites. Just making sure 🍇 Roadmap These are the things I'm looking to implement myself at some point. The wishlist is a lot longer, but those are the 80/20 items that I probably won't regret spending time on. Improve documentation & add examples ⚙️ Panel types Google Analytics ⚙️ Add new page 🔥 Drafts 🔥 At a glance / Page counter 404s Layout options Render multiple tabs per panel panel groups with heading and spacing between ✅ panel wrappers as grid item (e.g. stacked notices) ✅ Admin themes support AdminThemeReno and AdminThemeDefault ✅ Shortcuts panel add a table layout with icon, title & summary ✅ Chart panel add default styles for common chart types ✅ load chart data from JS file (currently passed as PHP array) Collection panel support image columns ✅ add buttons: view all & add new ✅
  2. 40 points
    ProcessWire 3.0.157 on the development branch continues the trend of core refactoring that’s been happening quite a bit in 2020. Rather than doing a rewrite every few years (like some CMS projects) we instead refactor parts as we go, constantly improving and optimizing the core. This works because the core design/architecture is right where it needs to be, even 10 years in. But there’s always still bits of legacy code, and code that can be improved. So in the context of ProcessWire, refactoring means incrementally rewriting code on the inside, without changing its behavior on the outside (other than making it faster and/or more secure). This has been happening regularly over the last 10 years, and will likewise continue happening over the next 10 years and likely beyond. This week the code behind ProcessWire’s core Database and PageFinder classes got a major refactoring. This is some of the most used code in PW, as it handles everything involved in taking a selector and converting it to a database query. But it’s always been a little bit of a pain point for me because it had to build queries in a way that I thought wasn’t ideal, in order to make it possible for lots of different modular parts (mostly Fieldtype modules) to contribute to the query and for PageFinder to put it all together. It was fast and secure, but still one of those parts that felt like a little too much duct tape to me. But considering how crucial the code is, I’ve always been reluctant to make major changes, since it all worked just fine. Spending lots of years thinking about it (on and off), a desire to work out any pain points, and having better tools available (like Phpstorm and Tracy) made it possible to finally massage out this pain point. Some work still remains to be done, but it’s mostly there and I’m feeling good about it. Stuff like this is key for the maintenance and longevity of the core, and involved a lot of time and effort, but makes very little difference to users, even if it makes a lot of difference to me in maintaining the core. It would make a boring blog post for sure—lots of work and changes, but no new toys to show for it. Nevertheless, it has that feeling of a good house cleaning, even if you can't see it from the outside. The scope of changes here means that there may actually be new bugs to work out, so to be on the safe side, consider 3.0.157 to be a little more “beta” than the dev branches usually are. Though I’m running it here on processwire.com and it’s working well. Beyond the fairly major updates to the Database classes, there are also a few new Sanitizer convenience methods that are primarily variations on existing ones, but useful ones for sure. Thanks for reading and have a great weekend!
  3. 37 points
    I hope that you have had a great week! I’ve been working hard on finishing up the LoginRegisterPro module this week and actually have it ready. But I’ve been told I have to get off the computer in 20 minutes, so so I think I’ll wait till Monday to release it. But I do have the new info page (which is kind of like last week's blog post) and new documentation page now online. The documentation page in particular is pretty comprehensive. In last week’s post there was a form to request more info once it’s released, so if you are interested in this module and haven’t filled out that form, please do. That’s how I’ll be sending out the introduction coupon code this time around, for those that want it. There have also been some core updates this week, but it was just a few commits, so not enough to warrant a version bump today. That’s actually a good thing, as no major new issues turning up means one step closer to merging onto the master branch. There will be a new master version before this year is done! Thank you for reading and I hope that you all have a great Christmas and/or holiday week next week!
  4. 36 points
    I hope everyone here is doing well, staying in, and staying healthy. Our town here is under a “stay at home” order, and it’s now the law that you can’t get within 6 feet of any other person when out walking. So haven’t left the house (other than for walks and bike rides) in about 2 weeks now. Though with the whole family home all the time, it admittedly feels a lot busier than before this Coronavirus stuff, I think because there’s now a lot more people to attend to during the day (especially kids). Not much silence compared to before. 🙂 Not a bad thing, just very different. If we’ve got to spend a few months, or even a year this way, it’ll be alright, so long as the internet keeps working. I’m just thankful to have a job where I’m already used to working this way, as I know many of you do too. It seems that this whole situation is going to move a lot of activity online that previously wasn’t, so I anticipate it’s going to be potentially a very busy and important year for web development. Online communication and content delivery is going to be that much more important for the world, making reliability, scalability and security every bit as important. These are always our focus, but just want to emphasize this even more as we look forward. With a world in temporary disarray, you can count on ProcessWire to be an especially stable and reliable tool that gets even better every week, and our community always a friendly and helpful place. I’ve got several things in progress in the core, but nothing far enough along to write about just yet. I’ve also been putting a lot of work into ProCache this week, which is long due for a version update. The module still has quite a bit of PW 2.x architecture that I don’t think is needed anymore, so I’m refactoring and improving quite a bit, in addition to feature updates. Thanks for reading and I hope that you have a good and safe weekend!
  5. 35 points
    Last week I worked primarily on GitHub issues, and did some of that this week as well. Likely I'll be doing a lot of this in October. Thank you for all of your reports. While there's already a lot of commits on the dev branch, I'm going to wait till next week to bump the version, as I've got some stuff in progress that I want to get committed first (more on that below). Next week I'm releasing version 40 of FormBuilder that supports paginated forms, as well as forms within forms (not to mention some other minor additions). Basically, all the stuff that was covered in this video from a few weeks ago, plus a little more. I actually think it's ready right now, but as is often the case, I started writing instructions for using the new features today and thought of a couple minor tweaks that would be helpful along the way. So I'm going to apply those early next week, finish the instructions, test it all out again, and then release it... likely mid-week next week. For the ProcessWire core, one feature people have been asking for for quite awhile is the ability to specify custom fields with file and image fields. I've been working on that here quite a bit this week, and have the initial test cases working quite nicely! Unlike the Description and Tags fields that come as built-in options with file and image fields, the new option instead uses a subset of ProcessWire's Fieldtype and Inputfield modules to support this (note: it does not use pages like repeaters do). This gives you more flexibility in defining what you want and how you want it to look. Though there are some limitations of what kinds of fields you can use here, but I think you will like what it offers and how it works. For those that just need a description and/or tags, then of course those features will remain as they are. But for those that need something more for file/image fields, you are going to have a whole lot of new options in 3.0.142. Unless I run into any roadblocks in finishing development of this part, I'll have it ready by this time next week along with a blog post that outlines it in more detail.
  6. 33 points
    Hope you guys are having a great week. I'll keep this week's update short since everything I'm working on is in-progress rather than ready to post. But I can tell you about a few things you'll likely see in next week's post: First is that I've got multi-page/paginated form support just about ready to release in FormBuilder. What this means is that you can take forms (especially long forms) and break them up into multiple paginations. This makes for multi-part forms that are more digestible and easy to use for users. The end of each pagination has "Next" and "Prev" buttons for navigation between them. FormBuilder validates each pagination independently as it is submitted so that any errors are taken care of as the user proceeds rather than all at the end. And these are true paginated forms, rather than a JS manipulation of existing forms. More on this next week. I'm also working here on a new Inputfield module called InputfieldToggle. It's an alternative to the core InputfieldCheckbox and the intention here is to make it a selectable alternative for FieldtypeCheckbox fields. Unlike InputfieldCheckbox, it is presented as two radio buttons for "on" and "off" states. (It's also possible to have a "no selection" state distinct from the "no" state, where supported). It comes predefined with several toggle types (Yes/No, On/Off, True/False, Enabled/Disabled), along with the ability to specify your own (multi-language too of course). Like a checkbox, because it is a toggle, it holds a value of either true (1) or false (0). There is also null for no selection. While this is a relatively simple Inputfield, it answers a common need (at least in my experience) and often can provide a better experience than a standard checkbox, depending on the input need. Not to mention, it's a lot more efficient than using an Options or Page field to accomplish the same thing. In addition to sites and apps running in ProcessWire, I think this particular Inputfield has a lot of potential use in the core and its administrative forms, so I might include it in the core, though not yet certain. I'm already using it quite a bit in forms I'm developing for clients in FormBuilder, where in many cases I find it a better fit than InputfieldCheckbox. Lastly, there are some nice UI enhancements just about ready for manipulating column widths of fields in ProcessWire. It makes it a much simpler and quicker job than it currently is, so I'll have more on that next week too. Have a great weekend!
  7. 30 points
    Modules Manager 2 provides an easy to use interface to download, update, install, uninstall and configure modules. It is meant to provide an optimized alternative to the core ProcessModule dashboard. Maybe @ryan agrees to merge it to the core at some point when it is finished and polished. Features: Seamlessly download, update, install, uninstall or delete modules Live-Search (aka find as you type) for module names Live-Search (aka find as you type) for categories Browse new and unkown modules from the modules directory on modules.processwire.com Choose your favorite layout (cards, reduced cards, table) Modern UIKit design (therefore only works with AdminThemeUikit) Caches the module list from modules.processwire.com directory locally. What is Modules Manager 2? Why a new module manager? Some people including myself think that the actual module installation in ProcessWire could be improved in some places. Make it easy for ProcessWire beginners and power users Offer better discoverbility to find the right module Make it easier and faster for powerusers to manage modules A manager that list all official modules is a feature, that many other frameworks/CMS's like ModX, WordPress or PrestaShop have by default. What are the disadvantages of the actual core module interface? Installation of a module is not very user-friendly: You have to be aware where to get new modules, search for a module, copy or remember the module name or URL, go back to your ProcessWire installation, paste the module name(URL, click on "get module info" and finally install the module It only displays installed modules, not the ones that are available in the modules directory Uninstalling a module requires you to go to the module detail page, click a checkbox and then submit the change. After that you have to go back to the module overview page. It only displays installed modules, not the ones that are available in the modules directory, so it makes discovering modules hard BETA software Use this module at your own risk. I am not responsible for any damage or unexpected behaviour. Some things might not work fully, please see the TODO list for details. I need your feedback and help This module is still in development and I am happy to discuss with you and get some feedback. What do you like? What is missing? What could make the process even easier? Ask, suggest or provide pull requests. You can download the module at https://modules.processwire.com/modules/modules-manager2/ or from Github: https://github.com/jmartsch/processwire-modules-manager2
  8. 30 points
    I've seen a couple of questions regarding namespaces and autoloading floating around the forum recently, so I decided to write a little tutorial. In general, I often see people getting confused when they try to wrap their head around namespaces, autoloading, Composer and the mapping of namespaces to directory structures all at once. In fact, those are very much independent, distinct concept, and it is much easier to explain and understand them separately. So this guide is structured as follows: How namespaces work in PHP. How autoloading works in PHP. Conventions for mapping namespaces to directory structures: PSR-4. How autoloading works in Composer and ProcessWire's class loader. How to use the class loader in a ProcessWire module. Feel free to skip the sections you're already familiar with. Namespaces in PHP The purpose of namespaces in PHP is to avoid naming conflicts between classes, functions and constants, especially when you're using external libraries and frameworks. Nothing more. It's important to understand that this has nothing at all to do with autoloading, directory structures or file names. You can put namespaced stuff everywhere you want. You can even have multiple namespaces inside a single file (don't try this at home). Namespaces only exist to be able to use a generic name – for example, ProcessWire's Config class – multiple times in different contexts without getting a naming conflict. Without namespaces, I couldn't use any library that includes a Config class of it's own, because that name is already taken. With namespaces, you can have a distinction between the classes ProcessWire\Config and MoritzLost\Config. You can also use sub-namespaces to further segregate your code into logical groups. For example, I can have two classes MoritzLost\Frontend\Config and MoritzLost\Backend\Config– a class name only needs to be unique within it's namespace. You can declare the namespace for a PHP file using the namespace statement at the top: // file-one.php <?php namespace ProcessWire; // file-two.php <?php namespace MoritzLost\Frontend; This way, all classes, methods and constants defined inside this file are placed in that namespace. All ProcessWire classes live in the ProcessWire namespace. Now to use one of those classes – for example, to instantiate it – you have a couple of options. You can either use it's fully qualified class name or import it into the current namespace. Also, if you are inside a namespaced file, any reference to a class is relative to that namespace. Unless it starts with a backward slash, in this case it's relative to the global namespace. So all of those examples are equivalent: // example-one.php <?php namespace ProcessWire; $page = new Page(); // example-two.php <?php use ProcessWire\Page; $page = new Page(); // example-three.php <?php $page = new ProcessWire\Page(); // example-four.php <?php namespace MoritzLost\Somewhere\Over\The\Rainbow; $page = new \ProcessWire\Page(); The use statement in the second example can be read like this: “Inside this file, all references to Page refer to the class \ProcessWire\Page” How autoloading works Every PHP program starts with one entry file – for ProcessWire, that's usually it's index.php. But you don't want to keep all your code in one file, that would get out of hand quickly. Once you start to split your code into several individual files however, you have to take care of manually including them with require or include calls. That becomes very tedious as well. The purpose of autoloading is to be able to add new code in new files without having to import them manually. This, again, has nothing to do with namespaces, not even something with file locations. Autoloading is a pretty simple concept: If you try to use a class that hasn't been loaded yet, PHP calls upon it's registered autoloaders as a last-ditch attempt to load them before throwing an exception. Let's look at a simple example: // classes.php <?php class A { /** class stuff */ } class B { /** class stuff */ } // index.php <?php spl_autoload_register(function ($class) { include_once 'classes.php'; }); new A(); new B(); This is a complete and functional autoloader. If you don't believe me, go ahead and save those two files (classes.php and index.php) and run the index.php with php -f index.php. Then comment out the include_once call and run it again, then you'll get an error that class A was not found. Now here's what happens when index.php is executed (with the autoloader active): Our anonymous function is added to the autoload queue through spl_autoload_register. PHP tries to instantiate class A, but can't because it's not loaded yet. If there was no autoloader registered, the program would die with a fatal error at this point. But since there is an autoloader ... The autoloader is called. Our autoloader includes classes.php with the class definition. That was a close one! Since the class has been loaded, execution goes back to the index.php which can now proceed to instantiate A and B. If the class was still not loaded at this point, PHP would go back to the original plan and die. One thing to note is that the autoloader will only be called once in this example. That's because both A and B are in the same file and that file is included during the first call to the autoloader. Autoloading works on files, not on classes! The important takeaway is that PHP doesn't know if the autoloader knows where to find the class it asks for or, if there are multiple autoloader, which one can load it. PHP just calls each registered autoloader in turn and checks if the class has been loaded after each one. If the class still isn't loaded after the last autoloader is done, it's error time. What the autoloader actually does is pretty much wild wild west as well. It takes the name of the class PHP is trying to load as an argument, but it doesn't have to do anything with it. Our autoloader ignores it entirely. Instead, it just includes classes.php and says to itself “My job here is done”. If class A was in another file, it wouldn't have worked. This process has two main advantages: Since autoloaders are only called on-demand to load classes just in time, we only include the files we actually need. If in the example above class A and B are not used in some scenarios, the classes.php will not be included, which will result in better performance for larger projects (though this isn't as cut and dry, since autoloading has it's own overhead, so if you load most classes anyway during a single request, it will actually be less efficient). If the autoloader is smart enough to somehow map class names to the files they're located in, we can just let the autoloader handle including the classes we need, without having to worry about jamming include statements everywhere. That brings us to ... PSR-4, namespaces and directory structures As you see, namespaces and autoloading are both pretty limited concepts. And they aren't inherently linked to each other. You can namespace your classes without ever adding an autoloader, and you can autoload classes that are all in the same namespace. But they become useful when you put them together. At the core of all that autoloading talk is a simple idea: By putting classes in files named after their class names, and putting those files in directory hierarchies based on the namespace hierarchy, the autoloader can efficiently find and load those files based on the namespace. All it needs is a list of root namespaces with their corresponding directories. The exact way class names and namespaces are mapped to directory structures and file names is purely conventional. The accepted convention for this is PSR-4. This is a super simple standard which basically just sums up the ideas above: A base namespace is mapped to a specific directory in the file system. When the autoloader is asked to load a class in that namespace (or a sub-namespace of it), it starts looking in that folder. This "base" namespace may include multiple parts – for example, I could use MoritzLost\MyAwesomeLibrary as a base and map that to my source directory. PSR-4 calls this a "namespace prefix". Each sub-namespace corresponds to a sub-directory. So by looking at the namespace, you can follow subdirectories to the location where you expect to find the class file. Finally, the class name is mapped directly to the file name. So MyCoolClass needs to be put inside MyCoolClass.php. This all sounds simple and straightforward - and it absolutely is! It's only once you mash everything together, mix up language features, accepted conventions and proprietary implementations like Composer on top that it becomes hard to grasp in one go. Composer and ProcessWire's class loader Now all that's left is to talk about how Composer and ProcessWire provide autoloading. Composer, of course, is primarily a tool for dependency management. But because most libraries use namespaces and most developers want to have the libraries they're using autoloaded, those topics become a prerequisite to understanding what Composer does in this regard. Composer can use different autoloading mechanisms; for example, you can just give it a static list of files to include for every request, or use the older PSR-0 standard. But most modern libraries use PSR-4 to autoload classes. So all Composer needs to function is a mapping of namespace prefixes to directories. Each library maintains this mapping for it's PSR-4-structured classes through the autoload information in their composer.json. You can do this for your own site to: Just include the autoload information as shown in the documentation and point it to the directory of your class files. Composer collects all that information and uses it to generate a custom file at vendor/autoload.php — that's the one you need to include somewhere whenever you set up Composer in one of your projects. Bells and whistles aside, this file just registers an autoloader function that will use all the information collected from your own and your included libraries' composer.json to locate and include class files on demand. You can read more about how to optimize Composer's autoloader for production usage here. If you want to read up on how to set up Composer for your own sites, read my ProcessWire + Composer integration guide instead. And finally, what does ProcessWire do to handle all this? Turns out, ProcessWire has it's own autoloader implementation that is more or less PSR-4 compliant. You can access it as an API variable ($classLoader or wire('classLoader'), depending on context). Instead of using a static configuration file like Composer, the namespace -> directory mapping is added during the runtime by calling $classLoader->addNamespace. As you would expect, this function accepts a namespace and a directory path. You can use this to register your own custom namespaces. Alternatively, if you have site-specific classes within the ProcessWire namespace, you can just add their location to the class loader using the same method: $classLoader->addNamespace('ProcessWire', '/path/to/your/classes/'). Utilizing custom namespaces and autoloading in ProcessWire modules Now as a final remark, I wanted to give an example of how to use custom namespaces and the class loader in your own modules. I'll use my TrelloWire module as an example: Decide what namespace you're going to use. The main module file should live in the ProcessWire namespace, but if you have other classes in your module, they can and should use a custom namespace to avoid collisions with other modules. TrelloWire uses ProcessWire\TrelloWire, but you can also use something outside the ProcessWire namespace. You need to make sure to add the namespace to the class loader as early as possible. If either you or a user of your module tries to instantiate one of your custom classes before that, it will fail. Good places to start are the constructor of your main module file, or their init or ready methods. Here's a complete example. The module uses only one custom namespaced class: ProcessWire\TrelloWire\TrelloWireApi, located in the src/ directory of the module. But with this setup, I can add more classes whenever I need without having to modify anything else. /** * The constructor registers the TrelloWire namespace used by this module. */ public function __construct() { $namespace = 'ProcessWire\\TrelloWire'; $classLoader = $this->wire('classLoader'); if (!$classLoader->hasNamespace($namespace)) { $srcPath = $this->wire('config')->paths->get($this) . 'src/'; $classLoader->addNamespace($namespace, $srcPath); } } Source Thanks for making it through to the very end! I gotta learn to keep those things short. Anyway, I hope this clears up some questions about namespaces and autoloading. Let me know if I got something wrong, and feel free to add your own tips and tricks!
  9. 28 points
    --- Please use RockFinder3 ---
  10. 27 points
    This week I was back to focusing on the core and got quite a lot done. A lot of GitHub issue reports were resolved, plus several minor tweaks and additions were made in 3.0.156 as well. But the biggest update was the addition of the $pages->parents() API, which is something that I think you’ll likely have zero use for (and why I’m not putting it into a blog post) but something that the core itself will use quite a bit, and is a really nice improvement for the system and its scalability. So if you don’t mind some technical reading, read on. Whenever you call a $page->find() method ($page, not $pages) or use a “has_parent=“ in a selector, ProcessWire joins in a special table for the purpose called pages_parents. It uses this table to keep track of family relationships that aren’t otherwise apparent. For instance, let’s say we have page “g” that lives at path /a/b/c/d/e/f/g/. Page “g” only knows that it has page “f” as its parent. It doesn’t know that page “e” is its grandparent unless or until page “f” is loaded. Once “f” is loaded, then “f” can reveal its parent “e”. It works the same for every relationship down to the root parent “a”. So the pages are like a linked list or blockchain of sorts, where only 1 relationship forward or backward is known per page. The “pages_parents” table fills in this gap, enabling PW to quickly identify these relationships without having to load all the pages in the family. This is particularly useful in performing find() operations that you want to limit to a branch started by a particular parent. It’s the reason why we have both $pages->find() that searches the entire site, and $page->find() that limits the search within the branch started by $page. I haven’t paid much attention to the code behind this pages_parents table because it generally just worked, needing little attention. But I came across a couple of cases where the data in the table wasn’t fully accurate with the page tree, without a clear reason why. Then I became aware of one large scale case from a PW user where it was a huge bottleneck. It involved a large site (250k+ pages) and a recursive clone operation that appeared to involve hundreds of pages. But that operation was taking an unreasonable 10 minutes, so something wasn’t right. It came down to something going on with the pages_parents table. Once I dove into trying to figure out what was going on, I realized that if I was to have any chance of keeping track of it, we needed a dedicated API for managing these relationships and the table that keeps track of them. So that’s what got a lot of attention this week. While still testing here, it does appear initially that the 10 minute clone time has gone down to a few seconds, and everything about this relationship management is now rewritten, optimized and significantly improved. It was a lot of work, but absolutely worth it for PW. Rebuilding the entire table from scratch now takes between 2-3 seconds on a site with 250k pages and 150k relationships. The new API can be accessed from $pages->parents(). This API is really useful to the work that I do here (maintaining the core) but I’ll be honest, it’s probably not useful to most others, so I won’t go into the details here, other than to say I’m happy with it. But if you are interested, there are methods finding all the parents in a site, or a particular branch, and methods for rebuilding the pages_parents table, among others. Maybe more will be added to it later that actually would be useful in the public API, but for now I’ll likely leave it out of our public API docs. The $sanitizer->selectorValue() method also got a full rewrite this week (actually, one the last few weeks). It’s now quite a bit more comprehensive and configurable (see the new method options). The previous version was just fine, and actually still remains — you can use it by specifying [ ‘version’ => 1 ] in the $options argument to the selectorValue() method. But the new version is coded better and covers more edge cases, plus provides a lot more configurability for the times when you need it.
  11. 27 points
    We’ve got several updates on the dev branch this week, but I want to finish one more of them before bumping the version to 3.0.150, so will likely do that next week. The biggest change this week is the addition of a new core class called FieldsTableTools. This will be used internally by the $fields API variable for manipulating field table schema in the database, and its methods are accessed from $fields->tableTools(); Though this is one you probably won't be using from the API unless developing Fieldtype modules, so if you just want to know what the benefits are, feel free to skip to the last two paragraphs. Initially these methods are primarily focused on managing unique indexes for fields, though will expand to manage other schema manipulations as well. An example of its utility is also included in this week’s commits—our email fields (FieldtypeEmail) now have the ability to enforce uniqueness at the DB level. If you edit an email field (Setup > Fields > email) and click on the Advanced tab, there’s now a checkbox there to enforce unique values, ensuring that a given email address cannot appear more than once (i.e. not appear on more than one page). The same ability will be added to text and integer fields (FieldtypeText and FieldtypeInteger) as well, but we’re starting with the email field because it’s needed for an update hopefully wrapping up next week: the optional ability to login to the admin with your email address. Having a unique index on a DB column is actually a pretty simple thing, but as it turns out, it takes a lot of code to support the ability in an existing installation. That’s because we have it as something you can toggle on and off (on that Advanced tab), and when you toggle ON a unique index, there can’t be any duplicate values in the existing data, or the index will fail to apply. So there’s a lot of supporting code in FieldsTableTools to do things like detect and warn about duplicate values, delete empty values before adding the index, identify when the index is present without querying the table, reporting error conditions in a manner that understandable and recoverable, as well as the actual schema manipulations that add or remove the index. I realize this sounds a bit technical (and that's partly why I'm not putting this in a more formal blog post), but I think most developers will at some point find it very useful in building sites and applications. Not only will it enable us to safely support login-by-email in the admin (coming next), but it’ll be useful in any situation where you need to prevent a value from repeating. Whether that is in preventing double bookings for a date, location, seat, etc., or preventing any kind of redundancy like post titles, author names, product titles, phone numbers, or codes (UPC, ISBN, ASINs, EIN, SSN, etc.), this feature can come in handy. And supporting it at the DB level is a lot more solid than supporting it at the code level. Right now it’s just supported in email fields, but all of the logic has been delegated to the newly added class so that we can easily support it in any other fields (with text and integer fields coming next). Following that, the $fields->tableTools(); will also be gaining methods for modifying other types of field schema. For instance, rather than just supporting the INT column type for integer fields, wouldn't it be nice to go to Setup > Fields > [some integer field] and select both unsigned and signed integers of different types: TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT, for any new or existing integer field? MySQL also offers a similar array of size variations for other types that would be useful in our text and textarea fields, among others. So you'll be continuing to see ProcessWire offer more options for existing FIeldtypes in future core versions, enabling you to more efficiently store and manage content in PW. But of course we'll do it in a way that keeps it simple, ensuring that you don't have to think about these things unless/until you want or need them.
  12. 27 points
    This week we’ve got version 3.0.147 out on the dev branch. Relative to version 3.0.146, most of the new commits (about 13 of them) are focused on resolving reports submitted at GitHub. I love refactoring existing code and adding optimizations and improvements to the core (and then writing about them in the blog). But right now I’m trying really hard not to! I’d like to have a new master version out before the new year, so the focus is on making sure the core is as absolutely stable as possible. Any time I refactor or add something new (or even sometimes when fixing something or another), there’s a chance of introducing new bugs, and it needs thorough testing on the dev branch, so right now the goal is that everything is thoroughly tested and stable before merging to the master branch. As a result, I’m trying to keep my hands off the core as much as possible, other than fixing obvious things that aren’t likely to introduce new code that needs testing. Please consider 3.0.147 release candidate 1 (RC1) for the next master. Thanks for all of your help and testing. Hopefully by (or before) 3.0.150, we’ll be ready for a merge to master. One way I try and take a break from modifying too much in the core before a master version release is to focus on other modules instead. That’s why last week I wrote about the new UserActivity module, and why next week I’ll be posting all the details for a new LoginRegisterPro module — one that I’ve been working on-and-off for more than a year, but have recently given it a lot of attention in getting it ready for release. I’ll save most of the details on LoginRegisterPro for next week, except to say that it provides an all-in-one solution for front-end login, account registration, profile editing and more. You might be wondering how that’s different from the LoginRegister module I built and released awhile back. That LoginRegister module was built for a very specific purpose and client, largely as a proof-of-concept. But there was a lot more interest in it than I ever expected, and several people suggested that there was a need that would be better served by the quality and support of a Pro module. The result is LoginRegisterPro, which is a full reimagining of that from the ground up. It does everything LoginRegister didn’t, in addition to everything it did. It goes much further in terms of security, reliability and customizability, and I look forward to telling you more about it. I may have it ready to release as soon as next week, but just wanted to make sure all the documentation was complete (and there is quite a lot of it so far). A couple other things I’m looking forward to working on here in the next month or so are the 2020 core roadmap and [finally] the new modules.processwire.com site. Thanks for reading and have a great weekend!
  13. 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!
  14. 27 points
    This week we’ll take a look at a new version of FormBuilder that's on the way (with a screencast), as well as the latest version of the core: ProcessWire 3.0.140— https://processwire.com/blog/posts/pw-3.0.140-and-formbuilder-v40/
  15. 26 points
    Hey folks! Took a couple of late nights, but managed to turn this old gist of mine into a proper module. The name is SearchEngine, and currently it provides support for indexing page contents (into a hidden textarea field created automatically), and also includes a helper feature ("Finder") for querying said contents. No fancy features like stemming here yet, but something along those lines might be added later if it seems useful (and if I find a decent implementation to integrate). Though the API and selector engine make it really easy to create site search pages, I pretty much always end up duplicating the same features from site to site. Also – since it takes a bit of extra time – it's tempting to skip over some accessibility related things, and leave features like text highlighting out. Overall I think it makes sense to bundle all that into a module, which can then be reused over and over again 🙂 Note: markup generation is not yet built into the module, which is why the examples below use PageArray::render() method to produce a simple list of results. This will be added later on, as a part of the same module or a separate Markup module. There's also no fancy JS API or anything like that (yet). This is an early release, so be kind – I got the find feature working last night (or perhaps this morning), and some final tweaks and updates were made just an hour ago 😅 GitHub repository: https://github.com/teppokoivula/SearchEngine Modules directory: https://modules.processwire.com/modules/search-engine/ Demo: https://wireframe-framework.com/search/ Usage Install SearchEngine module. Note: the module will automatically create an index field install time, so be sure to define a custom field (via site config) before installation if you don't want it to be called "search_index". You can change the field name later as well, but you'll have to update the "index_field" option in site config or module settings (in Admin) after renaming it. Add the site search index field to templates you want to make searchable. Use selectors to query values in site search index. Note: you can use any operator for your selectors, you will likely find the '=' and '%=' operators most useful here. You can read more about selector operators from ProcessWire's documentation. Options By default the module will create a search index field called 'search_index' and store values from Page fields title, headline, summary, and body to said index field when a page is saved. You can modify this behaviour (field name and/or indexed page fields) either via the Module config screen in the PocessWire Admin, or by defining $config->SearchEngine array in your site config file or other applicable location: $config->SearchEngine = [ 'index_field' => 'search_index', 'indexed_fields' => [ 'title', 'headline', 'summary', 'body', ], 'prefixes' => [ 'link' => 'link:', ], 'find_args' => [ 'limit' => 25, 'sort' => 'sort', 'operator' => '%=', 'query_param' => null, 'selector_extra' => '', ], ]; You can access the search index field just like any other ProcessWire field with selectors: if ($q = $sanitizer->selectorValue($input->get->q)) { $results = $pages->find('search_index%=' . $query_string . ', limit=25'); echo $results->render(); echo $results->renderPager(); } Alternatively you can delegate the find operation to the SearchEngine module as well: $query = $modules->get('SearchEngine')->find($input->get->q); echo $query->resultsString; // alias for $query->results->render() echo $query->pager; // alias for $query->results->renderPager() Requirements ProcessWire >= 3.0.112 PHP >= 7.1.0 Note: later versions of the module may require Composer, or alternatively some additional features may require installing via Composer. This is still under consideration – so far there's nothing here that would really depend on it, but advanced features like stemming most likely would. Installing It's the usual thing: download or clone the SearchEngine directory into your /site/modules/ directory and install via Admin. Alternatively you can install SearchEngine with Composer by executing composer require teppokoivula/search-engine in your site directory.
  16. 25 points
    Here’s a brief update on what’s new in the core since last week. Next week I’m going to compile a more complete overview of everything that’s in 3.0.154 (and more) and put it in a blog post. I wanted to do that today, but my kids have needed a lot of help with various things today (and this whole week actually) with all of this online school stuff and I’ve run out of time. We're a few weeks in and school is cancelled for the rest of the year due to the coronavirus (likely the same around the world). I'm still learning how to adapt to kids around all day and needing attention, but overall it's nice to have the family around more even if it is sometimes hard to get work done. So I’ll keep this sort of brief, and get into more details next week. Several updates were made to the core/LanguageFunctions.php (language functions) file, with biggest being the option to programmatically replace translation text (whether multi-language support is installed or not), useful for very easily updating core/module output to say exactly what you want, and without needing to install multi-language modules. No longer do you need to use various hooks to change text in one module or another. Also moved the __('text') configuration options to dedicated functions which makes it more clear to use, as well as much better for documentation purposes: wireLangEntityEncode(), wireLangTranslations(), wireLangReplacements(). You'll see these new functions in our docs by next week. More details and examples of these in action in next week’s blog post. The CKEditor version has been upgraded from 4.12.1 to 4.14.0. To see what’s changed, see the release notes for CKEditor 4.13.0, 4.13.1 and 4.14.0 here. There’s actually quite a lot here in terms of additions and fixes. Note especially the two security related fixes in 4.14.0 (though I think only the first may be applicable to us). While it looks like a rather unlikely combination of factors needs to come together, and with just the right person, it’s good to be aware of nevertheless. Of note is that these particular cases are entirely client side, before PW ever gets to see or filter the content, so this is something that only a CKEditor upgrade can fix. Updated the core Debug class to use PHP 7.3’s recommended hrtime (high resolution time) for debug timers, rather than microtome. This is only available in PHP 7.3 and newer. As far as I can tell it produces identical results to microtome (as far is our timers go) but the PHP documentation page indicates hrtime is what we should now be using, so that’s what we’re going with. I also did a little refactoring and minor improvements in the Debug class as well. Updated ProcessWire’s Inputfield rendering system so that you can customize markup by matching a field by name, and without hooks. This essentially takes part the Inputfield customization system developed for LoginRegisterPro and brings it into the core. This opens up a lot more customizability for those that want it. Various resolutions of GitHub issue reports, with more on the way. The above are just updates from the last week, but there’s plenty more in 3.0.154 as well. See ProcessWire Weekly #309 for details on Image and file field updates, new $database->columnExists() method and new Selectors::getOperators() method, and more. There are also the new expanded file and image properties, as discussed in #308 and the last blog post. Thanks for reading and I hope you all have a great weekend!
  17. 25 points
    ProcessWire 3.0.142 has a lot of updates but the biggest is the addition of custom fields support for file and image fields. In this post, we take a closer look and also outline all of the new features in the just-released FormBuilder v40— https://processwire.com/blog/posts/pw-3.0.142/
  18. 25 points
    A module created in response to the topic here: Page List Select Multiple Quickly Modifies PageListSelectMultiple to allow you to select multiple pages without the tree closing every time you select a page. The screencast says it all: https://github.com/Toutouwai/PageListSelectMultipleQuickly https://modules.processwire.com/modules/page-list-select-multiple-quickly/
  19. 24 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/
  20. 24 points
    Today we have a new master version released, version 3.0.148! The last master version was 3.0.123, so there are 25 new versions worth of upgrades, fixes and optimizations in this new master version, relative to the previous. In this post we’ll take a closer look at what’s new, how to upgrade, and more— https://processwire.com/blog/posts/pw-3.0.148-master/
  21. 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/
  22. 23 points
    --- There might be some changes to this module in the near future! Please see this comment --- I guess we have all been there... We need to store a price to a product. "Ok, easy, let's create a new field for that in the PW backend!" might be the first thought. But then the headache starts... What about TAX? What about NET and GROSS values? And what about rounding problems when not using the correct float or decimal values ( https://processwire.com/talk/topic/7542-development-fieldtypefloat-fieldtypedecimal/ )? Meet RockPrice - a brand new (and not well tested!) module to save you from those headaches and make the UI more compact and reactive (nobody wants to calc tax/net/gross manually!). If you discover any issues or have suggestions for improvement please let me know! 🙂 --- Download: https://github.com/BernhardBaumrock/RockPrice --- RockPrice Price Fieldtype + Inputfield for ProcessWire CMS Settings Usage The field always returns a RockPriceMulti object. This object contains an array of items and the totals vor vat, net and gross (where tax stands for the tax rate in percent and vat for the actual tax value, eg. Euros or Dollars): d($page->price); d($page->price->items->first()); API Saving field value: $page->setAndSave('price', [1000, 20]); $page->setAndSave('price', [ [1000, 20], [3000, 10], ]); Comparisons $p1 = new RockPrice(1000, 20); $p2 = new RockPrice(1000, 10); d($p1->equals($p2)); // false $m1 = new RockPriceMulti([$p1, $p2]); $m2 = new RockPriceMulti([$p1, $p2]); $m3 = new RockPriceMulti([$p2, $p1]); // flipped! d($m1->equals($m2)); // true d($m1->equals($m3)); // false d($m1->equals($m3, true)); // true (ignoring sort order)
  23. 23 points
    ProcessWire 3.0.146 contains about 22 commits (relative to 3.0.145) and these commits are largely focused on resolving reports from the processwire-issues repository. However, there have also been several improvements and related additions. One of these additions was a foundational upgrade that adds support for Fieldtype modules to use a custom class for Field objects. This will open more possibilities for improved Field/Fieldtype-specific APIs. Several have asked for improvements in the APIs of Repeaters and other fields, so this is a step that begins the lay the tracks for moving in that direction. Traditionally the API calls for working with Fields and Fieldtypes have not been nearly as simple as those that work with Pages, so this will be an upgrade that narrows and eventually eliminates that gap, longer term. On the core side, I currently plan on using this to improve the API for Repeaters, Comments and Options fields, and perhaps others. Outside of the core, ProFields will eventually take advantage of custom Field objects as well. As usual, none of this will break any existing code, but it will add simplicity for those that work with Field/Fieldtype APIs in ProcessWire. As for other changes in 3.0.146, I think last week’s ProcessWire Weekly did a great job of covering them, so if you are interested be sure to check that out. Next week is partially a holiday week here in the US, so I’ll be on a little bit of a reduced schedule, but will still be working on the core. I’m also releasing a new module into the ProDevTools set of modules next week, so I’ll tell you more about that one in next week’s blog or forum post. Thanks for reading and have a great weekend!
  24. 22 points
    This module is (yet another) way for implementing a cookie management solution. Of course there are several other possibilities: - https://processwire.com/talk/topic/22920-klaro-cookie-consent-manager/ - https://github.com/webmanufaktur/CookieManagementBanner - https://github.com/johannesdachsel/cookiemonster - https://www.oiljs.org/ - ... and so on ... In this module you can configure which kind of cookie categories you want to manage: You can also enable the support for respecting the Do-Not-Track (DNT) header to don't annoy users, who already decided for all their browsing experience. Currently there are four possible cookie groups: - Necessary (always enabled) - Statistics - Marketing - External Media All groups can be renamed, so feel free to use other cookie group names. I just haven't found a way to implement a "repeater like" field as configurable module field ... When you want to load specific scripts ( like Google Analytics, Google Maps, ...) only after the user's content to this specific category of cookies, just use the following script syntax: <script type="text/plain" data-type="text/javascript" data-category="statistics" data-src="/path/to/your/statistic/script.js"></script> <script type="text/plain" data-type="text/javascript" data-category="marketing" data-src="/path/to/your/mareketing/script.js"></script> <script type="text/plain" data-type="text/javascript" data-category="external_media" data-src="/path/to/your/external-media/script.js"></script> <script type="text/plain" data-type="text/javascript" data-category="marketing">console.log("Inline scripts are also working!");</script> The type has to be "optin" to get recognized by PrivacyWire, the data-attributes are giving hints, how the script shall be loaded, if the data-category is within the cookie consents of the user. These scripts are loaded asynchronously after the user made the decision. If you want to give the users the possibility to change their consent, you can use the following Textformatter: [[privacywire-choose-cookies]] It's planned to add also other Textformatters to opt-out of specific cookie groups or delete the whole consent cookie. You can also add a custom link to output the banner again with a link / button with following class: <a href="#" class="privacywire-show-options">Show Cookie Options</a> <button class="privacywire-show-options">Show Cookie Options</button> This module is still in development, but we already use it on several production websites. You find it here: PrivacyWire Git Repo Download as .zip I would love to hear your feedback 🙂 CHANGELOG 0.1.1 Debugging: fixed error during uninstall 0.1.0 Added new detection of async scripts for W3C Validation 0.0.6 CSS-Debugging for hiding unused buttons, added ProCache support for the JavaScript tag 0.0.5 Multi-language support included completely (also in TextFormatter). Added possibility to async load other assets (e.g. <img type="optin" data-category="marketing" data-src="https://via.placeholder.com/300x300">) 0.0.4 Added possibility to add an imprint link to the banner 0.0.3 Multi-language support for module config (still in development) 0.0.2 First release 0.0.1 Early development
  25. 22 points
    There were lots of core updates this week, very much in the same theme as last week — major long term quality improvements for the core, but no shiny toys to play with… Just lots of good and solid foundational core improvements, and a few fixes too. It's mostly kind of technical stuff that's probably not that interesting to read about, so I'll keep this short, but for those interested here's the commit log. Thanks for reading and have a great weekend!
  26. 22 points
    I hope that you all are having a good week and staying healthy. I’ve been very much focused on preparing a new ProcessWire-powered client’s website for launch, hopefully sometime next week. This is a site that’s already pretty well established with the search engines and such, so I’ve been going through all of the pre-launch QA in terms of optimization, SEO accessibility, mobile testing, getting the multi-language translators everything they need to translate, adding ProCache to the picture, and so on. For me this is one of the most fun parts of development, with relentless optimization and having it all come together… but also one of the most time intensive. This is where all the little details surface, things that need improvement become more apparent, and new ideas keep popping up. It’s also where lots of the bugs are discovered and fixed, and where I sometimes have to slap myself on the head for missing one thing or another, but then of course taking care of it. So that’s been most of this week, and I think likely at least half of next week too. But then it’s back to work on the core and modules, covering issue reports and more. I’ve actually been at work on some parts of the core and ProCache this week as well, but not to the point of finishing anything major. So you’ll see some updates on the core this week, and there’s also lots of stuff pending that’s not yet committed, and it’s all work in progress, so stuff to review later. With kids at home I’m still moving at a little slower pace than usual, but starting to get the hang of it, doing well and building momentum. While I don’t have much new to offer in this update, thanks for reading anyway, and I hope that you have a great weekend!
  27. 22 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!
  28. 21 points
    We have created a module to create BlurHash strings for images while uploading in ProcessWire. This blurry images will be saved in the database because they are very small (20-30 characters) and can be used for Data-URL's as placeholders for image-lazy loading. https://github.com/blue-tomato/ImageBlurhash E.g. where we use this in production: https://www.blue-tomato.com/en-INT/blue-world/ https://www.blue-tomato.com/en-INT/blue-world/products/girls-are-awesome/ https://www.blue-tomato.com/en-INT/buyers-guides/skateboard/skateboard-decks/ https://www.blue-tomato.com/en-INT/team/anna-gasser/
  29. 21 points
    Last week I posted in the blog about current projects in progress, and consistent with that, this week I’ve been working primarily on the upgrades to our file/image fields. To start, this focuses on storing additional info in the DB table, including file size, created/modified user info, and for image fields: width and height. This is useful in making these properties searchable from page-finding selectors, but also a necessary pre-requisite to supporting external file systems. A “ratio” floating point property is now available on image fields as well (and also stored in the DB) and this value reflects the width divided by the height. This is a searchable property and will be useful for quickly finding portrait images, landscape images, square images, or any proportion you need to find. If you want to find images that are at least twice as wide as they are tall, then you’d be looking for a “ratio” of 2.0 or larger (“ratio>=2”). If you needed to find perfectly square images (i.e. 300x300) then you’d be looking for a ratio of 1.0. If you needed to find portrait images, then you’d be looking for images with a ratio under 1.0 (or twice as tall as wide would be 0.5). You can figure out what ratio you are looking for just by taking your target width and dividing by your target height. Longer term, combined with the other added properties, the goal is that this will help with finding pages that have images suitable for specific placements, proportions and dimensions. These new properties won’t be useful at first on existing sites because the data just isn’t present in the DB, even if the columns to store them are. They are populated over time whenever the page is saved. But I’m going to enable an option to have it populate all of this automatically whenever the field is even loaded. It’s actually there in the core right now, but I’ve got it disabled so I can test it out on a few more of my installations before others start using it. There were some other upgrades to the core as well, including improvements to the Selector and Selectors classes, addition of a $database->columnExists() method, lots of new (mostly internal) methods in FIeldtypeFile/FieldtypeImage, and some refactoring of the FieldtypeMulti class, among other minor updates. While there were a lot of changes and additions this week, it’s not stuff that you’ll likely be using right away, so I’m going to hold off on bumping the version till next week. Thanks for reading and have a great weekend!
  30. 21 points
    I’ve been working on some major upgrades to our InputfieldDatetime core module and they are just about ready, but because there’s a lot of new code involved, I just want to test things out more thoroughly before pushing to the dev branch. I’ll have more details on this for you next week, but here’s a summary of what’s new in our Date/Time Inputfield coming in 3.0.152: If you’ve used date inputs in ProcessWire before (whether in the admin, FormBuilder or elsewhere), you know that they consist of a text field with a configurable date format and optional jQuery UI powered date picker. On recent projects, I’ve wanted more options here. First off, I’ve wanted support for the HTML5 “date” and “time” input types, because on some browsers (mobile-based in particular) the browser implementation is quite good. On mobile clients (testing in Android Chrome at least), I find it’s actually pretty amazing, certainly preferable to the jQuery UI date picker. On desktop clients, I think the jQuery UI date picker might still be preferable, but the browser implementation is still quite good (though varies widely from browser to browser). So I’ve upgraded InputfieldDatetime to support both HTML5 “date” and “time” inputs as configurable options, as well as the ability to use them both together. Another thing I’ve often wished for when it comes to date inputs is the ability to have the Month, Day and Year isolated into separate selects. This is something that would work in any environment, and not require any particular feature support from the browser, nor would it require jQuery UI (like if you are wanting lightweight date fields in FormBuilder). This type of date selection seems to be the simplest, easiest and most portable way to go, and it’s something I’ve wanted for quite a long time. So support for this has also been added to InputfieldDatetime this week as well! Of course the order of month, day and year is completely configurable. Some other useful additions include support for HTML5 step values (date or time), minimum and maximum dates (and times), support for seconds in time selections, automatic localized month names in selects (via strftime), and the ability to ask for just month and year (when day is not applicable), or month and day (when year is not applicable). These updates—along with others—will appear in ProcessWire 3.0.152 on the dev branch within the next week.
  31. 21 points
    Hi guys, I was very excited for this module, but my life took a huge direction change and I no longer have the time to invest in module development. I am gonna leave the files here. You guys can take it and run. Maybe there might be something useful here. Maybe not. I still think it's a good idea to do drag and drop modal building in PW. So hopefully one day something like that can come to light. I love this community and I love ProcessWire. Live long and prosper. - Joshua Designme 2.zip
  32. 21 points
    This latest version on the dev branch includes a few additions, optimizations and fixes. I'm only in the office for a brief time today, so I'll plan to cover these in more detail hopefully in next week's blog post (along with next week's updates), but here's a preview. There's a lot of improvements to the "Overrides" tab that you see when editing a field (Setup > Fields > Edit). These are the field settings in the contexts of different templates. In 3.0.144, you now get a list of all field settings that you want to allow to be customized per-template. In previous versions, you only saw this option if you were in $config->advanced mode. It's still a YMMV setting, so appropriate disclaimers have been added to the field. But since it's been there for more than a year and there have not been any reports of issues (that I'm aware of yet), I thought it didn't need to be limited to advanced mode anymore. The table that shows you what overrides are in place is now improved as well. You can now click any override setting to open a modal window to see/edit them as needed (previously they were just non-clickable text labels). This is the same window that you get when clicking a field in the Template editor. Though unlike the Template editor, it takes you directly to the setting you clicked on and highlights it. After saving or closing the window, it updates the table for any changes you made. In addition to this, the overrides table now shows a Diff rather than separate Old and New values for modified settings, which better clarifies what was changed. (That Diff comes by way of a new method added in the WireTextTools class). Version 3.0.144 also adds a new Inputfields Javascript API. More methods are likely to be added to it, but you can see what's available in this first version in the comments for this file (inputfields.js). The purpose of this Inputfields JS API is to make it simpler to manipulate Inputfields from the JS side, as well as solve some needs that I've had in the core and modules; which presumably some other developers may have had as well. It takes a lot of stuff that previously required knowing which classes and attributes to manipulate and simplifies them to simpler methods calls from the new Inputfields JS API variable. It also abstracts away some admin-theme specific stuff so that you can use the same API to perform certain manipulations regardless of admin theme. I'm still working out some details and likely have some issues to fix and additional functions to add, so I'll save examples and more description for next week. Though the updates mentioned for the "Overrides" tab do use a little bit from this API. This version also contains a couple of fixes per GitHub issue reports, and that'll continue into next week's version as well. More updates next week. Until then, thanks for reading and have a great weekend!
  33. 21 points
    A sneak preview of a new page builder concept that I'm close to completing. I'll write more about this in coming weeks, but this video demonstrates a lot of unique things going on:
  34. 20 points
    This week I'm not bumping the version number just yet because I've got lots of work in progress. The biggest thing so far is something I hinted at last week. Basically, I like what the addition of the MySQL query expansion operators have brought (per posts last week and week before), but they also reveal what's lacking: something as simple as a search for "books" still can't directly match the word "book". But that's the most basic example. It's not a limitation of ProcessWire, but just the type of database indexes in general. I think it'd be amazing if ProcessWire had the ability of being really smart about this stuff, able to interpolate not just plurals vs. singulars but related words. In a perfect world, this is what query expansion would do (in addition to what it already does). But the reality is that it involves all kinds of complicated logic, rules and dictionaries; well beyond the scope of even a database. And it can be vastly different depending on the language. So this isn't something we can just add to the core and have it work. On the other hand, I figured maybe we should just put in a hookable method that just pretends the ability was there. Then people could hook it and make it respond with variations of words, according to their needs. The searches that use query expansion could then call this method and use whatever it returns... for when someday the ability is there. So I went ahead and added that hook — WireTextTools::wordAlternates(). And our database-searching class (DatabaseQuerySelectFulltext) now calls upon it, just in case an implementation is available. Well, after getting that hook added and having our class call it, naturally I wanted to test it out. So I got to work on it and came up with this module: WireWordTools. The WireWordTools module provides an API for English word inflection and lemmatisation. And it hooks that new method mentioned above, so that you can install it and immediately have it bring your searches to the next level. While it only helps for English-language searches, maybe we'll be able to add more languages to it, or maybe it'll lead to other modules that do the same thing for other languages. The expanded/alternate words are only used for searches that use the new query expansion operators, which are the ones that have a "+" in them: ~+=, ~|+=, *+=, **+=. They all can return similar results, but are weighted differently. Unlike most operators, where the logic is direct and you can expect them to always behave the same way, these query expansion operators are more subjective, and ones I think we should intend to keep tweaking and improving over time to continually improve the quality of the results they return. Basically, they are geared towards building site search engines, so I think it makes sense for us to pursue anything that makes them better at that, rather than aiming to always have them return the same thing. I am currently testing out the ~|+= operator ("contains any words expand") on our main site search engine here, along with the WireWordTools module. Finally, searching for "books" does match "book" too, and a lot more. More to be done here, but it's a good start hopefully.
  35. 20 points
    In ProcessWire 3.0.160 we’ve got some major upgrades and additions to our text-matching selectors and operators. This brings a whole new level of power to $pages->find() and similar API calls, especially when it comes to search engine type queries. This blog post also includes a demo search engine that lets you test everything out live— https://processwire.com/blog/posts/pw-3.0.160/
  36. 20 points
    I've only recently stumbled across Processwire. Thanks to ADHD and avoiding doing other work. I'm blown away at the power of Processwire. Ryan and all those who've built modules have done an incredible job of making a system that is fully customizable with incredible dev speed. Wordpress has so much bloat, it isn't very user friendly when all you want to do is quick a blog post or change. In fact unless you're a geek, It's actually quite daunting. Hell I'm still daunted by it all the time. I've never got my head around the interface layout and placement of menus and settings. On top of that, most plugins are trying to cater for anyone everyone and they have more junk to 'impress' than anything else. I love that I can just build my own functionality with PW easily or find a way to do it. What I'm particularly excited about is the end clients point of view. I could show a client in 5 minutes what they need to do to maintain their site. Changing look and theme as looks and trends move on would also be a cinch because no mark-up data is stored within the fields, unlike all the WP editor themes out there. Change templates, new site and look. All legacy data looks as good. So thank you to Ryan and the community for Processwire. I've only recently restarted developing after 15 years and frankly really didn't want to but PW has excited about web development again. I'm not one for blowing air up peoples arses but when something excites me, it excites me. 😁 Now I have to decide on whether I'm going to import my existing website's data or start an entirely new one 🙂 G
  37. 20 points
    Hi all, Apologies for the very loud silence! I hope to elaborate more on this a bit later. However, rather than keep people guessing, I'll write something short. I have been working my fingers to the bone to release a beta by spring 2020. I suppose it hasn't gone unnoticed that I rarely post in the forums at large these days. This is because I am dedicating nearly all my time to Padloper. The plan was to start early beta testing in mid-April 2020. This was largely on track. Like many of us, maybe most of us in the forums, we have all been affected in one way or another by the current situation in the world. This has thrown a monkey wrench in the works. I have had to readjust how I work, albeit my productivity taking a hit. I wish I could properly 'guesstimate' how much delay this is going to cause but it will just be futile. On the other hand, I appreciate that you have been waiting for a relatively long time for this release. I want to reassure you that I am not just kicking the can down the road. Maybe I should have been showing you more screenshots of progress but currently, that would just eat further into my limited time. Thanks for reading, and hopefully, your patience. Cheers.
  38. 20 points
    I'm currently building a Fieldtype/Inputfield for selecting date and time ranges (eg for events). There was quite some interest in this thread, so I thought I start a dedicated discussion... Background: I guess everybody of us knows the problem: You want to present events on your website and you start with a new template and a title and body field... For events you'll also need a date, so you add a datetime field called "eventdate". Maybe the event does not take place on a specific day but on multiple days, so you need a second field... Ok, you rename the first field to "date_from" and add a second field called "date_to". So far, so good. Then you want to list your events on the frontend. Simple thanks to the pw API, you might think. But you realize that it's not THAT simple... Which events take place on a specific day? What would the selector be? Yeah, it's not that complicated... it would be something like: $from = strtotime("2020-01-01"); $to = strtotime("2020-02-01"); $events = $pages->find("template=event, date_from<$to, date_to>$from"); Why? See this example, where the first vertical line represents the $to variable and the second is $from: The start of the event must be left of $to and the end must be right of $from 🙂 Ok, not that complicated... but wait... what if the date range of the event (or whatever) was not from 2020-01-18 to 2020-02-25 but from 18 TO 25 (backwards)? The selector would be wrong in that case. And did you realize the wrong operator in the selector? We used date_to>$from, which would mean that an event starting on 2020-01-01 would NOT be found! The correct selector would be >=$from. That's just an example of how many little problems can arise in those szenarios and you quickly realize that the more you get into it, the more complicated it gets... Next, you might want to have full day events. What to do? Adding a checkbox for that could be a solution, but at the latest now the problems really begin: If the checkbox is checked, the user should not input times, but only dates! That's not possible with the internal datetime field - or at least you would have to do quite some javascript coding. So you add 2 other fields: time_from and time_to. You configure your date fields to only hold the date portion of the timestamp and show the time inputfields only if the "fullday" checkbox is not checked. We now have 5 fields to handle a seemingly simple task of storing an event date. That's not only taking up a lot of space in the page editor, you'll also have to refactor all your selectors that you might already have had in place until now! Idea So the idea of this module is to make all that tedious task of adding fields, thinking about the correct selectors etc. obsolete and have one single field that takes care of it and makes it easy to query for events in a given timeframe. The GUI is Google-Calendar inspired (I'm acutally right now liking the detail that the second time input comes in front of the date input). I went ahead and just adopted that: Next steps I'm now starting to build the FINDING part of the module and I'm not sure what is the best way yet. Options I'm thinking of are: // timestamps $from = strtotime("2020-01-01"); $to = strtotime("2020-02-01")+1; // last second of january // option 1 $pages->find("template=event, eventdate.isInRange=$from|$to"); $pages->find("template=event, eventdate.isOnDay=$from"); $pages->find("template=event, eventdate.isInMonth=$from"); $pages->find("template=event, eventdate.isInYear=$from"); // option 2 $finder = $modules->get("RockDaterangeFinder"); $finder->findInRange("eventdate", $from, $to, "template=event"); $finder->findOnDay("eventdate", $from, "template=event"); ... I think option 1 is cleaner and easier to use and develop, so I'll continue with this option 🙂 Future As @gebeer already asked here I'm of course already thinking of how this could be extended to support recurring events (date ranges) in the future... I'm not sure how to do that yet, but I think it could make a lot of sense to build this feature into this module. I'm not sure if/how/when I can realease this module. I'm building it now for one project and want to see how it works first. Nevertheless I wanted to share the status with you to get some feedback and maybe also get your experiences in working with dates and times or maybe working with recurring events (or the abandoned recurme field). For recurring events the finding process would be a lot more complicated though, so there it might be better to use an approach similar to option 2 in the example above.
  39. 20 points
    ... and everything went silky-smooth. I finally got around to update a small portfolio site from PW 2.3 straight to latest dev. I spent roughly one hour, tested everything, and hey: no major hassles. I updated the few modules I used (and installed some new ones like Tracy or AoS), updated PHP as well, and that was more or less it. Imagine having such an un-touched WP, Typo3 or MODX site, and updating straight to the latest version... What a nightmare. That is - if they would actually still run that long 🙂 I actually expected it to go as smooth as it did, but I'm always surprised and in awe again when I do such a big update. Thank you ProcessWire.
  40. 20 points
    In the last few weeks... or almost months... I worked on a project for a restaurant. Sad to say that the company behind the restaurant went out of business before the project could be finished - or was paid or the website ever saw the light of day. As there is kind of a lot of work in it... but yet a lot of customization... I decided to create a site profile of it and make it public as ProcessWire Barebone Site Profile. Right now I'm stripping out every client detail and finish some functions and details that were planned a few weeks back so you can build right on top of it, if you like or want. Maybe it's only a playground for someone or an inspiration for how to f**k up data-mangement... but hey... better than a ZIP file on a backup drive. 😎 As the project was and is super tailor-made to that client, it may not work for a lot of restaurants out there, but nonetheless I don't want miss the opportunity to offer a foundation for upcoming restaurant projects you may face. The project had a huge focus on dishes, beer and wine variations so those templates are kind of feature-rich. You might want to rip out some of it as most restaurants don't focus that much on those details. Important details I want to be honest and clear about: this profile does NOT include a highly polished and usable frontend as the design is paid and therefore can't be made public the frontend will be a collection of re-usable snippets to show every possible detail of each page, item and whatever the whole site profile will be without any support - future updates aren't planned right now the site profile will be released in single-language setup only (english) existing translations (as seen in parts of the screenshots) will be changed to english existing data, for example dishes, will be replaced with demo content if you, one of your clients or anyone else wants to use this profile - feel free to do so Pro Modules were already stripped out of it only public modules were used and are included with the profile itself the full module list will soon be published on Github the full README will soon be published on Github the full LICENSE will soon be published on Github So... yes... that's just a short preview of an upcoming site profile. As mentioned before, the site profile will most likely never ever receive any future updates, so it will be available on Github only. It's not planned to publish this as a full featured site profile, yet. More details, screenshots and the site profile itself (of course) will be available soon. Questions? Please feel free to ask. Github Repository: https://github.com/webmanufaktur/pwbrestaurant
  41. 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!
  42. 19 points
    Hello, I would like to present you a new module which aim to facilitate the productivity of your editors/publishers when working on ProcessWire. The idea begun when my co-worker told me that when typing in ProcessWire CkEditor field he was feeling "loosing motivation" when writing big wall of text and/or inspiration. So he opened his web-browser and show me a site looking to Wordpress - feel free to put your preferred emoji here - then he opened Gutenberg... typed some text and moving some "blocks". I understood immediately why he got this feeling with CkEditor. If you or your client feel like this guy, then you will love this module ! What is currently supported ? Features Auto-save Medias upload support HannaCode support Blocks Implemented Heading Image Paragraph Embed Quote Code Link Table (beta) Block Delimiter Raw HTML Note (custom block markup) Feature Request Frontend Edition And there you go for the preview - sorry I am to lazy and bad at typing text so I had a copy/pasta moment : Module featured in the ProcessWire Weekly #317 - Thanks @teppo
  43. 19 points
    In the blog post last week we looked at some of the two-factor authentication system upgrades, like the new “remember this computer” feature. This week I finished off the remaining parts, as well as released new versions of both the TfaTotp and TfaEmail modules. Auto-enable TFA support We now have auto-enable support (forced 2FA), which lets you setup two-factor authentication for users, without their input (if they haven’t enabled it already). This is a good way to add a lot of security for very little work. Currently, the module that supports this is the TfaEmail module. That’s because it’s a safe bet to assume the user has access to their email, even if they haven’t specifically setup 2FA. So email is a very good way to nudge people into 2FA, and people are already used to this, as many online services now do it. Considering that the computer can now be remembered, I think it’s unlikely you’ll get any complaints from users. Setting up auto-enable is really simple. Grab the latest version of the TfaEmail module and install it. Then go to your ProcessLogin module settings (Modules > Configure > ProcessLogin) and you’ll see an option there to select Email in the “Force two-factor authentication - Type” field. If you want to limit this to specific roles, then you can also do that here. If you don’t select any roles, then it applies to all roles. Once setup, any user logging into your admin will be asked to enter an authentication code sent to their email, and they’ll need that code to complete the login. Chances are they’ll also click that “remember this computer” checkbox so that they can skip the code on future logins. TfaEmail version 2 The new version of the TfaEmail module also lets you now configure what WireMail module you want it to use for sending authentication emails. If using multiple mail sending services, you’ll want your most reliable and fastest email sending service to handle these kinds of transactional emails. TfaTotp version 4 Once users understand the benefits of 2FA, chances are they’ll want to upgrade to TOTP, where they can use a dedicated authenticator app. The ProcessWire TfaTotp module got several upgrades this week. The biggest was the addition of a locally hosted QR code generator (QRCode for PHP by Kazuhiko Arase). No longer does it have to rely upon an external service to generate QR codes (previous versions used Google Charts for QR code generation). In addition, the TOTP TwoFactorAuth library has been updated to the latest version. Moving those modules to the core Speaking of those two modules (TfaEmail and TfaTotp), thanks for your input last week about their inclusion in the core. It sounds like most think it’s a good idea, so I think we’ll go that route. But I need a little more time to do that, so going to hold that update and the 3.0.160 version bump for next week. Coming next week: Useful new selector operators Next week I’ve also got a couple of special new text-matching operators being added to our selectors system that I think you are going to really like. They are operators that are especially useful to those building text search engines, and ones that I’ve found so useful this week that I wish we’d had them since the beginning. I’m excited to add those into 3.0.160 and tell you more about them next week. By the way, while 3.0.160 isn't officially the version on the dev branch yet, if you download the current dev branch version (3.0.159), all of the TFA updates mentioned above are present and ready to use.
  44. 19 points
    This module allows you to integrate hCaptcha bot / spam protection into ProcessWire forms. hCaptcha is a great alternative to Google ReCaptcha, especially if you are in the EU and need to comply with privacy regulations. The development of this module is sponsored by schwarzdesign. The module is built as an Inputfield, allowing you to integrate it into any ProcessWire form you want. It's primarily intended for frontend forms and can be added to Form Builder forms for automatic spam protection. There's a step-by-step guide for adding the hCaptcha widget to Form Builder forms in the README, as well as instructions for API usage. Features Inputfield that displays an hCaptcha widget in ProcessWire forms. The inputfield verifies the hCaptcha response upon submission, and adds a field error if it is invalid. All hCaptcha configuration options for the widget (theme, display size etc) can be changed through the inputfield configuration, as well as programmatically. hCaptcha script options can be changed through a hook. Error messages can be translated through ProcessWire's site translations. hCaptcha secret keys and site-keys can be set for each individual inputfield or globally in your config.php. Error codes and failures are logged to help you find configuration errors. Please check the README for setup instructions. Links Github Repository and documentation InputfieldHCaptcha in the module directory (pending approval) Screenshots (configuration) Screenshots (hCaptcha widget)
  45. 19 points
    Just created my first VSCode Extension - if you like it please give it a star on Github and rate it on VSCode Marketplace 🙂 Installation: Either via https://marketplace.visualstudio.com/items?itemName=baumrock.pwsnippets or directly in VSCode: Contributions welcome! https://github.com/BernhardBaumrock/pwsnippets
  46. 19 points
    This week we have some major improvements to our core date/time Inputfield, as well as a new ability to specify your own custom classes for Page objects. Read on for all the details and examples— https://processwire.com/blog/posts/pw-3.0.152/
  47. 19 points
    I've spent the last while experimenting with srcset implementation - and PageimageSrcset is the result: PageimageSrcset Provides configurable srcset and sizes properties/methods for Pageimage. Overview The main purpose of this module is to make srcset implementation as simple as possible in your template code. It does not handle images rendered in CKEditor or similar fields. For an introduction to srcset and sizes, please read this Mozilla article about responsive images. Pageimage::srcset() // The property, which uses the set rules in the module configuration $srcset = $image->srcset; // A method call, using a set rules string // Delimiting with a newline (\n) would also work, but not as readable $srcset = $image->srcset("320, 480, 640x480 768w, 1240, 2048 2x"); // The same as above but using an indexed/sequential array $srcset = $image->srcset([ "320", "480", "640x480 768w", "1240", "2048 2x", ]); // The same as above but using an associative array // No rule checking is performed $srcset = $image->srcset([ "320w" => [320], "480w" => [480], "768w" => [640, 480], "1240w" => [1240], "2x" => [2048], ]); // Use the default set rules with portrait images generated for mobile/tablet devices $srcset = $image->srcset(true); // Return the srcset using all arguments $srcset = $image->srcset("320, 480, 640x480 768w, 1240, 2048 2x", [ "portrait" => "320, 640", ]); // The set rules above are a demonstration, not a recommendation! Image variations are only created for set rules which require a smaller image than the Pageimage itself. On large sites this may still result in a lot of images being generated. If you have limited storage, please use this module wisely. Portrait Mode In many situations, the ratio of the image does not need to change at different screen sizes. However, images that cover the entire viewport are an exception to this and are often the ones that benefit most from srcset implementation. The main problem with cover images is that they need to display landscape on desktop devices and portrait when this orientation is used on mobile and tablet devices. You can automatically generate portrait images by enabling portrait mode. It is recommended that you use this in combination with Pageimage::focus() so that the portrait variations retain the correct subject. The generated variations are HiDPI/Retina versions. Their height is determined by the portrait ratio (e.g. 9:16). Variations are always generated, regardless of whether the original image is smaller. Upscaling is disabled though, so you may find that some variations are actually smaller than they say they are in their filename. The sizes attribute should be used when portrait mode is enabled. Pageimage::sizes will return (orientation: portrait) and (max-width: {maxWidth}px) 50vw by default, which handles the use of these images for retina devices. The maximum width used in this rule is the largest set width. Pageimage::sizes() There is no option to configure default sizes because in most cases 100vw is all you need, and you do not need to output this anyway as it is inferred when using the srcset attribute. You can use the method for custom sizes though: // The property $sizes = $image->sizes; // Returns 100vw in most cases // Returns '(orientation: portrait) and (max-width: {maxWidth}px)50vw' if portrait mode enabled // A method call, using a mixture of integer widths and media query rules // Integer widths are treated as a min-width media query rule $sizes = $image->sizes([ 480 => 50, "(orientation: portrait) and (max-width: 640px)" => 100, 960 => 25, ]); // (min-width: 480px) 50vw, (orientation: portrait) and (max-width: 640px) 100vw, (min-width: 960px) 25vw // Determine widths by UIkit 'child-width' classes $sizes = $image->sizes([ "uk-child-width-1-2@s", "uk-child-width-1-3@l", ]); // (min-width: 640px) 50vw, (min-width: 1200px) 33.33vw // Determine widths by UIkit 'width' classes $sizes = $image->sizes([ "uk-width-1-2@m", "uk-width-1-3@xl", ]); // (min-width: 960px) 50vw, (min-width: 1600px) 33.33vw // Return the portrait size rule $sizes = $image->sizes(true); // (orientation: portrait) and (max-width: {maxWidth}px) 50vw // The arguments above are a demonstration, not a recommendation! Pageimage::render() This module extends the options available to this method with: srcset: When the module is installed, this will always be added, unless set to false. Any values in the formats described above can be passed. sizes: Only used if specified. Any values in the formats described above can be passed. uk-img: If passed, as either true or as a valid uk-img value, then this attribute will be added. The srcset attribute will also become data-srcset. Please refer to the API Reference for more information about this method. // Render an image using the default set rules echo $image->render(); // <img src='image.jpg' alt='' srcset='{default set rules}'> // Render an image using custom set rules echo $image->render(["srcset" => "480, 1240x640"]); // <img src='image.jpg' alt='' srcset='image.480x0-srcset.jpg 480w, image.1240x640-srcset.jpg 1240w'> // Render an image using custom set rules and sizes // Also use the `markup` argument echo $image->render("<img class='image' src='{url}' alt='Image'>", [ "srcset" => "480, 1240", "sizes" => [1240 => 50], ]); // <img class='image' src='image.jpg' alt='Image' srcset='image.480x0-srcset.jpg 480w, image.1240x640-srcset.jpg 1240w' sizes='(min-width: 1240px) 50vw'> // Render an image using custom set rules and sizes // Enable uk-img echo $image->render([ "srcset" => "480, 1240", "sizes" => ["uk-child-width-1-2@m"], "uk-img" => true, ]); // <img src='image.jpg' alt='' data-uk-img data-srcset='image.480x0-srcset.jpg 480w, image.1240x640-srcset.jpg 1240w' sizes='(min-width: 960px) 50vw'> // Render an image using portrait mode // Default rule sets used: 320, 640, 768, 1024, 1366, 1600 // Portrait widths used: 320, 640, 768 // Original image is 1000px wide // Not possible to use portrait mode and custom sets or portrait widths in render() // Sizes attribute automatically added echo $image->render(["srcset" => true]); // <img src='image.jpg' alt='' srcset='image.320x569-srcset-hidpi.jpg 320w, image.640x1138-srcset-hidpi.jpg 640w, image.768x1365-srcset-hidpi.jpg 768w, image.jpg 1024w' sizes='(orientation: portrait) and (max-width: 768px) 50vw'> Configuration To configure this module, go to Modules > Configure > PageimageSrcset. Set Rules These are the default set rules that will be used when none are specified, e.g. when calling the property: $image->srcset. Each set rule should be entered on a new line, in the format {width}x{height} {inherentwidth}w|{resolution}x. Not all arguments are required - you will probably find that specifying the width is sufficient for most cases. Here's a few examples of valid set rules and the sets they generate: Set Rule Set Generated Arguments Used 320 image.320x0-srcset.jpg 320w {width} 480x540 image.480x540-srcset.jpg 480w {width}x{height} 640x480 768w image.640x480-srcset.jpg 768w {width}x{height} {inherentwidth}w 2048 2x image.2048x0-srcset.jpg 2x {width} {resolution}x How you configure your rules is dependent on the needs of the site you are developing; there are no prescriptive rules that will meet the needs of most situations. This article gives a good overview of some of the things to consider. When you save your rules, a preview of the sets generated and an equivalent method call will be displayed to the right. Invalid rules will not be used, and you will be notified of this. Portrait Mode Set Widths A comma limited list of widths to create HiDPI/Retina portrait variations for. Crop Ratio The portrait ratio that should be used to crop the image. The default of 9:16 should be fine for most circumstances as this is the standard portrait ratio of most devices. However, you can specify something different if you want. If you add a landscape ratio, it will be switched to portrait when used. Any crops in the set rules ({width}x{height}) are ignored for portrait mode variations as this ratio is used instead. UIkit Widths If your website theme uses UIkit, you can pass an array of UIkit width classes to Pageimage::sizes to be converted to sizes. The values stored here are used to do this. If you have customised the breakpoints on your theme, you should also customise them here. Please note that only 1- widths are evaluated by Pageimage::sizes, e.g. uk-width-2-3 will not work. Remove Variations If checked, the image variations generated by this module are cleared on Submit. On large sites, this may take a while. It makes sense to run this after you have made changes to the set rules. Image Suffix You will see this field when Remove Variations is checked. The value is appended to the name of the images generated by this module and is used to identify variations. You should not encounter any issues with the default suffix, but if you find that it conflicts with any other functionality on your site, you can set a custom suffix instead. Debug Mode When this is enabled, a range of information is logged to pageimage-srcset. PageimageSrcsetDebug.js is also added to the <head> of your HTML pages. This will console.log a range of information about the images and nodes using srcset on your page after a window.onresize event is triggered. This can assist you in debugging your implementation. The browser will always use the highest resolution image it has loaded or has cached. You may need to disable browser caching to determine whether your set rules are working, and it makes sense to work from a small screen size and up. If you do it the other way, the browser is going to continue to use the higher resolution image it loaded first. UIkit Features This module implements some additional features that are tailored towards UIkit being used as the front-end theme framework, but this is not required to use the module. Installation Download the zip file at Github or clone the repo into your site/modules directory. If you downloaded the zip file, extract it in your sites/modules directory. In your admin, go to Modules > Refresh, then Modules > New, then click on the Install button for this module. ProcessWire >= 3.0.123 is required to use this module.
  48. 19 points
    Hello all, sharing my new module FieldtypeImageReference. It provides a configurable input field for choosing any type of image from selectable sources. Sources can be: a predefined folder in site/templates/ and/or a page (and optionally its children) and/or the page being edited and/or any page on the site CAUTION: this module is under development and not quite yet in a production-ready state. So please test it carefully. UPDATE: the new version v2.0.0 introduces a breaking change due to renaming the module. If you have an older version already installed, you need to uninstall it and install the latest master version. Module and full description can be found on github https://github.com/gebeer/FieldtypeImageReference Install from URL: https://github.com/gebeer/FieldtypeImageReference/archive/master.zip Read on for features and use cases. Features Images can be loaded from a folder inside site/templates/ or site/assets Images in that folder can be uploaded and deleted from within the inputfield Images can be loaded from other pages defined in the field settings Images can be organized into categories. Child pages of the main 'image source page' serve as categories mages can be loaded from any page on the site From the API side, images can be manipulated like native ProcessWire images (resizing, cropping etc.), even the images from a folder Image thumbnails are loaded into inputfield by ajax on demand Source images on other pages can be edited from within this field. Markup of SVG images can be rendered inline with `echo $image->svgcontent` Image names are fully searchable through the API $pages->find('fieldname.filename=xyz.png'); $pages->find('fieldname.filename%=xy.png'); Accidental image deletion is prevented. When you want to delete an image from one of the pages that hold your site-wide images, the module searches all pages that use that image. If any page contains a reference to the image you are trying to delete, deletion will be prevented. You will get an error message with links to help you edit those pages and remove references there before you can finally delete the image. This field type can be used with marcrura's Settings Factory module to store images on settings pages, which was not possible with other image field types When to use ? If you want to let editors choose an image from a set of images that is being used site-wide. Ideal for images that are being re-used across the site (e.g. icons, but not limited to that). Other than the native ProcessWire images field, the images here are not stored per page. Only references to images that live on other pages or inside a folder are stored. This has several advantages: one central place to organize images when images change, you only have to update them in one place. All references will be updated, too. (Provided the name of the image that has changed stays the same) Installation and setup instructions can be found on github. Here's how the input field looks like in the page editor: If you like to give it a try, I'm happy to hear your comments or suggestions for improvement. Install from URL: https://github.com/gebeer/FieldtypeImageReference/archive/master.zip Eventually this will go in the module directory, too. But it needs some more testing before I submit it. So I'd really appreciate your assistance. Thanks to all who contributed their feedback and suggestions which made this module what it is now.
  49. 19 points
    This week we’ll take a quick break from core updates and have a look at a new module called UserActivity, just added to the ProcessWire ProDevTools package— https://processwire.com/blog/posts/user-activity-module/
  50. 19 points
    I never submitted the newest iteration of my own business site which already is 3 years online, promoting my webdesign/UI/UX/Frontend stuff. "Just" a Onepager, content managed by PW, usage of D3 for the circles and a completely handwritten gallery at the top. But as always, way more hours went into this as expected 😉 https://siebennull.com
×
×
  • Create New...