Jump to content

Leaderboard


Popular Content

Showing content with the highest reputation since 02/24/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. 39 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. 38 points
    Hey folks! I'm happy to finally introduce a project I've been working on for quite a while now: it's called Wireframe, and it is an output framework for ProcessWire. Note that I'm posting this in the module development area, maily because this project is still in rather early stage. I've built a couple of sites with it myself, and parts of the codebase have been powering some pretty big and complex sites for many years now, but this should still be considered a soft launch 🙂 -- Long story short, Wireframe is a module that provides the "backbone" for building sites (and apps) with ProcessWire using an MVC (or perhaps MVVM – one of those three or four letter abbreviations anyway) inspired methodology. You could say that it's an output strategy, but I prefer the term "output framework" since in my mind the word "strategy" means something less tangible. A way of doing things, rather than a tool that actually does things. Wireframe (the module) provides a basic implementation for some familiar MVC concepts, such as Controllers and a View layer – the latter of which consists of layouts, partials, and template-specific views. There's no "model" layer, since in this context ProcessWire is the model. As a module Wireframe is actually quite simple – not even nearly the biggest one I've built – but there's still quite a bit of stuff to "get", so I've put together a demo & documentation site for it at https://wireframe-framework.com/. In addition to the core module, I'm also working on a couple of site profiles based on it. My current idea is actually to keep the module very light-weight, and implement most of the "opinionated" stuff in site profiles and/or companion modules. For an example MarkupMenu (which I released a while ago) was developed as one of those "companion modules" when I needed a menu module to use on the site profiles. Currently there are two public site profiles based on Wireframe: site-wireframe-docs is the demo&docs site mentioned above, just with placeholder content replaced with placeholder content. It's not a particularly complex site, but I believe it's still a pretty nice way to dig into the Wireframe module. site-wireframe-boilerplate is a boilerplate (or starter) site profile based on the docs site. This is still very much a work in progress, but essentially I'm trying to build a flexible yet full-featured starter profile you can just grab and start building upon. There will be a proper build process for resources, it will include most of the basic features one tends to need from site to site, etc. -- Requirements and getting started: Wireframe can be installed just like any ProcessWire module. Just clone or download it to your site/modules/ directory and install. It doesn't, though, do a whole lot of stuff on itself – please check out the documentation site for a step-by-step guide on setting up the directory structure, adding the "bootstrap file", etc. You may find it easier to install one of the site profiles mentioned above, but note that this process involves the use of Composer. In the case of the site profiles you can install ProcessWire as usual and download or clone the site profile directory into your setup, but after that you should run "composer install" to get all the dependencies – including the Wireframe module – in place. Hard requirements for Wireframe are ProcessWire 3.0.112 and PHP 7.1+. The codebase is authored with current PHP versions in mind, and while running it on 7.0 may be possible, anything below that definitely won't work. A feature I added just today to the Wireframe module is that in case ProcessWire has write access to your site/templates/ directory, you can use the module settings screen to create the expected directories automatically. Currently that's all, and the module won't – for an example – create Controllers or layouts for you, so you should check out the site profiles for examples on these. (I'm probably going to include some additional helper features in the near future.) -- This project is loosely based on an earlier project called pw-mvc, i.e. the main concepts (such as Controllers and the View layer) are very similar. That being said, Wireframe is a major upgrade in terms of both functionality and architecture: namespaces and autoloader support are now baked in, the codebase requires PHP 7, Controllers are classes extending \Wireframe\Controller (instead of regular "flat" PHP files), implementation based on a module instead of a collection of drop-in files, etc. While Wireframe is indeed still in a relatively early stage (0.3.0 was launched today, in case version numbers matter) for the most part I'm happy with the way it works, and likely won't change it too drastically anytime soon – so feel free to give it a try, and if you do, please let me know how it went. I will continue building upon this project, and I am also constantly working on various side projects, such as the site profiles and a few unannounced helper modules. I should probably add that while Wireframe is not hard to use, it is more geared towards those interested in "software development" type methodology. With future updates to the module, the site profiles, and the docs I hope to lower the learning curve, but certain level of "developer focus" will remain. Although of course the optimal outcome would be if I could use this project to lure more folks towards that end of the spectrum... 🙂 -- Please let me know what you think – and thanks in advance!
  4. 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!
  5. 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!
  6. 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.
  7. 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!
  8. 33 points
    Just a quick update this week rather than a blog post. I’ve been continuing to work through the PW issues repo with Netcarver and it seems to be working well, so am planning to just keep working through it till we’ve covered everything there. Thanks to Netcarver and everyone else helping there. When we get the issues repo to the point where it’s pretty empty, I thought we’d then move on to the requests repo and PRs if possible. While there have been several commits to the core this week (mostly related to the issues repo), I don’t think there’s anything major enough to warrant a version bump, so will get another week’s worth of updates in place before bumping it to the next version number. This week I’ve been putting the finishing touches on the ProMailer module, getting that ready for release. A copy of ProMailer will be available to current subscribers of the ProDevTools package that want it. A few people have indicated that they’d also like to see it as a product independent of the ProDevTools, and actually I think that makes sense because ProMailer has become a much more comprehensive product than originally planned, and it really needs its own dedicated support board, as well as dedicated dev and agency versions. So I will make those available separately from ProDevTools. If you are a current ProDevTools subscriber and you’d like to get the first version of ProMailer when it is ready, please send me a PM here in the forums indicating that, and I will get a copy to you when it is released in beta. Even if you aren’t a ProDevTools subscriber, but would still like to be notified when ProMailer is available, please send me a PM as well. If all goes well, it should be available by this time next week or earlier. Next month we’ll hopefully be back to work on the website here as well, and develop the new modules directory.
  9. 32 points
    We recently rebuilt the Architekturführer Köln (architectural guide Cologne) as a mobile-first JavaScript web app, powered by VueJS in the frontend and ProcessWire in the backend. Concept, design and implementation by schwarzdesign! The Architekturführer Köln is a guidebook and now a web application about architectural highlights in Cologne, Germany. It contains detailled information about around 100 objects (architectural landmarks) in Cologne. The web app offers multiple ways to search through all available objects, including: An interactive live map A list of object near the user's location Filtering based on architect, district and category Favourites saved by the user The frontend is written entirely in JavaScript, with the data coming from a ProcessWire-powered API-first backend. Frontend The app is built with the Vue framework and compiled with Webpack 4. As a learning exercise and for greater customizability we opted to not use Vue CLI, and instead wrote our own Webpack config with individually defined dependencies. The site is a SPA (Single Page Application), which means all internal links are intercepted by the Vue app and the corresponding routes (pages) are generated by the framework directly in the browser, using data retrieved from the API. It's also a PWA (Progressive Web App), the main feature of which is that you can install it to your home screen on your phone and launch it from there like a regular app. It also includes a service worker which catches requests to the API and returns cached responses when the network is not available. The Architekturführer is supposed to be taken with you on a walk through the city, and will keep working even if you are completely offline. Notable mentions from the tech stack: Vue Vue Router for the SPA functionality VueX for state management and storage / caching of the data returned through the API Leaflet (with Mapbox tiles) for the interactive maps Webpack 4 for compilation of the app into a single distributable Babel for transpilation of ES6+ SASS & PostCSS with Autoprefixer as a convenience for SASS in SFCs Google Workbox to generate the service worker instead of writing lots of boilerplate code Bootstrap 4 is barely used here, but we still included it's reboot and grid system Backend The ProcessWire backend is API-only, there are no server-side rendered templates, which means the only PHP template is the one used for the API. For this API, we used a single content type (template) with a couple of pre-defined endpoints (url segments); most importantly we built entdpoints to get a list of all objects (either including the full data, or only the data necessary to show teaser tiles), as well as individual objects and taxonomies. The API template which acts as a controller contains all the necessary switches and selectors to serve the correct response in <100 lines of code. Since we wanted some flexibility regarding the format in which different fields were transmitted over the api, we wrote a function to extract arbitrary page fields from ProcessWire pages and return them as serializable standard objects. There's also a function that takes a Pageimage object, creates multiple variants in different sizes and returns an object containing their base path and an array of variants (identified by their basename and width). We use that one to generate responsive images in the frontend. Check out the code for both functions in this gist. We used native ProcessWire data wherever possible, so as to not duplicate that work in the frontend app. For example: Page names from the backend translate to URLs in the frontend in the form of route parameters for the Vue Router Page IDs from ProcessWire are included in the API responses, we use those to identify objects across the app, for example to store the user's favourites, and as render keys for object lists Taxonomies have their own API endpoints, and objects contain their taxonomies only as IDs (in the same way ProcessWire uses Page References) Finally, the raw JSON data is cached using the cache API and this handy trick by @LostKobrakai to store raw JSON strings over the cache API. Screenshots
  10. 31 points
    Just a brief update today. I’m going to give it another week before bumping the core version, as I don’t think there’s enough changes yet to warrant a version bump. For whatever reason, several of my clients have needed integration with Stripe (payments) over the last few weeks. I’d not worked with it before the last month or so, but now all of the sudden am working with it a lot, because that's what my clients have asked for. I’ve found myself working on four different Stripe integrations on existing PW sites, both Stripe Elements and Stripe Checkout. None of these are for sites that have an actual “store” where they would need a cart, but rather just “pay for your reservation”, “buy this book”, “buy this song”, and “make a donation of $10”, “make a recurring donation”, type things. After doing a few of these, I thought it would make a lot of sense to have this built into FormBuilder, which would save us time on this stuff. So this week I built Stripe support into FormBuilder (using the Stripe Elements API). It’s already fully functional, so I will be releasing a new version of FormBuilder with this capability quite soon. To add a Stripe payment input to your form you just add a new field of type “Stripe payment for FormBuilder”, and then it asks you for some info about it (like amount to charge) and then your form works as a payment processor. Stripe has a clever way of making this all work, so that the user never leaves your site, but your site (and FormBuilder) never sees credit card numbers or anything like that, so it’s secure and you don’t have to consider things like PCI compliance. I've also got some other unrelated updates for FormBuilder that I'll be covering soon as well. Have a great weekend!
  11. 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!
  12. 29 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
  13. 28 points
    --- Please use RockFinder3 ---
  14. 28 points
    Good Morning ProcessWIre Community, PW Review is a website that has a lofty goal of covering ProcessWire on a regular basis. That may seem like an impossible task, however I believe I'm up to the challenge. First, this is a strictly non-commercial endeavor and a project I believe in. This is not Wired Magazine or anything like that. I'm not a serious programmer or pretending to be one. My goal is to cover the modules (commercial and non-commercial) that may not get that much press or attention. I will also be covering modules that maybe you haven't heard about in awhile. There will also be screenshots, information and videos regarding the Commercial modules. I've been with this community since 2012 and have benefitted tremendously from ProcessWire. So this is my way of giving back, which is important to me. It's my hope that over time that what I'm doing myself will get some community participation. One person that I've had nothing but positive support from is Ryan Cramer. That's enough to keep me going at this for some time. That's it. Have a nice day and I hope you find something to like with PW Review. If not, please send me an email. I'm a great listener. Best Regards, Charles
  15. 27 points
    SnipWire - Snipcart integration for ProcessWire Snipcart is a powerful 3rd party, developer-first HTML/JavaScript shopping cart platform. SnipWire is the missing link between Snipcart and the content management framework ProcessWire. With SnipWire, you can quickly turn any ProcessWire site into a Snipcart online shop. The SnipWire plugin helps you to get your store up and running in no time. Detailed knowledge of the Snipcart system is not required. SnipWire is free and open source licensed under Mozilla Public License 2.0! A lot of work and effort has gone into development. It would be nice if you could donate an amount to support further development: Status update links (inside this thread) for SnipWire development 2020-04-06 -- SnipWire 0.8.6 (beta) released! Adds support for Snipcart subscriptions and also fixes some problems 2020-03-21 -- SnipWire 0.8.5 (beta) released! Improves SnipWires webhooks interface and provides some other fixes and additions 2020-03-03 -- SnipWire 0.8.4 (beta) released! Improves compatibility for Windows based Systems. 2020-03-01 -- SnipWire 0.8.3 (beta) released! The installation and uninstallation process has been heavily revised. 2020-02-08 -- SnipWire 0.8.2 (beta) released! Added a feature to change the cart and catalogue currency by GET, POST or SESSION param 2020-02-03 -- SnipWire 0.8.1 (beta) released! All custom classes moved into their own namespaces. 2020-02-01 -- SnipWire is now available via ProcessWire's module directory! 2020-01-30 -- SnipWire 0.8.0 (beta) first public release! (module just submitted to the PW modules directory) 2020-01-28 -- added Custom Order Fields feature (first SnipWire release version is near!) 2020-01-21 -- Snipcart v3 - when will the new cart system be implemented? 2020-01-19 -- integrated taxes provider finished (+ very flexible shipping taxes handling) 2020-01-14 -- new date range picker, discount editor, order notifiactions, order statuses, and more ... 2019-11-15 -- orders filter, order details, download + resend invoices, refunds 2019-10-18 -- list filters, REST API improvements, new docs platform, and more ... 2019-08-08 -- dashboard interface, currency selector, managing Orders, Customers and Products, Added a WireTabs, refinded caching behavior 2019-06-15 -- taxes provider, shop templates update, multiCURL implementation, and more ... 2019-06-02 -- FieldtypeSnipWireTaxSelector 2019-05-25 -- SnipWire will be free and open source Plugin Key Features Fast and simple store setup Full integration of the Snipcart dashboard into the ProcessWire backend (no need to leave the ProcessWire admin area) Browse and manage orders, customers, discounts, abandoned carts, and more Multi currency support Custom order and cart fields Process refunds and send customer notifications from within the ProcessWire backend Process Abandoned Carts + sending messages to customers from within the ProcessWire backend Complete Snipcart webhooks integration (all events are hookable via ProcessWire hooks) Integrated taxes provider (which is more flexible then Snipcart own provider) Useful Links SnipWire in PW modules directory SnipWire Docs (please note that the documentation is a work in progress) SnipWire @GitHub (feature requests and suggestions for improvement are welcome - I also accept pull requests) Snipcart Website ---- INITIAL POST FROM 2019-05-25 ----
  16. 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.
  17. 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.
  18. 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!
  19. 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!
  20. 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/
  21. 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.
  22. 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!
  23. 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/
  24. 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/
  25. 25 points
    Like in recent weeks past, the primary focus this week in core development was working through the queue of older issue reports. Relative to 3.0.128, ProcessWire 3.0.129 contains 17 commits over 1 week, most of which are focused on resolving and closing out older issue reports. However, there have also been a few very useful additions too, and I’m going to cover them in a blog post next week. In terms of our issues repository, we are now down to 65 open issues and 777 closed (the closed number is a total over the life of this repo). If we subtract issues that are tagged as being fixed, not a bug or ready to close, we’re around 55 open issue reports (give or take a couple depending on when we check). Which is to say, there’s a lot of great progress here. And many of the remaining issues are minor things that might only affect one person, though still important nonetheless. Thanks to everyone that’s helping figure things out (such as Toutouwai, Matjazpotocnik, Netcarver and others), your help is greatly appreciated. Just now I also released a new version of ProMailer (v7) which accommodates many of the recent feature requests and fixes a few minor issues as well. There has been a lot of enthusiasm for ProMailer, pleasantly more than expected—thanks for your interest and support. Here’s the changelog for the latest version (v7) below. There is also more good stuff in the works for v8 as well. Changed subscribers list interface to use tabs. Added ability to remove all subscribers from a list. Added ability to remove filtered subscribers from a list (matching find query). Added ability to import subscribers from another list (creating a merged list). Added support for single, multi-select and checkbox custom fields. Added support for PW 3.0.129+ core WireMail blacklist (more details in next week’s blog post). Added several new options for custom fields (see new custom fields reference). Added documentation for conditional placeholders. Split the rather large ProcessProMailer.module into separate files/classes. Fix some display issues in AdminThemeDefault and AdminThemeReno. Various minor bug fixes and other minor tweaks, additions and improvements. Thanks for reading this quick update and hope that you have a great weekend.
  26. 25 points
    Relative to the last dev branch version (3.0.127) version 3.0.128 contains 40 commits, mostly specific to resolving older GitHub issue reports. If you are interested, here is the commit log: https://github.com/processwire/processwire/commits/dev — version 3.0.128 covers commits between March 2nd and today. We're down to about 77 remaining issue reports, mostly older stuff that didn't get covered in the past for one reason or another, but we're making good progress. Similar updates will be continuing next week, and I've also got several ProMailer updates coming out next week too. Hope you all have a great weekend!
  27. 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/
  28. 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/
  29. 24 points
    This week I’m happy to report we now have WEBP support in ProcessWire thanks to a GitHub pull request from Horst Nogajski. This enables you to have highly optimized image output in PW and I think you’ll really like the difference it makes. Read on for all the details… https://processwire.com/blog/posts/webp-images-in-pw/
  30. 23 points
    After forgetting the class name of the wonderful AdminPageFieldEditLinks module for what feels like the 100th time I decided I needed to give my failing memory a helping hand... Autocomplete Module Class Name Provides class name autocomplete suggestions for the "Add Module From Directory" and "Add Module From URL" fields at Modules > New. Requires ProcessWire >= v3.0.16. Screencast Installation Install the Autocomplete Module Class Name module. Configuration Add Module From Directory Choose the type of autocomplete suggestions list: "Module class names from directory" or "Custom list of module class names". The latter could be useful if you regularly install some modules and would prefer a shorter list of autocomplete suggestions. The list of class names in the modules directory is generated when the Autocomplete Module Class Name module is installed. It doesn't update automatically (because the retrieval of the class names is quite slow), but you can use the button underneath when you want to retrieve an updated list of class names from the directory. Add Module From URL If you want to see autocomplete suggestions for the "Add Module From URL" field then enter them in the following format: [autocomplete suggestion] > [module ZIP url] Example: RepeaterImages > https://github.com/Toutouwai/RepeaterImages/archive/master.zip Awesomplete options The "fuzzy search" option uses custom filter and item functions for Awesomplete so that the characters you type just have to exist in the autocomplete suggestion item and occur after preceding matches but do not need to be contiguous. Uncheck this option if you prefer the standard Awesomplete matching. Custom settings for Awesomplete can be entered in the "Awesomplete options" field if needed. See the Awesomplete documentation for more information. https://github.com/Toutouwai/AutocompleteModuleClassName https://modules.processwire.com/modules/autocomplete-module-class-name/
  31. 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)
  32. 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/
  33. 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!
  34. 23 points
    File Info A textformatter module for ProcessWire. The module can add information to local Pagefile links in two ways: As extra markup before, within or after the link As data attributes on the link (handy if you want to use a Javascript tooltip library, for instance) Screenshots Module config Example of output Installation Install the File Info module. Add the textformatter to one or more CKEditor fields. Configuration Add markup action (and general) Select "Add markup to links" Select the Pagefile attributes that will be retrieved. The attribute "filesizeStrCustom" is similar to the core "filesizeStr" attribute but allows for setting a custom number of decimal places. If you select the "modified" or "created" attributes then you can define a date format for the value. Enter a class string to add to the links if needed. Define the markup that will be added to the links. Surround Pagefile attribute names in {brackets}. Attributes must be selected in the "Pagefile attributes" section in order to be available in the added markup. If you want include a space character at the start or end of the markup then you'll need >= PW 3.0.128. Select where the markup should be added: prepended or appended within the link, before the link, or after the link. Add data attributes action Select "Add data attributes to links" Select the Pagefile attributes that will be retrieved. These attributes will be added to the file links as data attributes. Attributes with camelcase names will be converted to data attribute names that are all lowercase, i.e. filesizeStrCustom becomes data-filesizestrcustom. Hook If you want to customise or add to the attributes that are retrieved from the Pagefile you can hook TextformatterFileInfo::getFileAttributes(). For example: $wire->addHookAfter('TextformatterFileInfo::getFileAttributes', function(HookEvent $event) { $pagefile = $event->arguments(0); $page = $event->arguments(1); $field = $event->arguments(2); $attributes = $event->return; // Add a new attribute $attributes['sizeNote'] = $pagefile->filesize > 10000000 ? 'This file is pretty big' : 'This file is not so big'; $event->return = $attributes; }); https://github.com/Toutouwai/TextformatterFileInfo https://modules.processwire.com/modules/textformatter-file-info/
  35. 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.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
  36. 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!
  37. 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!
  38. 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!
  39. 21 points
    Mystique Module for ProcessWire CMS/CMF Github repo : https://github.com/trk/Mystique Mystique module allow you to create dynamic fields and store dynamic fields data on database by using a config file. Requirements ProcessWire 3.0 or newer PHP 7.0 or newer FieldtypeMystique InputfieldMystique Installation Install the module from the modules directory: Via Composer: composer require trk/mystique Via git clone: cd your-processwire-project-folder/ cd site/modules/ git clone https://github.com/trk/Mystique.git Module in live reaction with your Mystique config file This mean if you remove a field from your config file, field will be removed from edit screen. As you see on youtube video. Using Mystique with your module or use different configs path, autoload need to be true for modules Default configs path is site/templates/configs/, and your config file name need to start with Mystique. and need to end with .php extension. // Add your custom path inside ready or init function, didn't tested outside Mystique::add('your-configs-path'); All config files need to return a php array like examples. Same as ProcessWire Inputfield Api, only difference is set and showIf usage <?php namespace ProcessWire; /** * Resource : testing-mystique */ return [ 'title' => __('Testing Mystique'), 'fields' => [ 'text_field' => [ 'label' => __('You can use short named types'), 'description' => __('In file showIf working like example'), 'notes' => __('Also you can use $input->set() method'), 'type' => 'text', 'showIf' => [ 'another_text' => "=''" ], 'set' => [ 'showCount' => InputfieldText::showCountChars, 'maxlength' => 255 ], 'attr' => [ 'attr-foo' => 'bar', 'attr-bar' => 'foo' ] ], 'another_text' => [ 'label' => __('Another text field (default type is text)') ] ] ]; Example: site/templates/configs/Mystique.seo-fields.php <?php namespace ProcessWire; /** * Resource : seo-fields */ return [ 'title' => __('Seo fields'), 'fields' => [ 'window_title' => [ 'label' => __('Window title'), 'type' => Mystique::TEXT, // or InputfieldText 'useLanguages' => true, 'attr' => [ 'placeholder' => __('Enter a window title') ] ], 'navigation_title' => [ 'label' => __('Navigation title'), 'type' => Mystique::TEXT, // or InputfieldText 'useLanguages' => true, 'showIf' => [ 'window_title' => "!=''" ], 'attr' => [ 'placeholder' => __('Enter a navigation title') ] ], 'description' => [ 'label' => __('Description for search engines'), 'type' => Mystique::TEXTAREA, 'useLanguages' => true ], 'page_tpye' => [ 'label' => __('Type'), 'type' => Mystique::SELECT, 'options' => [ 'basic' => __('Basic page'), 'gallery' => __('Gallery'), 'blog' => __('Blog') ] ], 'show_on_nav' => [ 'label' => __('Display this page on navigation'), 'type' => Mystique::CHECKBOX ] ] ]; Searching data on Mystique field is limited. Because, Mystique saving data to database in json format. When you make search for Mystique field, operator not important. Operator will be changed with %= operator. Search example $navigationPages = pages()->find('my_mystique_field.show_on_nav=1'); $navigationPages = pages()->find('my_mystique_field.page_tpye=gallery');
  40. 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.
  41. 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
  42. 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!
  43. 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:
  44. 21 points
    I've been working remotely this week and haven't had a good place to sit or very particularly reliable internet, so I've put my attention towards things that I could make progress with in small and inconsistent chunks of time. As a result, it worked well to focus on resolving issue reports this week. In addition to updates mentioned last week, 3.0.134 resolves 18+ issues and contains roughly 25 commits relative to 3.0.133. Since most of the commits since last week are focused on resolving issue reports, I don't have much to write here beyond what's in the commit log, but do want to thank everyone for their reports and hope that you have a great weekend!
  45. 21 points
    For the last couple months, I've been working on a module that does exactly this. It's not a site profile, it's not a theme. I don't know what term would accurately describe it, but its a ProcessWire module that's an opinionated, update-able starting point, oriented towards developers, and brings in a bunch of boilerplate like: an installer that will create fields, templates, pages, hannacodes; it runs through a thorough check before doing anything that may result in an error (module installation errors are aggravating; i'm testing it very thoroughly) the installer will rename your 'templates' dir to something temporary, and then copy a starting 'templates' folder that's prepped for the module (you should only do this on dev) if you try to add it to an existing site with a bunch of data, it will work as long as there are not collisions in some fields and templates; if there are, the module won't install and tell you what you need to change around to allow it to install establishes some new $config variables ('env', 'disableTemplateCache', a few others) built with UIkit 3 in mind, but not hardcoded to it a menu builder based on Repeaters with its own Process Module has its own seo+og+twitter+sitemap metadata fields and uses Profields FieldGroup since that is the best module for handling such a grouping has it's own templates inside the module which can be used, or overridden in /site/templates/; this includes blog sitemap.xml maintenance search in addition to template files being able to be overridden, partial files can be overridden too! some module configuration fields (site_name, maintenance, etc.) the module has documentation and other statistics built into it for easy reference takes full advantage of setting() and markup regions; applies attributes like UIkit 'uk-grid' and other data attributes without "touching" the html; keeps the module flexible and easily allows you to rip out UIkit and swap it for another CSS framework (Bootstrap 4); you'll never need to touch edit the _main.php file because of how regions has been set up has it's on RepeaterMatrix fields with boilerplate matrix-types (headline, text, slideshow, etc. etc.); if the user makes new custom matrix types and a later version of my module brings in a new matrix-type, it will update the RepeaterMatrix field and merge it correctly (tricky to do!); the matrix types use many of the same base fields, thereby allowing you to switch from one matrix type to another without data being destroyed (this was only possible as of 2 weeks ago with the new matrix update) to avoid creating a bunch of 1-off fields for the matrix field for block configuration, it uses the new and quite amazing Mystique fieldtype in a unique way (this was tricky to do); this module is critical to establishing a page-builder that is clean (this was only possible as of April 2019 since the module is brand new) brings in PW's debugger to the frontend (brings in jQuery UI); can be disabled all field and template names don't use any prefixes; this would allow you to dump the module one day in the future if you don't like it, without having a bunch of field and template names that sound too specific to the module Laravel Mix based asset compiler pre-configured with UIkit works with FormBuilder and other modules I often use; works and may require certain Pro modules; eliminates the need for any modules that do blogging, menu building, sitemap, maintenance or seo since this module does it using native ProcessWire login/logout/account/register templates it may include a 'collection' generator for common things like events, products, people, properties, along with some templates for those. don't like the templates my module provides? then just create your own template file in /site/templates/the-template-file.php which will override /site/modules/my-module-name/templates/the-template-file.php Right now I just started building a few sites with it (spent the last 2 months developing it) which will hammer it out further. I will release it late summer, early fall.
  46. 20 points
    MarkupMenu is a markup module for generating menu trees. When provided a root page as a starting point, it generates a navigation tree (by default as a HTML "<ul>" element wrapped by a "<nav>" element) from that point onwards. If you've also provided it with current (active) page, the menu will be rendered accordingly, with current item highlighted and items rendered up to that item and its children (unless you disable the "collapsed" option, in which case the full page tree will be rendered instead). Modules directory: https://modules.processwire.com/modules/markup-menu/ GitHub repository: https://github.com/teppokoivula/MarkupMenu Usage As a markup module, MarkupMenu is intended for front-end use, but you can of course use it in a module as well. Typically you'll only need the render() method, which takes an array of options as its only argument: echo $modules->get('MarkupMenu')->render([ 'root_page' => $pages->get(1), 'current_page' => $page, ]); Note: if you omit root_page, site root page is used by default. If you omit current_page, the menu will be rendered, but current (active) page won't be highlighted etc. A slightly more complex example, based on what I'm using on one of my own sites to render a (single-level) top menu: echo $modules->get('MarkupMenu')->render([ 'current_page' => $page, 'templates' => [ 'nav' => '<nav class="{classes} menu--{menu_class_modifier}" aria-label="{aria_label}">%s</nav>', 'item_current' => '<a class="menu__item menu__item--current" href="{item.url}" tabindex="0" aria-label="Current page: {item.title}">{item.title}</a>', ], 'placeholders' => [ 'menu_class_modifier' => 'top', 'aria_label' => 'Main navigation', ], 'include' => [ 'root_page' => true, ], 'exclude' => [ 'level_greater_than' => 1, ], ]); Note: some things you see above may not be entirely sensible, such as the use of {menu_class_modifier} and {aria_label} placeholders. On the actual site the "nav" template is defined in site config, so I can define just these parts on a case-by-case basis while actual nav markup is maintained in one place. Please check out the README file for available render options. I'd very much prefer not to keep this list up to date in multiple places. Basically there are settings for defining "templates" for different parts of the menu (list, item, etc.), include array for defining rules for including in the menu and exclude array for the opposite effect, classes and placeholders arrays for overriding default classes and injecting custom placeholders, etc. 🙂 MarkupMenu vs. MarkupSimpleNavigation TL;DR: this is another take on the same concept. There are many similarities, but also some differences – especially when it comes to the supported options and syntax. If you're currently using MarkupSimpleNavigation then there's probably no reason to switch over. I'd be surprised if someone didn't draw lines between this module and Soma's awesome MarkupSimpleNavigation. Simply put I've been using MSN (...) for years, and it's been great – but there are some issues with it, particularly in the markup generation area, and it also does some things in a way that doesn't quite work for me – the xtemplates thing being one of these. In some ways less about features, and more about style, I guess 🙂 Anyhow, in MarkupMenu I've tried to correct those little hiccups, modernise the default markup, and allow for more flexibility with placeholder variables and additional / different options. MarkupMenu was built for ProcessWire 3.0.112+ and with PHP 7.1+ in mind, it's installable with Composer, and I have a few additional ideas (such as conditional placeholders) still on my todo list. One more small(ish) difference is that MarkupMenu supports overriding default options via $config->MarkupMenu. I find myself redefining the default markup for every site, which until now meant that each site had a wrapper function for MarkupSimpleNavigation (to avoid code / config repetition), and this way I've been able to leave that out 🙂 Requirements ProcessWire >= 3.0.112 PHP >= 7.1.0 If you're working on an earlier version of ProcessWire or PHP, use MarkupSimpleNavigation instead.
  47. 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.
  48. 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.
  49. 20 points
    Repeater Images Adds options to modify Repeater fields to make them convenient for "page-per-image" usage. Using a page-per-image approach allows for additional fields to be associated with each image, to record things such as photographer, date, license, links, etc. When Repeater Images is enabled for a Repeater field the module changes the appearance of the Repeater inputfield to be similar (but not identical) to an Images field. The collapsed view shows a thumbnail for each Repeater item, and items can be expanded for field editing. Screencast Installation Install the Repeater Images module. Setup Create an image field to use in the Repeater field. Recommended settings for the image field are "Maximum files allowed" set to 1 and "Formatted value" set to "Single item (null if empty)". Create a Repeater field. Add the image field to the Repeater. If you want additional fields in the Repeater create and add these also. Repeater Images configuration Tick the "Activate Repeater Images for this Repeater field" checkbox. In the "Image field within Repeater" dropdown select the single image field. You must save the Repeater field settings to see any newly added Image fields in the dropdown. Adjust the image thumbnail height if you want (unlike the core Images field there is no slider to change thumbnail height within Page Edit). Note: the depth option for Repeater fields is not compatible with the Repeater Images module. Image uploads feature There is a checkbox to activate image uploads. This feature allows users to quickly and easily add images to the Repeater Images field by uploading them to an adjacent "upload" field. To use this feature you must add the image field selected in the Repeater Images config to the template of the page containing the Repeater Images field - immediately above or below the Repeater Images field would be a good position. It's recommended to set the label for this field in template context to "Upload images" or similar, and set the visibility of the field to "Closed" so that it takes up less room when it's not being used. Note that when you drag images to a closed Images field it will automatically open. You don't need to worry about the "Maximum files allowed" setting because the Repeater Images module overrides this for the upload field. New Repeater items will be created from the images uploaded to the upload field when the page is saved. The user can add descriptions and tags to the images while they are still in the upload field and these will be retained in the Repeater items. Images are automatically deleted from the upload field when the page is saved. Tips The "Use accordion mode?" option in the Repeater field settings is useful for keeping the inputfield compact, with only one image item open for editing at a time. The "Repeater item labels" setting determines what is shown in the thumbnail overlay on hover. Example for an image field named "image": {image.basename} ({image.width}x{image.height}) https://github.com/Toutouwai/RepeaterImages https://modules.processwire.com/modules/repeater-images/
  50. 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!
×
×
  • Create New...