Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation since 09/28/2021 in all areas

  1. Something I've wanted in ProcessWire for a long time is full version support for pages. It's one of those things I've been trying to build since ProcessWire 1.0, but never totally got there. Versioning text and number fields (and similar types) is straightforward. But field types in ProcessWire are plugin modules, making any type of data storage possible. That just doesn't mix well with being version friendly, particularly when getting into repeaters and other complex types. ProDrafts got close, but full version support was dropped from it before the first version was released. It had just become too much to manage, and I wanted it to focus just on doing drafts, and doing them as well as we could. ProDrafts supports repeaters too, though nested repeaters became too complex to officially support, so there are still some inherent limitations. I tried again to get full version support with a module called PageSnapshots developed a couple years ago, and spent weeks developing it. But by the time I got it fully working with all the core Fieldtypes (including repeaters), I wasn't happy with it. It was functional but had become too complex for comfort. So it was never released. This happens with about 1/2 of the code I write – it gets thrown out or rewritten. It's part of the process. What I learned from all this is that it's not practical for any single module to effectively support versions across all Fieldtypes in ProcessWire. Instead, the Fieldtypes themselves have to manage versions of their own data, at least in the more complicated cases (repeaters, ProFields and such). The storage systems behind Fieldtypes are sometimes unique to the type, and version management needs to stay internal [to the Fieldtype] in those cases. Repeaters are a good example, as they literally use other pages as storage, in addition to the field_* tables. For the above reasons, I've been working on a core interface for Fieldtypes to provide their own version support. Alongside that, I've been working on something that vaguely resembles the Snapshots module's API. But rather than trying to manage versions for page field data itself, it delegates to the Fieldtypes when appropriate. If a Fieldtype implements the version interface, it calls upon that Fieldtype to save, get, restore and delete versions of its own data. It breaks the complexity down into smaller chunks, to the point where it's no longer "complexity", and thus reasonable and manageable. It's a work in progress and I've not committed any of it to the core yet, but some of this is functional already. So far it's going more smoothly than past attempts due to the different approach. My hope is to have core version support so that modules like ProDrafts and others can eventually use that API to handle their versioning needs rather than trying to do it all themselves. I also hope this will enable us to effectively version the repeater types (including nested). I'm not there yet, but it's in sight. If it all works out as intended, the plan is to have a page versions API, as part of the $pages API. I'll follow up more as work continues. Thanks for reading and have a great weekend!
    41 points
  2. This week I've been working on something a little different: developing a new site profile in ProcessWire. Actually, I should probably call it an application profile rather than a site profile, as it's not a website profile. Instead it's a profile for an invoicing application in ProcessWire. Though you would install and run it on a web server, but it would be an independent application rather than part of a website... perhaps something you run in a subdirectory, subdomain, or even localhost. This is something I've been wanting to build for awhile—ever since the invoice service I use raised their rates beyond my budget. So I thought I'd build a replacement that I could use, as well as share for others that might have a similar need. I think it might also be a pretty decent PW profile example in general, too. I'd originally considered building it as a Process module but decided not to for a few reasons. Though the biggest one is that a site profile enables the greatest potential for customization and expansion according to each person's needs. Since you can expand upon it by adding your own fields and templates, or editing existing ones, most can really tailor it to their own needs a lot more easily than they could if it were a Process module. Likewise, since the actual invoices (and invoice emails) are rendered from front-end pages, you can customize the look and feel of them to match your brand very easily. (This is something I always wished I could do with the invoice service I've been using previously) This invoice profile requires nothing other than the ProcessWire core. It has no 3rd party or Pro module dependencies. I've got it largely functional at this stage, though will be putting a couple more weeks work into it before releasing it. I'd like to build in the option for clients to pay an invoice with a credit card (via Stripe) for instance. Below are a few screenshots of the work in progress. First is the page-list which shows the current invoices in the system and their status. (click image to view larger) As you can see, there are also pages for Clients and Settings. The client pages contain all the information about each client that invoices can be created for. The Settings page is where you can edit your own company information, logo and billing preferences. Next is the invoice editor. Here we have a repeater for each line item in the invoice. We also have a repeater for payments. All of the totals add up automatically as you type (Javascript added via hooks). They are also calculated automatically at the server side, so that everything stays consistent whether working with the API or in the page editor. (click image to view larger) At the bottom of the invoice editor you'll see a collapsed input for "Invoice action". This is where you can select actions to apply to the invoice. The two we currently have are "Email invoice to client" and "Email invoice to another address". Next up is what we see when viewing the invoice on the front-end. This is just the output of a template file but it is optimized for printing, saving to PDF and sending through email. I've kept it intentionally simple but of course the logo would be replaced with your own and all markup/styles are fully under your control. (click image to view larger) What I plan to add next are payment options, enabling a client to pay by credit card right from the invoice URL or email. What do you think, is this type of PW profile useful to you or someone you know? I've initially built it towards my own client invoicing needs, but I'm curious what other features you would like it to have? Or do you think it's better to keep it simple so that people can more easily take it in different directions? Thanks for your feedback. Have a great weekend!
    40 points
  3. This new main/master version has more than 220 commits, resolves more than 80 issues, adds numerous new features, performance improvements and optimizations, and consumes HALF the disk space of our previous release— https://processwire.com/blog/posts/pw-3.0.200/ I've just merged the dev branch to the master branch so this new version is available now. I will add the 3.0.200 tag (which should trigger packagist and others) over this weekend or Monday.
    40 points
  4. Happy New Year! Hope that you all have a great end to 2021 and start of 2022. No major core updates to report this week, just a few minor issue fixing commits, so no version bump today. The dev branch is getting close to 100 commits (and at 7 versions) above the master branch, and with even more improvements/fixes/optimizations than that. So we may try to get a new master/main version out in early 2022, as I'd really like to get more master versions out in 2022 than we did in 2021. Some portion of our audience does not use the dev branch where most of the activity happens, and so it might be nice to share more of that activity on the master/main branch. That's one of many things I'm thinking about as the New Year approaches and am certain 2022 is going to be a great year for ProcessWire and the community. Hope that you have a great weekend and Happy New Year!
    38 points
  5. 140 commits, 55 resolved issues, dozens of new features, eight contributors, and five new pull requests make yet another great new version of ProcessWire. This week I’m happy to announce another new main/master branch version of ProcessWire, version 3.0.210. Like most main release versions, there is a lot here. This post covers some of the most notable additions and improvements— https://processwire.com/blog/posts/pw-3.0.210/
    35 points
  6. I did my first talk ever yesterday @ PHP Meetup Vienna!! Once more everything was a lot more work than I first thought, but I'm quite proud of the result 😎 What do you think? Did I forget something important? It was really hard to put 10 years into one hour... The recording was not planned at first, but I thought I'd just give it a try and everything worked quite well 🥳 If you like what you see please share it with others so that ProcessWire gets the attention that it deserves 🙂 Special thanks to @gebeer for showing me ProcessWire in 2013 🤗
    35 points
  7. Merry Christmas, Happy Hanukkah, and Happy Holidays! The plan is to release a newyear main/master core version. I'm not currently aware of any showstopper issues specific to the current dev branch, but if you are aware of any, please report them in the GitHub issues repo (or reply to an existing issue report), as the dev branch will soon become the master branch. There's not yet a hard deadline, so whether it's end of December or the 1st week of January, we'll have a new release out shortly. The dev branch is very stable right now (likely more stable than the master branch) so it's a good time to get a new version out. I've also been holding off on several larger updates to the dev branch in anticipation of this merge to master and new release, so I've likewise been holding back on any commits that would add more to test. Once we get a new main/master release out there, the dev branch will then get InputfieldTinyMCE merged into it and there will be some JS library updates, along with other larger updates, and those that would need dev branch testing. Thanks for reading and I hope that you have a great holiday weekend and week ahead
    35 points
  8. PAGEGRID – A visual page builder for ProcessWire. Design fully responsive websites (or parts of them) without writing any code. Use ProcessWire's native templates (and fields) to create your own blocks. Rearrange and resize items in a visual way and use inline or modal editing to quickly edit the content of your website. (The demo uses AdminThemeCanvas but it will work just fine with the core Uikit Theme) Try PAGEGRID for free Checkout the online demo. PAGEGRID is not free software. However, you can try PAGEGRID on your local machine or on a test server as long as you need to make sure it is the right tool for your next project. … and when you’re convinced, buy your license. Get it here Download from GitHub Download from Module Directory Requirements ProcessWire 3.0.210 or greater Currently the dev version of ProcessWire 3.0.216 or greater has some bugs with PG. (I have a working fix, and it will be comming soon). Please use the latest master release of ProcessWire for now) Installation Go to “Modules > Site > Add New“ in your admin Paste the Module Class Name "FieldtypePageGrid" into the field “Add Module From Directory“ Click “Get Module Info“ On the overview, click “Download And Install“ On the following screen, click “Install Now“ More install options Module install guide Site profile install guide Get up and running Quick start Create your own blocks or install the PageGridBlocks Module (installs premade templates and fields for PAGEGRID blocks). What's PAGEGRID? page-grid.com – Get to know PAGEGRID. Documentation – Read the official documentation. Issues – Report bugs and other problems. Forum – Whenever you get stuck, don't hesitate to reach out for questions and support. Why I build it ProcessWire is super flexible in itself and lets me build whatever I want. But building a custom website can be a lot of work. For some projects, I've ended up using a lot of templates and fields. To make my pages more flexible, I sometimes build my own little page builder based on the RepeaterMatrix or PageTable module. While these page builders were great for the specific site I was building them for, they were never flexible enough to be used for new projects, so I ended up customizing them frequently. The more complex they became, the harder it became to use them for my clients. After playing around with some WYSIWYG page builder tools, I realized that while they can save me a lot of time, they can also be very limiting or have expensive subscriptions and somehow tie you to their ecosystem. So I decided to build my own page builder based on the most flexible CMS I knew. Concept This fieldtype Renders block templates and adds drag and drop functionality in admin, as well as enable inline editing for text, and file fields. It also let's you manipulate CSS in a visual way to design fully responsive websites (or parts of them) without writing code. The fieldtype comes with an optional style panel to manipulate CSS properties directly on the page. You can customize the panel or disable it completely from the module settings (and just use a CSS file that you include in your template). The data to style the items is stored directly on the item using PW's meta data (no extra fields are created). Don't want to give your client all that power? Use ProcessWire’s powerful permission system to control what your clients can edit. You can then also grant access individually to the style panel, resize or drag functionality using ProcessWire's build in permission system. Features Blocks are just pages Blocks are defined by native PW templates and fields Manipulate CSS grid or flexbox based layouts in a visual way to design fully responsive websites (or parts of them) Encapsulated frontend code (PAGEGRID renders the template of your frontend inside an iframe in the backend) Design and editing features can be disabled for certain roles (using ProcessWire's build in permission system) Inline editing of text, textarea, TinyMCE (supports latest version), ckeditor and file fields Simply drag and resize to manipulate grid items directly inside the backend Manipulate grid columns and rows directly on the page (use any number of columns you want) All style manipulations are saved as JSON and used to generate dynamic styles that you render in your main template (no inline styles) Nested groups/grids (child pages of nested blocks are created under group parent) The style panel supports adding custom classes and assigning styles to them. These classes can be used globally on all pages (a css class is also a page) The style panel supports selecting html tags to style tags globally across the whole site Global blocks work with page reference field (changes on one page, changes all blocks on all pages) Manual and auto placement of grid items blocks and nested blocks can be cloned Redo/undo and copy/paste shortcuts Editing block items in modal sidebar immediately updates frontend (Ajax Save). Define custom icons for your blocks via native template settings (template -> advanced -> icon) Automatic page save (Changes are getting saved via ajax, no need to click the save button) NEW: Option to automatically load lazysizes lazyloader (V 0.1.0) Changelog V 0.1.0: Feature: Option to automatically load lazysizes lazyloader (Module Settings > Plugins). V 0.1.5: Fixed bug: Tabs not working when editing items via modal panel. V 0.1.6: Fixed bug: Setting height in VH unit was not working. V 0.1.7: Feature: Option to hide save button (and use automatic ajax save ) if there are no other fields than PAGEGRID on the content tab (Module Settings > Interface). V 0.2.0: Fixed bug: Custom block wrapper element <p> was not working with inline editor. V 0.2.0: Fixed bug: Inline editor would sometimes not save after clicking cancel and then edit item again. V 0.2.0: Feature: Now it's possible to add classes to elements inside richt text fields via style panel. V 0.2.0: Fixed bug: Inline editor was not working after first item was added to the page (needed reloading the page). V 0.2.1: Feature: Updated PageGridBlocks Module: Using TinyMCE as the default editor. V 0.2.1: Feature: Updated PageGridBlocks Module: Group/container wrapper element can now be changed to <div>, <section>, <article>, <header>, <footer>, <nav>. Thanks to everyone who helped me improve my coding skills and for the support of this great community! Special thanks to @diogo for the valuable feedback and @ryan for this great CMS and his support for the PageFrontEdit module!
    34 points
  9. This is one of those rare weeks where I've got a lot of projects in progress, but all are in the middle, none are at a convenient Friday conclusion for this weekly update. In progress are some core updates, Pro module updates, other module updates, and a client project that's keeping me busy. So I don't have anything new or interesting to report this week, but I like to still check in and say hello, and let you know I'm not running low on coffee or anything like that. 🙂 I hope that you have a great weekend!
    34 points
  10. ProcessWire 3.0.197 resolves 9 issue reports and adds 3 feature requests. For details in resolved issues and feature requests, be sure to see the commit log. I'll cover a couple of my favorite feature requests that have been added this week: New $files->getCSV() method This comes by way of feature request #400 via @bernhard to add something to abstract away the redundant details of reading a CSV file into one simple method call. This simplifies the reading of a CSV file by abstracting file-open, get-header, get-rows and file-close operations into a single method call, where all those operations are handled internally. All you have to do is keep calling the $files->getCSV($filename) method until it returns false. This method will also skip over blank rows by default, unlike PHP’s fgetcsv() which will return a 1-column row with null value. Here's how you use it: Let's say we have this CSV file (first row is the header): Food,Type,Color Apple,Fruit,Red Banana,Fruit,Yellow Spinach,Vegetable,Green Here's how you'd use the new method: while($row = $files->getCSV('/path/to/foods.csv')) { echo "Food: $row[Food] "; echo "Type: $row[Type] "; echo "Color: $row[Color] "; } There are also several $options supported, see the $files->getCSV() documentation page for more. There's also a new $files->getAllCSV() method that does the same thing but returns all the rows in the CSV file in one call. Improvement to InputfieldPageListSelectMultiple Next was feature request #339 that requested adding the enhancement developed by @Robin S (via the PageListSelectMultipleQuickly module) into the core. This enhancement keeps the selectable page list open for multiple selections rather than closing it for each selection. It also better indicates when an item is selected. While I ended up writing it in a different way than Robin S.'s module, the end result is nearly identical to Robin S.'s short GIF screencast below: Also added this week is a new "visibility" option for Inputfields: Open + Locked (not editable), along with some small hook documentation improvements in the Pages class. Thanks for reading and have a great weekend!
    34 points
  11. TL:DR I've updated a PW page we've built 9 years ago for the first time and it's still a solid experience. Backstory Back in May I was on a crowded train somewhere in the middle of Germany. Now working as a "Consultant" who builds slidedecks instead of websites, I happily noticed the men next to me talking about responsive webdesign with his friend. During the obligatory "This train is late" announcement we started to chat. My seatmate, a geography teacher, recently attended a web workshop at a large Hamburg agency. He told me he now understands the value of a CMS for updating their site and he wonders how to build a responsive layout. They don't get paid for this and work on their homepage in their spare time. And they have a Typo3 installation 😅 Back in 2013, together with my friend Marvin, we've rebuild our school website with ProcessWire optimized for mobile devices. Launched in 2014 this was quite an impressive feat including online time tables, a working event calendar (with import feature) and many small nice touches. After my encounter on the train, I checked the page and yes, It's still online and updated daily! The next day I wrote my old teacher a short email if we should have a closer look into the underlying tech and within minutes I got a super happy reply that he is so glad that somebody would help (again). So let's dive into what we've done. Situation First some details about this ProcessWire installation that is updated by a few teacher on a regular basis. Over the 9 years they've wrote nearly 900 news articles and kept more than 250 pages up to date. The asset folder is over 11GB. Build with Processwire 2.4 (?) and lots of janky code we've updated the page once to 3.0.15 somewhere in 2016 quick and dirty. They even used the old admin layout. ProCache, CroppableImages3 and a few other plugins were used. Every single one of them required an update It's used the classical append-template approach with a single big "function.php" included file. It's running on PHP 5.6 and for whatever reason no PHP update was enforced by the hoster (But the admin panel screamed at me) A privacy nightmare: Google fonts embedded directly, no cookie banner and a no longer working Google Analytics tag included The old ProcessDatabaseModule made a database backup every week as planned over all these years. Nice. No hacks, no attacks and all teachers are using their own account with assigned permissions Changelog I've updated the page with a focus on making it stable and reliable for the next 9 years. After making a development copy of the page, I've started working on the following changes: Updated ProcessWire and all modules to the latest stable version. After reloading a few times, no errors encountered Updated the whole templates to make it work with PHP 8.2 Removed all externally hosted scripts, disabled cookies for all regular visitors and introduced a 2-click-solution for external content Reworked a few frontend style issues around the responsive layout, made slight visual changes for 2023 (e.g. no double black and white 1px borders) Ported the image gallery feature to more templates (Big wish of the people updating the site, they've used a workaround) Cleaned up folder and structures, removed a few smaller plugins and admin helpers no longer needed All this was done back in May and - with a big break - completed now in October. It took a few days and most of the time was spent figuring out our old code. Learnings ProcessWire is robust as f*ck. I just clicked "Update" and it mostly worked instantly I nearly removed features for the PHP update. A custom written importer for the proprietary XML schedule was hard to debug and understand (5-dimensional-arrays...). Gladly I've tossed a coin and just gave ChatGPT the php function source and error message and within a single iteration it updated the code for PHP8. The "responsive" CSS framework aged badly. The used 960gs skeleton uses fixed widths for the responsive layout. I couldn't get it be wider than 320px on mobile screens. So the site is responsive but with a slim profile for now. Replacing it would be a complete layout rewrite Result and looking forward The Werkgymnasium site is now updated and live again. It still loads superfast and looks great after all these years. We have a few more features planned to help our editors input new content but overall it just works. Looking forward a few issues remain. ProCache would require the paid update but it still works fine. The layout needs improvement on mobile screens. There is still an error with the pagination. We'll cleanup the code more and then make the whole template public on Github so that maybe a few students after us can continue with the updates. Maybe even rebuild the frontend one day. I hope I can give you an update in a few years again. As a closing note: I'm still grateful for the amazing community here and all the features ProcessWire has to offer. My daily work no longer resolves around websites but PW has a permanent spot in my heart. Thanks Ryan and all the contributors.
    33 points
  12. This week we've had a mixture of core updates and more work on the PageAutosave/LivePreview module. In the core we have mostly maintenance updates and issue resolutions. I'm not going to bump the version number this week as I'd like to get a bit more added to this version first. One feature request that appears in the core this week is the ability to specify colors for RepeaterMatrix item types (idea from @Ivan Gretsky). If you have any version of RepeaterMatrix and you use the current core dev branch, you can specify the background color for any repeater item type by placing a color hex code in the repeater type label (like at the end of it, but wherever). For instance, typing in #777777 (or just #777) would make the repeater items of that type have a grey background rather than the default color background. Though whatever color you choose, note that the text color is white, so don't choose too light of a color. (The hex color code is of course removed from the item labels.) The PageAutosave and LivePreview module also got several updates this week, bringing it that much closer to being production ready: Added support for automatic detection of best placement for live preview window position based on available space. It will choose the correct location and size either to the right, left, above or below your page editor window. Added support for remembering live preview window position in a cookie. So if you setup your preferred layout for editor window and live preview window, ProcessWire will continue to use it. Added a new option to support automatic replacement of individual page field values without having to add your own `pw-value-*` classes. Meaning, you can now gain the benefit of field-value updates in your live preview without PW having to re-render the page. This applies primarily to text-based fields (title, body, etc.) that are part of the page being viewed. LivePreview editor and preview windows now talk to each other with JS rather than use SSE/streaming. I found that it just works more reliably that way, puts less load on the server, and [to my surprise] also seems to be faster. The SSE option is still there in the module config, but it's no longer the default. The only time where I think SSE might be preferable is if you want to monitor changes made to the page from outside of the page editor (like from the API) in a live preview fashion, as the SSE option will pick those up too. In addition to the above, numerous minor bug fixes, improvements and optimizations were added. If you want to grab this new version (which is still a development/testing version), it's posted in the ProDrafts, ProDevTools and ProFields boards. There's more to cover with this module still. I've been so focused on it that I haven't yet started working with htmx. Though I did spend quite a bit of time in the docs and it looks amazing. From what I understand as it relates to this module, htmx could bring a lot of greatness to the front-end preview window, so I'm anxious to get into that, and even more excited to explore the possibilities it opens elsewhere in ProcessWire. Also, how awesome is it that the author of htmx stopped by to say hello and answer questions here in the forums, that made my day — big thanks to @totally not an htmx shill for joining the discussion, I hope I'll have some good questions before long too. Thanks for reading and have a great weekend!
    33 points
  13. I agree it is unfair to make comparisons like this. I also don't think it's very nice to come to an open source project's support board and try to convince people to use a commercial CMS. Maybe the fact that a comparison can be made at all is a compliment. But Craft is a big commercial project with a sizable team and lots of resources and money behind it. ProcessWire is the opposite of that and the comparison seems inappropriate. But since you've stated this is a comparison, I'm not sure where the comparison is — you've focused exclusively on what you think Craft does better (by your own preference), but haven't made efforts to point out the areas where PW does better. So it comes across a little bit as an advertorial for large commercial CMS at the expense of a small open source project. Throughout you are stating a preference for something in Craft and claim a similar feature in ProcessWire is "not well thought out." This is wrong. A more honest statement would be that you and I clearly have different preferences, or maybe you don't fully understand something. But that does not mean that something that differs from your preferences or understanding is "not well thought out." I would never commit something to the core that hasn't been really well thought out, that's something I take really seriously. While much of Craft doesn't suit my own (and others) preferences, and may not be "well thought out" according to how I think things should work, I'm not going to join their forum and tell them it's not well thought out. That's because I respect them and trust that it's well thought out, according to their needs and preferences. So when you start telling someone that something they've put a lot of work into isn't well thought out, that's akin to saying that you do not respect them. I make no claims about being perfect at anything, and there is room for improvement in everything. What I take issue with here are the broad, subjective and largely false generalizations, and that's what I'm replying to. I'm also concerned that anywhere that you've quoted me, you've taken a statement by me out of context, extrapolating it as proof for an unrelated conclusion you've made. For example: In that link we are not talking about ProcessWire at all, and instead are talking about the processwire.com website in development, NOT the CMS. Why would you say I'm "opposed to the idea" of accessible development? This is something I'm interested in and passionate about. I don't understand why you are quoting me on one thing and saying another. In that link, I'm writing about a module called ProcessUser and something that is imposed upon it for a specific security purpose, unique to that particular module, and no other. But you have used my statement as proof that building custom features in ProcessWire isn't encouraged or supported. Nothing could be further from the truth. I have never had such a thought. My thoughts and intentions would be exactly the opposite. What you consider an advantage, I consider a major disadvantage. PW puts nothing between you and PHP, by design, that's a major advantage. I certainly would not be happy with Twig integration being a default. Clearly you like using Twig, which is fine for you to have that opinion, and I think it's perfectly fine for Twig to be an option. But I definitely wouldn't want to standardize upon it. Maybe it doesn't suit your preference, but I hope you can appreciate why we don't do things like that in PW. You are comparing two different kinds of caches. You are talking about a template engine cache. In PW there is no template engine, so likewise no template engine cache. Most of the time you would never need such a cache in PW at all. The $cache variable is something different, and while it can also cache markup, it is much more simple than you imply. PW's API is not string-based. You are writing about selectors (and specifically selector strings), not the "API". ProcessWire lets you specify selectors as strings or as arrays. Most prefer to use strings due to the simplicity of it. But this does not mean that arrays are not reliable. And if you use arrays, then there's no need to do value sanitization. But the majority of the time you are using selectors, you are not injecting user input into it either, so I would consider selector strings to be a major advantage for PW the vast majority of the time. And for times that it's not, you don't have to use it. The problem with chaining methods for this is that they are live code, you can't store them. Perhaps it has benefits in Craft, but they would not be appropriate for PW. In PW selectors can be specified as arrays or strings, and in cases where you want to separate the query property from the query value, then that's why we have the array option in PW. And yes, they are reliable, despite your claim that they are not. The same is true in PW. Fieldtypes build the query and sanitize/validate the value to be queried. The only thing PW asks you to do is IF you are using a selector "string" that ALSO happens to contain user input, then just sanitize that user input part with the selectorValue() method. If you are using an array, then such sanitization is already done for you. This has nothing to do with details of a fieldtype, which will do its own validation/sanitization. Saying "clunky" is a subjective and hardly fair statement to make. What might be clunky to you is optimal to others. You are writing specifically about the file translation system, so the whole purpose is to provide an interface to translate your site php files (primarily template files). The interface is geared to focus on that task only, and that's the point of it. I understand you value more layers between things (like with Twig), but I always prefer fewer layers, a more direct approach, so you'll see that throughout PW. If I'm using a file translation system, I want to know what file I'm working with and I want to be able to tell the translator what file to work with. That my preference, and I understand you have a different one. Of course you can, that's who it is for. Yes they are grouped by file, and that's how I prefer it, and I have never had a client have a problem with that. If you have some preference to keep translators/clients out of the admin, you can also ask them to translate from a spreadsheet (exported from PW) and PW will happily read that in and use it. None of those 3 statements is true. Translations may be stored in JSON files, but they can be exported/imported (all files at once) to/from CSV files. Translation files are not stored in "random" locations. They are stored with the language's files, which is a static location identified by its ID. Adding translations in the code editor is not impossible, because where else would you add new translations, if not in the code? If you need a new translation, go ahead and add your __('text') in your code, and it immediately becomes available as a translation. You are welcome to your opinion, but as someone that put a lot of thought and effort into field translations, it's hard for me to take your opinions seriously when you state things like this. Nothing about them is "tacked on" or "not well thought out", quite the opposite. So to me it seems like at worst, you don't understand what you are talking about, or at best (and most likely), you just have an alternate preference. This is fine, but I don't think that's obvious to everyone reading. So please don't make statements like "not well thought out" when you really mean that something doesn't suit your preference, or maybe you don't fully understand it. I appreciate examples but I'm not aware of any field where you need to "add the second language's field manually". This does not sound at all like PW multi-language fields. If you are talking about PW at all, perhaps you are talking about the "language alternate" fields option? If so, this is an option that is there to provide additional flexibility for specific cases, but it is almost never used in my experience. Please don't use it as a foundation for any comparison, as its borderline deprecated at this point and I don't expect many will ever use it. This conclusion is the opposite of the truth. The fact that PW doesn't impose a specific framework upon you means that all options are open. Anything is available to you. It is more open, not less open. It's you and PHP, and anything you can run in PHP you can run in PW. ProcessWire feature approach is built around constant improvement. It is driven by the community and the needs of clients. The development of PW has always followed this approach. I have a job working with clients just like most PW users and so I develop according to what's needed and when. Most of my income comes from client work. I work on the PW core for free, so development usually has to have some crossover with the other projects that I work on. Despite being open source and largely unfunded, the fact that PW and Craft can end up with apparently similar capabilities and feature sets—and the fact that they can be compared at all—I would consider to be a sign of efficiency and that we are doing something right. If you consider our approach "chaotic" then fine, but the accurate terms would be "flexible", "sustainable", "consistent" and "continuous". The approach been very successful for PW as a project. And PW is also one of the longest lasting projects in this area (open source or commercial), and will continue to be because it's not built around money. I don't add features without completely thinking them out. That's silly, why would we do that? I would never commit something to the core that has not been well thought out. I don't understand why you would say this unless you just don't understand it. Nothing committed in the core has been abandoned. Elsewhere, I've abandoned a ton of code, but most of it has never been seen by anyone. When I commit something to the core, I've spent a lot of time working on it and I'm also committing to supporting it, long term, for as long as there is value in it. That's another reason why I take a lot of time to think through anything that gets added. You are welcome to say that you have a different preference, and you can I clearly have different preferences, but please do not suggest something is not well thought out because that is not true. Well, I'm glad you think there are "hundreds" of features that look cool... though not well supported? I don't know what we're talking about. False. The only bugs that don't get fixed are those where they cannot be duplicated, or that don't have a clear solution and seem to be isolated to one person. And I don't know what you mean about "don't work well with each other and so on". ProcessWire's core focus is already a limited feature set, where much is left to modules. The aim is to have everything you might "need" and do it really well, but save things you might "want" for modules. This is one area where it certainly does help to have a big commercial company behind it, with full time documentation writers and such. While they do have a structure and hierarchy, I understand it may not be exactly the way everyone might want it. The site search engine is actually quite powerful and searches the API and all documentation pages, so I recommend that when you want to find something specific. This is fair, and it'll continue to be that way, the blog posts can do heavy lifting in between the time that features are added and documentation is updated (as does this forum). The blog posts do also end up as the linked documentation for features in some cases. I'm okay with it. I understand that if we were a big company it might be odd though. I don't put efforts towards pretending that PW is a big company, I work within the resources we have, putting most of it towards where it counts and less of it towards pleasing critics. The aim is that all versions are compatible, meaning you can safely upgrade from any older version to any newer version. I don't know of any other projects that do this as well as PW, so I would consider it a major benefit. If we get to the point where this is regularly not the case, then we likely would adopt semantic versioning. But the need isn't there at this point in time. Close, but not totally accurate. New versions are currently aimed at a group of new related features or a group of issue fixes. The version number goes into systems that prompt people to upgrade, at the time it is appropriate to do so. They are also for documentation purposes so that we can easily reference them in "since" statements. Lastly, it's useful to have version numbers to reference in blog and/or forum posts. Currently this system of version numbers is the most beneficial one for this project. This isn't what I'd consider a breaking change. This is something where it just asks you to install a module as part of an upgrade to the version, it doesn't break the API or the site, just locks down a specific admin feature for security until you install the appropriate module. And security always comes first in PW. But I don't think there are any actual breaking changes in PW. Again, if it became commonplace like it is in other projects, then we'd likely adopt the same version number approach they do. So perhaps that's something that will come in the future, but we're not at that point. I'm open to it at the appropriate time. A "template file" is a "file" for a "template". I don't know how to be any more clear than that. But yes there are instances where we're talking about template files that I might use the term "template" rather than "template file" since the context is established. I agree that terms like "template" and "file" are fairly generic, and it would be nice if templates were like cars where we could refer to the "car" but also have a label like "wheels" and know we are still referring to the bigger "car" above it. But "file" is a generic enough term that while accurate, needs context. I'll take it as a compliment that you consider RepeaterMatrix to be so valuable. The Pro modules were never intended to be "required" for any site. They are meant to be a luxury or a time saver, but there's nothing you can do with any Pro module that you can't do yourself by some other means. Though Pro modules might save you time in doing it. I've built example modules that show you how to do do just about anything that Pro modules can do, they just might require more of your effort. The original intention for Fieldtypes and Inputfields was that everyone would build their own according to their needs, but I don't know many that do that other than me. Have a look at FieldtypeEvents sometime to see how simple it is to do. I don't share your opinion on this. If you are of the mindset that the page tree reflects the front-end site 1-to-1 then you are in the mindset of a different system. I consider the tree to be essential, a major benefit, as you know everything has a place and hierarchy and you don't have to have various different kinds of navigations or buckets to find them. If you know one thing you know everything in PW. I've never been a fan of different bucket systems and the ambiguity that they introduce. ProcessWire was designed from the beginning to get away from what you are talking about, what I subjectively feel is a mess in other systems (again, my preference). PW aims to be simpler and more flexible than that, though pages can still be browsed as a tree or as buckets, even if the tree is the source. I don't agree with your premise, or the linked post. Having a link to someone else's opinion does not make it right, maybe just right for you personally, that's fine. I would agree that there are benefits to letting the page tree influence your navigation, but there aren't drawbacks to not using it that way either. It may have started that way. While I am currently the gatekeeper in terms of maintaining the core I consider ProcessWire to be a long term community developed project, and it becomes more that every year. Every open source project needs someone to start it, and someone to be a caretaker, ensuring the quality of it, and I see that as my role, which I take very seriously. But this is a team project and one that is much more sustainable long term than a commercial one.
    33 points
  14. This week the core dev branch version has been bumped to 3.0.231. There are about 15 commits in this version relative to the previous. In the dev branch commit log you'll find a good mix of issue fixes and improvements. If you are already using the dev branch, this version is worth the upgrade. If using the main/master branch then it should be a safe upgrade as well, though none of the updates are urgent. And it won't be long till they are also merged to the main/master branch soon too. This week I've also been working on 2 new related modules: FieldtypeDateRange and InputfieldDateRange. These modules allow selection of starting and ending dates to support a date range. It also calculates and stores the number of days and nights for querying and sorting purposes. The "date from" and "date to" can be independently queried from $pages->find(), as can the days or nights. The InputfieldDateRange module can be used independently of its companion Fieldtype module, making it possible to use the date range Inputfield in FormBuilder or other Inputfield forms. One context where the Inputfield might be useful is when selecting travel dates on a front-end form, such as one from FormBuilder. When used as a Fieldtype, you might use it to specify availability of something, start and end dates to publish content, event dates, or any number of other use cases. Below is a screenshot of the Inputfield as well as its configuration tab. The JS-based input widget comes from an existing package that I've made some modifications to, and it works really nicely, with a polished and easy-to-use UI. I originally found it booking some travel online, and really liked the way it worked. I was able to track it down on GitHub here and thought it would be useful to build an Inputfield module around it. It can be set up to work like the core date picker where it appears when you focus in the input, or it can be configured "inline" where it is alway s visible (and the related text input is hidden). In the following screenshot, I've specified that Sundays can't be used for start/end selections and that November 23 is not available. The selected range spans two months. If you want it to span more months, you could click the right arrow in the December calendar to find your desired month, leaving the first calendar in November. In this manner, you can select ranges that span multiple months, or even years: Here's the Inputfield configuration screen so far. All of these settings are hookable as well, as some of them are more likely to be useful dynamically, especially min/max start and end dates, non-selectable dates, etc. (Note the screenshot below does not necessarily reflect the settings in the screenshots above). More on this next week. Have a great weekend!
    32 points
  15. The master/main branch is now updated to version 3.0.225. Early next week I'll be adding a git tag for the version number as well. I usually like to merge dev to the master/main branch first, let it marinate for a day or two, and then tag it. That's because once we tag it, it triggers other services to pick it up and broadcast it. So letting it marinate for the weekend just adds a layer of comfort, for whatever silly reason. That's pretty much how I've always done it. When I did the merge, it reported 511 files changed, 76421 insertions(+), 23539 deletions(-), so there's quite a lot in this version. There's enough, that I'm going to need another week to document it all into a new blog post, which should be ready by this time next week. Our contributors list also continues to grow nicely with this new version. Thanks to all those that have submitted PRs, reported issues, and submitted feature requests. Big thanks to @matjazp in particular who has been helping a lot in identifying, testing, organizing and even coding the solutions for numerous issue reports. More details on this new version next week. Until then, thanks for reading and have a great weekend!
    32 points
  16. In a recent GitHub issue report, I was asked about output formatting in ProcessWire, and where more information could be found about it. I know I've written about it numerous times, and went to locate the documentation page, only to find we didn't have one! Output formatting is such an important topic, so here is everything you need to know. I hope you'll find it simple enough, but also useful and thorough— https://processwire.com/blog/posts/output-formatting/
    32 points
  17. I was in New Orleans at the gymnastics Nationals most of this week. In her age group and level, my 10-year old daughter won 4th overall and 3rd on bars and beam. After a long drive, we're now back home in Atlanta and it's been a very short work week, but there's still a new dev branch version to write about. ProcessWire 3.0.221 continues primarily with minor issue fixes, working towards our next main/master version. Included are 11 resolved issues, 2 PRs, and code contributions from @matjazp and @dotnetic. In terms of new features, this version updates the language translating _n() function to support languages that consider 0 quantities as singular rather than plural in calls like _n('%d item', '%d items', $quantity); Previously this call has always used the plural "items" version for 0 quantities (i.e. "0 items"), which is correct in English, but may not be in other languages like French (as I've learned from issue #1757, though I think it has come up once before too). To define whether a language should consider 0 quantities plural or singular, use ProcessWire's language translation tool: Setup > Languages > [any language] > Find files to translate > wire/modules/LanguageSupport/LanguageTranslator.php ... when translating that file, you'll see the setting at the top labeled "Is zero (0) plural or singular?": That screenshot above also shows another new feature that was added, which is the ability to use Select and Radios fields when defining translatable text. Previously you could only use text, textarea and number fields. Let's say you wanted to have the person translating choose a color name for the language as part of the translation: $color = __('Red'); // What color? type=radios options=[Red, Green, Blue] As before, the "What color?" part is an optional description for the translatable text. Also as before, the "type=..." defines what Inputfield type to use. The supported values are any Inputfield name (minus the "Inputfield" part). Known to work values for this include: text, textarea, integer, float, radios and select. The "options=[...]" is the newly added part, and this enables you to define the selectable options for select or radios inputs. If you wanted to use separate value and label, you can also do that. In the example below, city abbreviations are used for the values and full city names as the labels: $city = __('ATL'); // What city? type=radios options=[ATL:Atlanta, CHI=Chicago, NYC:New York City] Another example is the one we used in the core for plural vs. singular here. By the way, if any of your values or labels need a literal comma, you can optionally use a pipe "|" as the separator rather than a comma. This ability to use Select and Radios is a fairly minor addition, but does open up better support for having certain language settings (rather than just translatable text) be part of language translation packs going forward. The plural vs singular setting for 0 seemed like a good first one to support with this. Next week we'll continue preparing our next main/master version. Thanks for reading and have a great weekend!
    32 points
  18. This week the ProcessWire core version on the dev branch has been bumped to 3.0.207. Relative to the previous version, there are several minor issue resolutions and improvements (commit log). I also recommend this version if you are testing out the InputfieldTinyMCE module, which will likely be merged into the core near the end of the year. Speaking of that module, it also received updates this week with the biggest being the addition of improved lazy loading modules for the Normal (non-inline) editor. Rich text editors are one of the most heavyweight input types you can use, so not having to initialize them all during page load is a major performance benefit, especially when you've got multiple fields using them at the same time. With these new lazy-loading modes, the Normal editor has many of the benefits of the Inline editor in terms of page editor performance, but without any of the drawbacks. The new default setting is to "load editor when it becomes visible". This ensures that resources aren't spent loading editors that are hidden behind editor tabs, fieldsets or language tabs, until they are needed. The other lazy-loading option ("load editor when clicked") is the most aggressive lazy loading option. It shows a preview of the editor content but doesn't actually load TinyMCE until you click the preview to edit it. Lastly, I've also been working on a new module (WireSitemapXML) that generates sitemap.xml output, but in a way that I think is more configurable than the other modules available for it. It also supports multi-language sitemaps, URL segments, various hooks and more. While I've got it in use already, I'm going to spend more time on the documentation before releasing it. That's all for this week, have a great weekend!
    32 points
  19. This week we have some great performance and scalability improvements in the core that enable lazy-loading of fields, templates and fieldgroups, thanks to @thetuningspoon — https://processwire.com/blog/posts/field-and-template-scalability-improvements/
    32 points
  20. This week I'm happy to report that the InputfieldTinyMCE module is now released. It is currently released in the modules directory and GitHub but the plan is it will be merged into the core, likely before the end of the year. No need to wait till then though, as you can start using it today. Please consider the module in beta for the moment, though the TinyMCE library itself is in a stable state. A lot of the work that went into developing this module went into the configuration aspect. Here are a few a more details that weren't covered in last week's post: After installing the module, on the module configuration screen, you can decide whether several settings should be configurable for each field, or if you want to just configure them with the module (affecting all fields): One of things that I thought was important was to make it a lot simpler to add custom classes/styles to the editor. I always found this kind of a pain in CKEditor. So in TinyMCE, I made it so that you can just define these custom styles with the field settings using just simple CSS definitions. InputfieldTinyMCE takes care of converting to a format that TinyMCE can understand (for its menus), as well as the styles to show in the editor. For instance, I wanted to add some common Uikit text classes to a custom "Uikit" group in the Styles dropdown: And here's the result in the editor: The markup produced has the correct Uikit classes in the markup so that on the front-end of my site the output is Uikit ready. You can add 3rd party or your own custom plugins from the module settings: And then you can enable them for any field in the field editor: These are just a few interesting tidbits, but there's a lot more. Also, if you didn't see last week's blog post, that covers a lot more too. Either way, I'd encourage you to download InputfieldTinyMCE, give it a try and please let me know how it works for you. If you come across any bugs, please open an issue report. Thanks for reading and have a great weekend!
    31 points
  21. Continuing from last week's post and discussion, ProcessWire 3.0.218 decouples the modules system from the cache system. Now the modules system maintains its own internal caches (at least once you do a Modules > Refresh). It'll still use the $cache API as a backup (temporarily), but now you can safely export the database without the "caches" table, or even delete the "caches" table, if you want to. It'll get re-created as needed. In this version, work also continued on the new WireCacheInterface (and major updates in WireCache) so that we could support external modules to handle cache storage. This capability is kind of similar to how we support 3rd party WireMail and WireSessionHandler modules. The first example is WireCacheDatabase, which is the default cache storage handler for the core. And today we have a new module called WireCacheFilesystem that replaces the default WireCache database storage with a file-system based storage, once installed. It's not yet clear if there are major benefits one way or the other (cache in database vs. file system), as I've not been able to put all this new code through performance testing yet. I'd definitely be interested to hear if anyone has a chance to test things out. I expect the file system might be faster for reading caches, while the database may be faster for writing caches. At least that's what I found with a few preliminary experiments, but they haven't been very thorough, so take that with a grain of salt. I thought we needed at least 2 examples of classes implementing WireCacheInterface before we'd be ready to support potential 3rd party WireCache modules. I imagine that 3rd party modules getting into dedicated cache options independent of database or file system is where we'll start to see major performance benefits. At least for sites that use the cache heavily. That's all for this week, have a great weekend!
    31 points
  22. On the dev branch this week we have a good collection of issue fixes and feature requests. The dev branch commit log has all the details. One feature added this week which I think could come in very handy is #520 (via @Jonathan Lahijani) which adds the ability to hide individual images in an images field. When an image is hidden, you can see and work with it in the admin, but it gets removed from the field value on the front-end of the site at runtime, effectively hiding it. I know I'll use this a lot, particularly on photo galleries where I may want to remove an image or two from appearing in the grid of photos, but don't necessarily want to delete them. Images can be hidden (or unhidden) from the Actions select of each image, where you'll see a "Hide" option (or an "Unhide" option if the image is already hidden). Hidden images are also dimmed out when viewing the images field in the admin. On the API side, you can hide or unhide images and files using $image->hidden(true) to hide, $image->hidden(false) to unhide, and $image->hidden() to get a true or false as to whether or not the image is hidden. Though this will only be useful on unformatted field values, since hidden images are automatically removed from formatted field values. The same can be used with regular file fields, but we don't currently have a UI/interface for hiding or unhiding items from regular (non-image) file fields. Likely we'll add one soon, but I figured it's likely to get more use with image fields than file fields, so figured we'd start there. More next week. Thanks for reading and have a great weekend!
    30 points
  23. This week core updates are focused on resolving issue reports. Nearly all of the 10 commits this week resolve one issue or another. Though all are minor, so I'm not bumping the version number just yet, as I'd like to get a little more in the core before bumping the version to 3.0.202. This week I've also continued development on this WP-to-PW site conversion. On this site hundreds of pages are used to represent certain types of vacations, each with a lot of details and fields. Several pages in the site let you list, search and filter these things. When rendering a list of these (which a lot of pages do), it might list 10, 20, 100 or more of them at once on a page (which is to say, there can be a few, or there can be a lot). Each of the items has a lot of markup, compiled from about a dozen fields in each list item. They are kind of expensive to render in terms of time, so caching comes to mind. These pages aren't good candidates for full-page caches (like ProCache, etc.) since they will typically be unique according to a user's query and filters. So using the $cache API var seems like an obvious choice (or MarkupCache). But I didn't really want to spawn a new DB query for each item (as there might be hundreds), plus I had a specific need for when the cache should be reset — I needed it to re-create the cache for each rendered item whenever the cache for it was older than the last modified date of the page it represents. There's a really simple way to do this and it makes a huge difference in performance (for this case at least). Here's a quick recipe for how to make this sort of rendering very fast. But first, let's take a look at the uncached version: // find items matching query $items = $pages->find('...'); // iterate and render each item foreach($items as $item) { echo " <!-- expensive to render markup here ---> <div class='item'> <a href='$item->url'>$item->title</a> ...and so on... </div> "; } That looks simple, but what you don't see is everything that goes in the <div class="item">...</div> which is a lot more than what you see here. (If we were to take the above code literally, just outputting url and title, then there would be little point in caching.) But within each .item element more than a dozen fields are being accessed and output, including images, repeatable items, etc. It takes some time to render. When there's 100 or more of them to render at once, it literally takes 5 seconds. But after adding caching to it, now the same thing takes under 100 milliseconds. Here's the same code as above, but hundreds of times faster, thanks to a little caching: // determine where we want to store cache files for each list item $cachePath = $config->paths->cache . 'my-list-items/'; if(!is_dir($cachePath)) wireMkdir($cachePath); // find items matching query $items = $pages->find('...'); // iterate and render each item foreach($items as $item) { $file = $cachePath . "$item->id.html"; // item's cache file if(file_exists($file) && filemtime($file) > $page->modified) { // cache is newer than page's last mod time, so use the cache echo file_get_contents($file); continue; } $out = " <!-- expensive to render markup here ---> <div class='item'> <a href='$item->url'>$item->title</a> ...and so on... </div> "; echo $out; // save item to cache file so we can use it next time file_put_contents($file, $out, LOCK_EX); } This is a kind of cache that rarely needs to be completely cleared because each item in the cache stays consistent with the modification time of the page it represents. But at least during development, we'll need to occasionally clear all of the items in the cache when we make changes to the markup used for each item. So it's good to have a simple option to clear the cache. In this case, I just have it display a "clear cache" link before or after the list, and it only appears to the superuser: if($user->isSuperuser()) { if($input->get('clear')) wireRmdir($cachePath, true); echo "<a href='./?clear=1'>Clear cache</a>"; } I found this simple cache solution to be very effective and efficient on this site. I'll probably add a file() method to the $cache API var that does the same thing. But as you can see, it's hardly necessary since this sort of cache can be implemented so easily on its own, just using plain PHP file functions. Give it a try sometime if you haven't already. Thanks for reading and have a great weekend!
    30 points
  24. This is a holiday week here in the US, at least for my kids (school break). With kids home I've been off work this week so don't have any ProcessWire updates to report, but I'm looking forward to getting back to work next week and will have more updates to report then. Yesterday was Thanksgiving here in the the US and that kind of marks the beginning of the holiday season here. So today the Christmas tree went up, the lights are coming out and the holiday music has taken over the radio (or Alexa or Google or whatever we call it). Cheers and Happy Holidays!
    30 points
  25. I'm off work this week, so I don't have any new ProcessWire updates, but just wanted to wish you a Happy New Year! Looking forward to a great 2024!
    29 points
  26. This week updates continued with various .js files in the core (for jQuery 3.6+ compatibility). A few external libraries were also updated to the latest versions, including jquery-tablesorter, jquery-ui-timepicker, magnific-popup and vex. And finally, InpufieldTinyMCE has been added to the core! That's a lot of updates, but there's not a lot more to write about these updates because we covered the jQuery updates last week, and InputfieldTinyMCE has been the topic of several past posts. But now we've got everything together and running smoothly in ProcessWire 3.0.216. I think we're now ready to focus on getting the next main/master version out in the coming weeks. There likely won't be an update next week because I'll be traveling for a few days, but will be back to a regular schedule the following week. Thanks for reading and have a great weekend!
    29 points
  27. This week we take a look at a new rich text editor for ProcessWire, why we chose it, some highlights, screenshots, and why we think you’ll like it— https://processwire.com/blog/posts/new-rte-for-pw/
    29 points
  28. This last week my wife and daughter took a trip to NYC and it was my daughter's first time there. I was browsing around online looking at things they could do and so I visited the Guggenheim museum website to look into that option... I've always been a fan of the building, a Frank Lloyd Wright masterpiece. In addition to New York, I learned from the website that Guggenheim also has museums in Abu Dhabi, Bilbao and Venice, so I clicked through to view them as well. I really liked the Venice Guggenheim website, which had a much nicer website than the other locations. It was such a nice site that I was curious what they were running, so I viewed the source and... not WordPress (like the others), but ProcessWire. What a nice surprise. Then I was curious about who made such a nice site and there was a credits link in the bottom right corner that says the site was made by basili.co, nice work! It's always fun to come across a ProcessWire powered website randomly like that, and I thought you all would enjoy this one too. This week there are fairly minor updates on the core dev branch. Though the updates include one I've been meaning to do for a long time: improve the API for processing Inputfield forms. Previously there's been no way to check if a form is submitted, short of checking an $input variable yourself. Today I committed an update that adds a $form->isSubmitted() method that solves that, and more. It can identify which form was submitted, which submit button was used, and it also performs additional checks to make sure it's a valid submission before deciding that it's a form worth processing. It improves reliability, accuracy and security. Next week I'll be updating several of the admin forms to use it, among other updates. A few other useful helper methods were added to the Inputfield forms API as well. I realize that these updates may only be of interest to module authors, but I like keep you up-to-date with the week's updates either way. Thanks for reading and have a great weekend!
    29 points
  29. This week I've continued work on the page versions support that I wrote about last week. While the main PagesVersions module needs more work before it's ready to commit to the dev branch and test externally, it is so far working quite well in internal testing. I mentioned last week how it will support an interface where Fieldtypes can declare that they will handle their own versions. That interface has been pushed to the dev branch as FieldtypeDoesVersions. I've also implemented it with the Repeater Fieldtype, which is visible starting here. Repeaters are so far working really well with versions! As far as core Fieldtypes go, Repeater, PageTable and FieldsetPage are likely the only ones that will need custom implementations. For ProFields, RepeaterMatrix already works with the implementation in the core Repeater (already tested). It's possible that other ProFields will not need custom implementations, though not yet positive. The module that provides version capability is called PagesVersions and the plan so far is to make it a core module that provides API version support for pages. A separate module provides interactive version support in the page editor. I've built this module initially so that I can test versions more easily, but it'll be expanded to provide a lot more. Below is a screenshot if what it looks like in the page editor Settings tab so far: As you can see, you can Edit or Restore any existing version. You can also create a new version from the Live version, or any other version. And of course you can view and delete any versions. When you Restore a version, it essentially replaces the live version with the one that you are restoring. All of this can be done from the module's API as well. Note that the API is provided by a $pagesVersions API variable that is present when PagesVersions module is installed. The API method names and such are a bit verbose right now but may be simplified before it's final. // Get page and add a new version of it $page = $pages->get(1234); $page->title = 'New title'; $version = $pagesVersions->addPageVersion($page); echo $version; // i.e. "2" // Get version 2 of a page $pageV2 = $pagesVersions->getPageVersion($page, 2); // Update a version of a page $pageV2->title = "Updated title"; $pagesVersions->savePageVersion($pageV2); // Restore version to live page $pagesVersions->restorePageVersion($pageV2); // Delete page version $pagesVersions->deletePageVersion($pageV2); Thanks for reading! More next week.
    28 points
  30. After 8 months in development we are excited to bring you ProcessWire 3.0.226 main/master. This version has a ton of great new features, improvements and optimizations, plus more than 100 issue fixes. This post takes an in-depth look at highlights from this great new version. While there's even more in this version than is covered fully here, we hope this gives you a good taste of what you'll find in 3.0.226! https://processwire.com/blog/posts/pw-3.0.226/
    28 points
  31. Sometimes you need to execute a slow task after some event occurs in the PW admin, and normally you have to wait for this task to finish before you can continue using the admin. This is because PHP is "blocking", meaning that while one thing is executing nothing else can execute. There are potentially lots of different kinds of tasks that could be slow, but just as an example suppose you want to generate resized variations of images on a page, and there are a lot of images. You might have a hook like this so that any non-existing variations are created when the page is saved: $pages->addHookAfter('saveReady', function(HookEvent $event) { /** @var Page $page */ $page = $event->arguments(0); // When a gallery page is saved if($page->template == 'gallery') { // Create an image variation for each image foreach($page->images as $image) { $image->size(1200, 1200); } } }); When you save a gallery page in the PW admin, the admin will be unresponsive and will only load again after all the variations have been created. I wanted to find a way for slow tasks to be triggered by events in the PW admin and for the website editor not to have to wait for the task to finish before continuing with other work in the admin. Inspired by this StackOverflow answer I came up with the following solution that seems to work well. Using the image variations task above as an example... First we make use of the URL hooks feature to set up a URL that can trigger tasks to run when it is loaded: // A URL that will trigger tasks when loaded $wire->addHook('/run-task/', function($event) { $input = $event->wire()->input; // A simple check to avoid unauthorised access // You could implement more advanced checks if needed if($input->post('key') !== 'cTdPMBQ7x8b7') return false; // Allow the script to keep running even though we have set a short WireHttp timeout ignore_user_abort(true); // The "create variations" task if($input->post('task') === 'create-variations') { $page_id = (int) $input->post('page'); $p = $event->wire()->pages->get($page_id); // Create an image variation for each image foreach($p->images as $image) { $image->size(1200, 1200); } return true; } return false; }); Then in the Pages::saveReady hook we use WireHttp to load that URL and post parameters that define what task to run and anything else needed for the task (in this case the ID of the page that has been saved). $pages->addHookAfter('saveReady', function(HookEvent $event) { /** @var Page $page */ $page = $event->arguments(0); // When a gallery page is saved if($page->template == 'gallery') { // Load the /run-task/ URL using WireHttp $http = new WireHttp(); // Set a short timeout so we don't have to wait until the script finishes // Timeout values shorter than 1 second can be tried once a core issue is fixed // https://github.com/processwire/processwire-issues/issues/1773 $http->setTimeout(1); $url = $event->wire()->config->urls->httpRoot . 'run-task/'; $data = [ 'key' => 'cTdPMBQ7x8b7', 'task' => 'create-variations', 'page' => $page->id, ]; $http->post($url, $data, ['use' => 'curl']); } }); By doing it this way the task runs in a separate request and the website editor doesn't have to wait for it to finish before they can continue working in the PW admin.
    28 points
  32. The new dev branch version 3.0.222 contains about 20 commits and 16 issue resolutions. In terms of new features, last week I mentioned some upgrades to WireHttp, and below are this week's additions: Multi-language month and day names The WireDateTime class (aka the $datetime API variable) has been updated to support multi-language month and day names. Now all month and days are translatable in the WireDateTime file (/wire/core/WireDateTime.php). So if you request any date in a format that uses month names or abbreviations, or day names or abbreviations, they now support multi-language, whether you requested it from the wireDate() function or the $datetime API variable. ProcessWire has long supported multi-language month and day names when using a PHP version prior to 8.1 and you've requested a strftime date format that uses them. But PHP 8.1 dropped the multi-language supporting strftime() function, without leaving a suitable replacement. PHP's IntlDateFormatter can't be relied upon since it's not always compiled with PHP. But as of PW 3.0.222, now we have a suitable replacement. Though it does require you to translate the 7 days and 12 months for file /wire/core/WireDateTime.php using ProcessWire's language translation tool. Note that unlike the previous strftime() solution, the new solution no longer requires you to use strftime() format codes and you can instead use regular date formats and they will be translated automatically. New conditional hooks that match by argument type Another new addition this week is support for conditional hooks that match by argument type. I think this is especially useful combined with ProcessWire's custom page class support. It enables you to make a hook apply only to methods containing arguments of a specific type. For instance, if you had a custom page class "ProductPage" for template "product" and you wanted to capture the "Pages::saved" event for pages of that type (ProductPage) you could do so like this: $wire->addHook('Pages::saved(<ProductPage>)', function($event) { $product = $event->arguments(0); /** @var ProductPage $product */ $event->message("ProductPage '$product->title' has been saved"); }); In addition to supporting class or interface names, you can also specify regular PHP types such as <array>, <string>, <int>, <object>, etc. For more details on this type of hook see the new Conditional hooks that match argument type section of the Hooks documentation. Thanks for reading and have a great weekend!
    28 points
  33. Before the recent and major core updates to WireCache and Modules, we were on track to get a new main/master version out. I'd like to get back to that, as we are now 10 dev versions ahead of the main/master branch, and with some pretty good and major improvements. That's why I've been focused largely on minor issue fixes the last couple of weeks, getting into more of the fine tuning stuff, and likely will be the next couple of weeks as well. Thank you for opening issue reports as you come across stuff to report. Thanks also to @matjazp who's been helping out in the issues repo, maintaining existing reports and often helping to solve them too. I'm thinking we may have a new main/master version ready soon as July, next month. Most of next week I'll be in New Orleans attending the gymnastics nationals where my 10 year old daughter is competing. Since I'll be out of town, there likely won't be a lot of commits next week. Though there may be enough to bump to the next version, 3.0.221, I'm not sure yet. In any case, have a great weekend and week ahead!
    28 points
  34. The ProcessPageList module now has a configuration setting where you can select pages that should not be shown in the page list. For example, maybe once you've set up your 404 page, you don't really need it to display there anymore, except maybe in debug mode. Or maybe you don't ever need the "Admin" page to display in the page list. This new feature lets you do that, and for any page that you decide. Next, a "Usage" fields has been added to the "Basics" tab in the Field editor, just like the one in the Template editor (requested feature #445). This shows you what fields are using the template. It's essentially the same information that you'll find in the "Actions > Add or remove from templates" feature, but easier to find, especially for people newer to PW. That's all for this week, I hope you have a great weekend!
    28 points
  35. In the last couple of weeks I've been working on the page versions support in ProcessWire (recap here and here). This week the new PagesVersions module was committed to the core. Though please consider it very much "beta" at this stage. Along with this, the core dev branch version was bumped to 3.0.232. The API reference page for PagesVersions is now live here: https://processwire.com/api/ref/pages-versions/. Note that the module is not installed by default, but once running 3.0.232, it can be installed by going in your admin to Modules > Wire > Pages > PagesVersions. In addition, a related development module named PagesVersionsPro has also been released. This module uses the new API from the core PagesVersions module. This module will eventually be merged with or replace ProDrafts. The new PagesVersionsPro support board and module is currently visible to ProDrafts, ProFields and ProDevTools subscribers here. Unlike ProDrafts, PagesVersionsPro gets all of its version abilities from the core, and instead just focuses on providing an interactive interface to them in the page editor. To word it another way, the module does not extend the PagesVersions module in the way that ListerPro extends Lister. Instead, it just provides a web interface for it. I think this is a better long term and more sustainable strategy for handling version support. Core version 3.0.232 also adds version support for nested repeaters and FieldsetPage fields. Support was added in those Fieldtypes directly. Still remaining are PageTable (core) and Table (ProFields), both of which will need their own implementations for versions like Repeater and FieldsetPage needed. But following that, there won't be any unsupported fieldtypes to my knowledge. ProcessWire Weekly published its 500th issue! Congratulations and big thanks to @teppo for his incredible work with ProcessWire Weekly, it is truly outstanding! Thanks for reading and have a great weekend!
    27 points
  36. This week we've got a few minor issue fixes and a couple of pull request additions on the dev branch. Pull request #251 thanks to @Jan Romero added a download button to the thumbnail images in InputfieldImage. I wasn't sure we really needed that, but really liked his thinking behind it, which was envisioning the ability to add more custom buttons/actions for images. So while I didn't specifically add the download button, I added the proposed system for adding any custom buttons, and then applied that same thinking to some other parts of InputfieldImage. And we'll talk about how to add that Download button here. 🙂 First, let's look at how you might add your own download button, and note we're using this as just an example, as you might add any kind of button this way. A new hookable getImageThumbnailActions() method was added for this purpose. So here's how you might hook it (in /site/ready.php) to add a download button: $wire->addHookAfter('InputfieldImage::getImageThumbnailActions', function(HookEvent $event) { $image = $event->arguments(0); // Pageimage $class = $event->arguments(3); // class to use on all returned actions $a = $event->return; // array $icon = wireIconMarkup('download'); $a['download'] = "<a class='$class' href='$image->url' download>$icon</a>"; $event->return = $a; }); With that hook in place, here's what it looks like when you hover a thumbnail image. And if you click that Download icon, it downloads the file to your computer: or in list mode (download icon appears in right corner next to trash): I was thinking it would be useful to also be able to add custom actions after you click the thumbnail, and it shows the image edit features. So let's add a Download button there instead, by hooking the new getImageEditButtons() method: $wire->addHookAfter('InputfieldImage::getImageEditButtons', function(HookEvent $event) { $image = $event->arguments(0); // Pageimage $class = $event->arguments(3); // class(es) to use on all returned actions $buttons = $event->return; // array, indexed by action name $icon = wireIconMarkup('download'); $buttons['download'] = "<button class='$class'><a download href='$image->url'>$icon Download</a></button>"; $event->return = $buttons; }); And the result looks like this (see new Download button after Variations button): We also have that Actions dropdown that you see in the screenshot above. This is already hookable but we've not had any good examples of it. In this case, you need two hooks: one to add the action to the <select> and another to handle the processing of the action when the page is saved. So in our next example, we'll demonstrate how to display verbose EXIF information about whatever image(s) the action was selected for. In this first hook, we'll add the action to the Actions <select>: // Example of adding an “Get EXIF data” action to the <select> $wire->addHookAfter('InputfieldImage::getFileActions', function(HookEvent $event) { $image = $event->arguments(0); // Pageimage if($image->ext == 'jpg' || $image->ext == 'jpeg') { $actions = $event->return; // array $actions['exif'] = 'Get EXIF data'; $event->return = $actions; } }); And in this next hook, we'll handle the action, which gets called when the page editor form is submitted: // Example of handling an “Get EXIF data” action $wire->addHookAfter('InputfieldImage::processUnknownFileAction', function(HookEvent $event) { $image = $event->arguments(0); $action = $event->arguments(1); if($action === 'exif' && file_exists($image->filename)) { $exif = exif_read_data($image->filename); $event->warning([ "EXIF data for $image->name" => $exif ], 'icon-photo nogroup'); $event->return = true; } }); And here's what it shows after you hit save (for any images that had the action selected): The screenshot above is truncated because it was about twice is big as what's above. All the above code examples are also included in the phpdoc for each of the relevant hookable methods in the InputfieldImage module. For another recent useful addition, be sure to check out ProcessWire Weekly #454 (last week) which covered some new options available for the language translation functions like __text('hello'); where you can now tell it what kind of input type (and how many rows) to use in the admin translation interface, via inline PHP comments. Thanks for reading and I hope you have a great weekend!
    27 points
  37. This week we have ProcessWire 3.0.201 on the dev branch which includes a couple minor fixes but also a couple of useful additions: There are new "Tab" field visibility options available for any field in the page editor. This makes the field display in its own page editor tab. It saves you from having to create a new/separate Tab field just to wrap around an existing field to make it display in a tab. So far I've found it particularly useful with ProFields (Combo, Table, Textareas, RepeaterMatrix) as well as the core Repeater, FieldsetPage and Images fields. For instance, I have a Combo field labeled "SEO" that lets me edit browser_title, meta_description, canonical_url, etc., and now I can add that field to any template and pages using that template have their own "SEO" tab. Sure you could do this before by creating a separate tab field with an "SEO" label, but now it's a lot simpler/faster, as any field can now display as a tab on its own. Like with the other visibility modes, also included are options to make the tab load dynamically on click with ajax or make the value non-editable. You can also optionally specify a label for the tab independently of the field label. This is because usually you want tab labels to be very short (1 word is best) whereas field labels have no such need. Please note that this new Tab option is exclusive to the page editor, and in order to work the field must not already be in a tab. Also added this week is a new $page->getMultiple() method. This method works the same as the $page->get() method except that it lets you retrieve multiple page property/field values at once and returns them in an array. For example: $a = $page->getMultiple([ 'foo', 'bar', 'baz' ]); list($foo, $bar, $baz) = $a; It also accepts a CSV string: $a = $page->getMultiple('foo,bar,baz'); By default it returns a regular PHP array, suitable for using in list() like the first example. But if you want it to return an associative array instead, then specify true as the 2nd argument: $a = $page->getMultiple('foo,bar,baz', true); echo $a['foo']; echo $a['bar']; echo $a['baz']; I find this method useful in reducing the amount of code/lines necessary in many cases, and most often I use it in combination with a PHP list() call, i.e. list($title,$subtitle,$body) = $page->getMultiple('title,subtitle,body'); That's all for this week. Thanks for reading and have a great weekend!
    27 points
  38. The TinyMCE 6 rich text editor opens up a lot of new and useful abilities for ProcessWire users. In this post, we'll take a look at a few of them, and how you can start using them now, with a focus on those that are unique to ProcessWire's implementation of TinyMCE— https://processwire.com/blog/posts/using-tinymce-6-in-processwire/
    26 points
  39. If you haven't yet noticed it, @teppo has hit the 500th issue of PW Weekly! That is a ridiculously massive milestone and an amazing achievement, and after some quick maths, also tells me that he's held strong for almost 10 years now! I'm not sure what's more surprising, that he's managed to keep it going continuously for this long, or that I remember when he started it... That's a long darn time. I may not be a hugely active member of the community, but I'm darn proud to be a part of it regardless. Thank you so much for your devotion to the PW Weekly project, @teppo!!!!
    26 points
  40. I posted a (rather lengthy) comment about some of the ways I work with my clients. I'll be posting some of that here where reading it in context can help the message as a whole. I've been a web designer/developer for ~15 years in many capacities like freelancing, in-house employee, and now at my own one-person company (I'll get into why that's different than freelancing). I've been the new guy on the bottom rung of the ladder, and a director at an ad agency. I don't think that I'm special or that I have it all figured out! I feel this comes from a culmination of experience, long hours, mistakes, and trying my best. I'll group some topics together and give some briefs on each: on being a web professional, building value/client relations, and yeah things don't always go well. Building skills in these areas may help boost performance in things like getting clients on board with ProcessWire, differentiating yourself from other web services, being confident in what you charge, and building both a great reputation and long-term relationships. Get on the bus, we're leaving our comfort zones. Here comes the disclaimer. How and where you work matters. The market you operate in matters, competition, local economy, etc. That's why I'm not going to say "here's how to come up with what you should charge" or "this one neat trick will change the game". These are my experiences, I think some of what I share will be things you can implement yourself, some may inspire you to think of something different, and other stuff might just be rubbish. I also think that knowledge unshared is knowledge lost- so please share your critiques, experiences, and suggestions below. Your opinion and experience matters! Onward! Note- my language will tend to lean towards freelancers and individual operators but if you're a formal employee this all 100% applies to you as well. Take a moment to envision the company you work for as your client. It becomes relevant very quickly. On being a web professional Being a web dev isn't easy. Taking anything you love (going to make an assumption there) and doing it professionally means doing what you like and slapping on a bunch of stuff you don't to make it work. It also means you need to up your game in places that involve things that "orbit" web development. It is always a good idea to break focus and bring in new skills that complement and increase your expertise. This is necessary so you can be more than a really good developer- you can become an expert. An expert professional proactively learns as much as possible, they can speak with authority in many areas related to their work through verifiable fact and with consideration for others and their needs. A professional works to be good at what they do but also has skills in multiple areas that allow them to do that. It means the difference between building a website and building an effective website. A professional values: Being a clear communicator. Always #1. Being an educator Being a solution Being a student Some of those seem obvious, so let's unpack 'em. Being a clear communicator means more than speaking well. It means conveying your ideas in a way that others can understand and appreciate. It also means working to understand others because that will affect what you say next. Communication requires translating your technical knowledge into layman's terms and rather than stop at stating a fact or good idea, following up with why. This helps others value things as much as you do and shows respect for others in that you believe that they deserve to know. You are a translator you modify your attitude and approach based on how other people are engaging you by being aware and paying attention. Being an educator means bringing others in on your knowledge. Education helps avoid arguments and shows that other people deserve to know more about what you do. I told a client the other day that there are a lot of people who do technical work that don't take the time to educate those around them, that means what you're doing is just a black box of mystery to them. How can they value what you do if they don't understand it? People have become comfortable with "well I'm not smart enough to do that" or "that's too complex for me", I start with the idea that everyone is capable and it is up to them to find their limits. We aren't teaching them how to program, but we should let other people in on things they might care about. This has to be done in a way that the person you are talking to feels like they are being elevated and not become embarrassed or have feelings that they are ignorant for not knowing something. Example: A client in a meeting started reviewing the mockup and picking it apart. "I don't like the color of that button, and why is it located there? Maybe we should move X above Y. I want the form over here not at the bottom." I addressed each of their concerns. I explained that the button is located there because we know from studying user behavior that it is more likely that people will engage. The color of the button is also a UI strategy, if you look across the entire site you'll see that buttons that do something we want them to do are the same color. The "Menu" button is the same color as the "Sign Up" button, we'll have encouraged interaction by normalizing the experience and by mentally associating that color with action. The background of the call to action form at the bottom of the page is the same color as the button- because it indicates an action that you should take. Being a solution means either providing the solution, or if it isn't possible, brainstorming an alternate option. My least favorite situation is the "yes or no" decision. I try to provide a "yes or yes", where we have two options that can address a concern- even if one isn't as good as the other, or isn't what someone had in mind- you have shown that you are here and ready to work on overcoming a challenge. That makes you and your client a team. A "yes or yes" situation means that you have "become the solution", because that's how people will think about what you bring to the table. This one is worth an example from an experience I just had the other day: The client puts together events and sells tickets online. It's a reasonably large event that means a lot of people buying tickets. A lot of people were buying at the last minute, tickets sell out, and people rushing to buy may need help under pressure, both mean unhappy customers. This means a big influx of customer service requests that are difficult to handle. So: "We are trying to think of a way of how to change the shopping cart system to fix this issue but not sure how." Yes/No - "We could work on that but it may be complex work and will be a challenge to solve the problem by the next ticket date. It's up to you whether that's worth it." Yes/Yes - "Yes it's certainly possible to work on the store, we could also consider having an email sign up form on the website and then send an email when tickets go on sale, then reminders so that it can make it easier on everyone to space out the purchases and prevent last minute pressure. I'm open to both, what do you think?" I worked on a Yes/Yes solution, and they felt a lot better, and they no longer have to worry about it. I also communicated and educated by pairing my suggestion with a clear "why it's a good idea", and then I invited their input after sharing. They're signing a retainer contract with me this week. Being a student seems obvious, but it means being an effective student. We learn how to code better and try new things but it's important to get out of your box. This allows you to be more of an authority on web overall and others will see that as something they can and should trust. It's easy to study what you like, but sometimes you gotta take the classes you don't like to graduate... Here are a few: Get to know how to design websites that work. Learn UI best practices, learn about what parts of web design actually drive conversions things like speed, the number of fields on a form, effective calls to action, the difference between a web developer and a web professional is knowing how to merge what you build with what it has to accomplish. I don't do SEO services, but I have taken a lot of time to study it and understand how my work can be the absolute best when it comes to future performance. It goes beyond semantic HTML. Be familiar with what Google ranks by, the things that matter, and understand what is more important than others. It will affect the structure of the site, it means you will be doing things like creating pages that you probably wouldn't think to. Be good at technicals! Sure you've got a good looking site, but have you truly taken time to study typography? Off the top of your head, do you know what the most effective width of paragraph text on a page leads to more reading by visitors? Do you know an effective length of copy? Learn human/web psychology. How do colors affect perception? What if animations aren't just for making things pretty and cool. etc. Read and study stuff from great people like Mike Monteiro's "Design is a Job" because it will open your eyes a lot to the work you should be doing to support the work you're already doing. Also go watch his video titled "F*ck you, pay me" (spoiler: bad words) Before every project I assume that web design changed and it's up to me to check in on the latest information. Google "web design best practices 2023" before designing, head over to Stitcher.io to check out the latest that PHP has to offer and great tips- using new features may save you time, make your code ready for the future, and keep from using something that will be deprecated (WHY IS MY SITE BROKEN? YOU JUST BUILT IT!). I'm serious. Every single project. I assume my knowledge is out of date in some way. Remember: being honest and saying "you know, I'm not sure but I'll research that and get back to you" or "I'm unfamiliar but that's not something I can't figure out" is a hallmark of a trustworthy professional. Do not be a snake oil salesman, do not lie to get the job, don't be a know-it-all. Learn to defer to others who have more experience than you, for me those are SEO specialists, SEM specialists, and sometimes graphic designers. This shows respect for the work others do and you may get some partners to work with in the future. I have SEO and SEM people I trust and can recommend at the drop of a hat. This also makes you a solution, because you have others ready to help you solve problems. If you get caught BSing your way through something, you won't get away with it. Eventually they'll find out and you won't work with them again. I know people who have dropped off the map because of their arrogance or inability to be humble, you know who gets the phone call for work? Me, not him- and some of the people I work with have known him longer. Building value/client relations If you've made it this far in this ridiculously long post, you'll guess why this section comes after being a web professional. Using communication, education, solutions, and a student are pretty much the only way you can build value. Building value validates your hourly rate, or the price for a project. Those skills create intangible value, "my web person is more expensive, but they're honest, open, and always works with me to get what we need done even when I'm not sure what to do." Be mindful that if you wish you could charge more, consider starting from the basics and build more value. Keep in mind that your clients have a job. They have a business to worry about, they probably need to go pick up their kids from school, payroll is due, and why are sales down this month?! Doing your best to make their experience working with you low stress and relieve them of extra work is invaluable. To build value and establish strong client relations you have to stop thinking like a developer and start thinking like a business- PLOT TWIST: not your business, your client's business. Your work needs to reflect that this is more than a website or web application, it's going to solve problems and be a net gain for their organization. You need to think as if you had an office down the hall, act as if their deadlines matter, through the way you work with clients they should feel like worrying about a website is off the table and they can get back to what their real job is. You know what matters to you? Their profits. What else matters? Their employees effectiveness, their customer satisfaction, etc. Your website/app may highly impact all of those things (and probably will). I had a manager tell me after a presentation for a new product page on a website "I was really impressed, you think like you own a business". Mission accomplished. My response? "I really appreciate the compliment, but it really is a culmination of the work you, me, and the rest of the team have done. I really appreciate you helping move the project forward." Most of the following about initial client meetings and writing a proposal is taken from my other comment so you can skip it if you've read it already. Building value starts from the first time you talk about a website. When we first meet I work to lead the conversation in a way that gets me as much information possible to do the best work I can, to let clients know that I'm here for the "big picture", and to get them to think about what their next website means for them and how much it's worth. In my initial meeting I type a lot of notes and I always discuss the following 7 topics: This may sound odd, but what does your business do? I've looked at your current website, but would like to hear that from you. (There's a chance they haven't updated their current website due to reasons we've talked about here, or that the content doesn't do a good enough job). What are your plans over the coming year? Do you plan on expanding to new locations? New products/services? (Not always, feel the client out to see if they're positioned for this- it also lets you think about whether you need to consider this for the site architecture/code) What do you want visitors to do when they get to your website? What does success look like? Filling a form? Calling? Getting them to your online shop? Visiting your location in-person? Stick around for ad revenue? Explain that websites are supposed to do things with purpose. Open up the conversation about how a website is not a brochure, it's a live business asset that establishes brand strength, is often first impression of a business, and because we just discussed what conversions look like in the previous question- a tool that works 24/7 on their behalf. What do you like about your current site? What don't you like about your current site? Is there something that you want your website to do now that it doesn't now? Get everyone in the room to think about how this isn't a replacement website, we're leveling-up here and it's time to get strategically creative. You'll see that all requires communication, education, solutions, and a being a student. After this, I talk about my values and what is important to me when working with clients. Think about the typical idea that "programmers are hard to work with" or "the IT guy is a jerk and is abrasive". We need to overcome that with new potential clients because it's safe to assume that they've had some not-great experiences. I talk with them about these things that matter most to me: Communication. Always #1. They already know that I've taking the time to communicate, now they know that it's my priority and it makes them realize how much we've been talking about them. This is another thing that I can almost guarantee has not been said to them. Ownership. Businesses/people should own their properties and assets. True ownership means being able to use and update it. If there are services along the way (like maybe Google Tag Manager) or a new hosting company then it should be an account their business owns and I'll guide them through any process to do so. If I were to be unavailable someday- you won't be stuck with a website that was dependent on me. Performance. Re-iterating the need to aim for conversions. Adhering to accessibility rules for legal and user friendliness reasons. Making the site 100% (like Google Lighthouse 100%) SEO ready when it comes time for a specialist to optimize. These questions almost never fail to get potential clients to go wide-open with you. Every single thing we have talked about has now built value and price follows value. This is where we end the first meeting and I tell them that I want to review my notes and put a proposal (I never call it a "quote") together for them and that we can meet/call next. This first meeting usually ends up taking 1-1.5 hours, an initial meeting I had last week took 2. My proposals run 2+ pages. The opening paragraph is the introduction to the project and the approach- if they said they want to expand then the website "will be built with your goals for expansion in mind". The next are bullet points that now re-iterate much of what we talked about in our initial meeting (the 7 questions). Next I talk about timing, to head off "how long will it take"- that this project is a collaboration where we depend on each other to get content, approve designs, consider the features that the website is going to have, etc. We're all in this together, and I am invested in this with them. You're now thinking about their concerns before their questions and you've worked to control that conversation. If I'm asked for more detail, I say that we will set timelines for each stage when we get started- so when design starts the timeline is X, when that is approved we set the next timeline, etc. Then comes the price but I close with the terms of payment. It's usually 40% to get started, 40% on design approval when we go to code, and 20% on launch. Now the client is looking at the price in terms of affordability over the coming X amount of time. Here's what this process has done: my language says "I'm an agency" not "a person who does websites". The client has probably talked to me more than they ever have to other web people. They've shared the good and bad, we have a relationship, and now you have absolutely everything you need to do your best work and they know that. They probably also haven't encountered someone who wants the website to achieve conversions. They now know me as an expert. When you establish relationships with clients consider these items: Don't talk about other clients too much unless you're building rapport and talking about wins before they sign a contract. After that it's like a husband talking to his wife about his girlfriend. You can break this rule if you're talking about a success they've had that you want this client to have and correlate- because you're making it about the client you're talking to. Don't let a client feel like you're too busy or rushed. If an existing or past client needs help with something and it's small- consider what's worth more to you: the money you can bill, or the value of a client relationship. Only consider this if it's small and you can afford it- there is never anything wrong with billing a client. Be selective of who you do this with, don't do it often. Don't create a pattern and expectations. If you do this, be explicitly clear in an email (document it) with something like "I took care of that issue. This on is on the house so we'll skip the bill on this one. Let me know if you need anything else!" Make sure these are small, it makes a client feel like you aren't nickle and diming them. Do not overdo this. Don't do this to win over a bad client. Keep it for your best customers. Yeah, things don't always go well If you're still reading this ridiculously long post... Sh!t happens. You miss a deadline, you got a flat tire, your dog ate your homework. You misconfigured someones DNS and took their site down. Whatever. I would argue that if something goes wrong it's almost always the best opportunity to show how good you are. The only thing that people remember more than a problem is how you handled it. Be honest about it, admit fault, own it. If it was due to an error on someone else's part, I generally try to stay tame on blame and explain what went wrong rather than who did it- unless it was a) something egregious, or b) the person that did web work for them previously. You're not trying to dunk on them (be diplomatic) but you are there to solve problems and making sure they understand why you're now working with them and the other person isn't can be positive. Did a potential client reject your proposal (surely you aren't calling them quotes, right?) Try to negotiate if you're in a position to do it. If you've built value and it really does come down to cost, then look to have the value you've built help in negotiating. Etc. Random stuff I don't send invoices, my billing platform does. Built in document emailing, automated reminders for late clients, time tracking, reporting, expense tracking, customer portals, online payments through Stripe (and others), customizable branding, etc. If you don't have one, check out InvoiceNinja. It's open-source, built on Laravel, has an API, and you know how to put stuff on a web server, right? 😎 Create a subdomain like billing.yourdomain.com and it's pretty much ready to go. An update email describing less progress on a project than a client may want to hear is better than saying the same thing after they had to ask because they haven't heard from you. Keep communication open and going, let them know that you are thinking about them. Consider offering web hosting to clients when you feel like it's a good fit. Easy and inexpensive, I use StatusCake to monitor all the sites I host so I know if anything goes wrong. So my hosting is "private, exclusively for my clients, and monitored 24/7" Spin up a VPS at a reputable place. For managed hosting I use Dreamhost, for unmanaged Digital Ocean. I have a Dreamhost VPS and a separate managed MySQL DB VPS It's not really an "income source", you don't have to charge much, but it pays for all of my hosting and domain fees (including for my billing platform). They're happy, I'm happy. Very convenient to work on client sites when you don't have to juggle passwords for GoDaddy or some service they've set up. Should you be a company or a freelancer? Up to you. Historically I was a freelancer, then this year I formed an LLC. You don't have to do it but there are benefits Professional image, taxes, a more formal business interaction, legal protections Shows my clients that I'm as serious about what I do for my clients as they are. Consider creating a "marketing deck" Agencies create these to display their services and offerings. They're usually tailored to a specific client, but having a general one to provide people I'm interested in working with is a great professional document. Make it look nice, you don't have to print it, just create a PDF to have and mail. What else can you do? Can you offer more services beyond websites? I provide "web services", so design/build new websites, update/maintain/optimize existing websites Offer building integrations between platforms, provide CRM work where needed if possible Analyze business practices to help optimize workflows Research and provide recommendations for software that solves problems Help manage existing platforms, and train others to use them Think about how a developer gives you the tools for analysis, problem solving, and fast learning. You may learn software faster than other people- I've offered to learn software and then teach it to people who already use it. Think about what you can do that solves a problem while at the same time lowering the bandwidth for employees at a company. This went on waaaaay longer than I thought it would but I just shared whatever came to mind. If you are interested in seeing the deck I put together for my services (and have a reputable account with a history here in the forums) PM me. I don't want to share it in the open since it contains PII. If you found any of this useful, shout out to @kongondo for encouraging me to share. Would love to hear everyone's thoughts, critiques, and suggestions! As always, I am 100% open to being wrong about things that someone else could help me be right about.
    26 points
  41. Happy New Year! This week we've got ProcessWire 3.0.209 released on the dev branch. Consider this version a release candidate (RC1) for the master/main branch. Relative to 3.0.208, this version contains primarily issue resolutions and minor improvements (commit log). If you have a chance to test it, please let us know if you run into any major issues by reporting them here. So long as no new major issues surface, by this time next week my hope is that we'll have the dev branch merged to the master/main branch for version 3.0.210. This week one of my clients pointed me to https://chat.openai.com/ which is really quite interesting. It's an artificial intelligence that you can chat with, and it knows quite a lot about ProcessWire and how to code in ProcessWire. Though it knows a lot about a lot of different APIs, and actually a lot about everything, not just development related stuff. But I mention it here because of how it appears it's been trained fairly well in ProcessWire. When I asked it for examples of what CMSs it knows how to code for it told me WordPress, Joomla, Drupal and ProcessWire. I'd encourage you to get an OpenAI account to try it out, it can blow your mind. Ask it questions like how to develop a ProcessWire module, how to build a search engine in ProcessWire, how to use pagination in ProcessWire, or anything you can think of. It seems to have an answer for everything. The client that told me about it is currently using it to write AWS scripts for him, and he said it's saving him a lot of development time. What I also find interesting is not just how it seems to have an answer for everything, and that it knows how to code, but also that it makes mistakes pretty regularly, and recognizes those mistakes when you point them out... and apparently learns from them. That's basically how we work. For example, I asked it how to resize an image in ProcessWire and it explained through text and code examples how to use the size() method on image fields. But it also told me that I could use the $options argument to specify the type of resize interpolation (bicubic, linear, nearest, etc.) and ProcessWire has no such option. When I told it that, it apologized and corrected its code example, and also explained more details about the size() method's $options argument. There was another instance where it made a mistake (trying to do a PHP math operation within a string) and I explained that PHP doesn't support that. It essentially said "I know that, but my example doesn't do that". Then I asked it to double check its example because it didn't look right to me, and then it replied (paraphrased) "Oops, sorry, you are right, here's a corrected version." Anyway, it's just kind of funny to me that I'm talking to a computer with these kinds of interactions, and how it seems to "think" and learn a lot like we do. It will be interesting to see where this technology leads. Currently it is a free research preview, where I suppose we are helping to test the AI and help it learn, but it's very interesting and engaging just to talk with it. Also worth noting is that it does not have internet access (other than to chat with people). So it can't go and hunt down information from websites, actually making it that much more impressive in my mind.
    26 points
  42. ProcessWire 3.0.196 contains 10 minor issue fixes, but the biggest additions this week are 6 pull requests submitted from @bernhard. This most notable PR is an upgrade to AdminThemeUikit that enables greater customization of the theme's markup. If you recall, Bernhard also provided a PR last year that enables customization of the admin theme's CSS/LESS, so now he's now completed the circle and provided us with a direction for customizing the markup as well. This newest addition lets you provide your own custom render files for different parts of the admin theme by placing them in /site/templates/AdminThemeUikit/. Third party modules can also define custom render files. Furthermore, this addition adds a new hook that enables you (or your modules) to hook into and modify just about any part of the admin theme rendering. Rather than going into all the details and repeating all of the instructions here, see the new AdminThemeUikit README file "Customizing Markup" section which describes all of the different things you can modify and how to do so. You can also find all of the render files in the core AdminThemeUikit directory — any .php files beginning with an underscore are custom render files that can be overridden. While not as broad in scope, there were other useful PRs added this week as well, so be sure to see the dev branch commit log if you are interested in seeing what else was added. Thanks for reading and have a great weekend.
    26 points
  43. As we continue to work towards the next master version, this week I've been working on fixing reported issues. A new $sanitizer->words() method was also added which reduces a string to contain just words without punctuation and such. It was added in part to work on an issue reported with the tags feature in the field editor, but should be handy for other cases as well. As part of that update, the existing $sanitizer->word() (singular) method was re-written to support the features needed for the new words() plural method. This week I've also been working on a pull request from Bernhard that enables greater customization of AdminThemeUikit by way of custom render files and hooks. I'm bringing in that PR and it has a lot of good ideas that have inspired some related updates to it. I've got a bit more work and testing to do before committing, but that part should be ready early next week, along with more core updates. Thanks for reading and have a good weekend!
    26 points
  44. https://www.kaumberg.gv.at/ Hello everybody! Today I want to share a project that I've been working on for a year or so: The new website for municipal Kaumberg - a beautiful village near Vienna. To be honest, this project was way more work than I initially expected. The site has tons of content and the client came with lots of good ideas during the process of building this site. For example after we launched the site several organisations of the village realized that the system works great and is easy to use, so they wanted their own section with their own color scheme... Right now the system is maintained by several user accounts that populate content to the site. Some of them only in the news-section, others are allowed to publish to the sub-sites (eg for the fire department). Recently we won the 2nd price out of 190 cities in lower austria - where Kaumberg was by far the smallest one also having the smallest budget of the top rated cities! ### NOTE ### This system was built with scalability in mind. If you know any other municipals (preferable in a german speaking country) that could possibly need a new website using my setup write me a PM. If you are a marketing guru and know how we can sell this product to 100s of municipals, let me know. I know how to do that from the technical point of view ? ### HIGHLIGHTS ### CONTENT Custom multi-level menu for managing loads of content pages that also works well with keyboard navigation ? --- EVENT MANAGEMENT Events are managed via the PW backend and presented as calendar on the website plus can be downloaded as PDF calendar in A3 format for printing: --- CONTENT BLOCKS I've developed a new (private) module called RockMatrix for versatile, easy and fool-proof content creation for this project. This setup ensures that even non-tech-savvy users can create content that looks nice and works on all devices from desktop to mobile: --- SITE SEARCH Another new module that was developed for this project is RockSearch. I hate site searches that do work on the first sight but do not on the second. For example if they show older results on top of newer ones. Or if they do not index content that is not stored within a regular text field but for example as image description. On the other hand I did not want to use ElasticSearch because it seemed to be overhead to send data to another service that is already stored in the database of my PW installation... RockSearch shows results based on different weighing mechanisms, for exampe a search for the garbage collection schedule first shows results that are nearer to the current date than others (both future and past). That means that a date two days in the future will be shown above one that is 10 days in the past. Also results get different score based on where the term was found - matches in the site title get higher scores than matches in the body or in image descriptions or the like. Each content block is a RockMatrix block that has a render() method to define the output on the website and - for RockSearch - has a method called "searchIndex()" that defines the content that is written to the search index that is queried for every search request: This setup makes it super easy to add new content elements and to add them to the search index ? --- SUB-SITES The client can create sub-sites for different organisations of the village having custom color schemes and managing user access: News can be tagged to show up on different areas of the website: --- OTHER The site uses no cookies and can therefore be used without an annoying cookie banner. Page hit statistics are gathered by PageHitCounter and shown by RockHitCounter Some other little features are short-links for social media (eg https://www.kaumberg.gv.at/goto-1027 ) or the possibility for creating custom subdomains for sub-sites like http://araburg.kaumberg.at/ I'm quite sure I forgot lots of great stuff, but I hope you enjoyed reading this article nevertheless ? Looking forward to your feedback!
    26 points
  45. All the updates I mentioned last week are committed on the dev branch today, and it's quite a lot of new code and revised code. There's enough here to justify a couple of version bumps, but I'm leaving it at 3.0.185 temporarily because the ProcessWireUpgrade module will see the version change and some people might upgrade automatically. Because there's so much that's been rewritten, refactored, added and changed here, I'd like it to marinate for a week on the dev branch before it triggers any upgrade notifications. Our dev branch tends to be very stable, and this is one week where there's enough new code that I don't want to call it as-stable-as-usual just yet (though it might very well be). In addition, it's also not totally finished as there's still some redundant code to be removed and stuff that needs to be moved around a bit, though everything should be fully functional as-is. I am running it here on the processwire.com site without any issues, so chances are that all is good, but I always like to be cautious. Below is a summary of what's new and changed. It's a bit technical, and there's nothing here that's all that important to know about, but whether you read on or not, just know that the core is getting even better and faster with updates like these. The core PagePaths module has been refactored and has multi-language support. Previously it only worked on single-language sites. Now multi-language sites can take advantage of potentially significant performance improvements offered by this module. This is one module that is not installed by default, but it's definitely worth installing: Modules > Core > PagePaths. The core LanguageSupportPageNames module has been refactored. Assuming I did it well, you shouldn't notice any difference other than that it's faster. For instance, one of the changes is that it drops its old indexes on the pages table and re-creates them in a different way. I discovered some situations where finding pages by multi-language path was resulting in full pages table scans. Using MySQL “explain” statements I found I could prevent that by reversing the order of the columns in the language-specific indexes, preventing the full table scan and making it many times faster. This could make a big difference on large multi-language sites, but is less likely to be noticed on smaller sites. The LanguageSupportPageNames module also gained a new configuration setting that lets you specify what should happen when a page is accessed at a language it's not available in: throw a 404 or redirect to the default language. The core PagePathHistory module has been refactored. It has a powerful new getPathInfo() method, though it's primarily of interest for internal core use. A new PagesRequest class has been added to the core and it can be accessed from $pages->request(). It primarily focuses on one hookable method: $pages->request()->getPage(). The method accepts no arguments but it analyzes the current request, identifies the page to render, and returns it. Previously this logic was in the ProcessPageView module. The benefit of this method is that you can now determine the page to render much earlier, like during the boot process. Previously the current page wasn't identified until after PW booted and the ProcessPageView module loaded. This will enable [for example] a module such as SessionAllow to decide whether or not to allow sessions based on what page was requested. In addition, this method is hookable, so you could have a module that adds its own logic to identify or change the page that should be rendered. A new PagesPathFinder class has been added to the core and can be accessed from $pages->pathFinder(). Like the PagesRequest class, this new class primarily focuses on one method: $pages->pathFinder()->get($path). The given $path can contain any page path, optionally containing language prefix, URL segments, pagination numbers, or even a previously named version of the path. It will find and validate that path and return an array of information about it. PagesPathFinder does its job very quickly. In fact, it's about 10 times faster (in testing/timing locally) than PW could previously identify this information using existing methods. PagesPathFinder will also take advantage of the PagePaths and/or PagePathHistory modules when appropriate, if they are installed. Below is an example of what the return value would be for path /es/hello/bar/baz/page3 where /es/ is the language prefix, /hello/ is the actual page, /bar/baz/ are the URL segments and /page3 is the pagination number segment. It analyzes everything out and returns a 200 "ok" response if the path is good, or in this case, it's returning a redirect because it detected a Spanish URL with English page name and pagination prefix, and it's suggesting a redirect: $info = $pages->pathFinder()->get('/es/hello/bar/baz/page3'); [ 'request' => '/es/hello/bar/baz/page3', // path that was requested 'response' => 301, // one of 200 (ok), 301 (permRedirect), 302 (tempRedirect), 404 (pageNotFound), 414 (pathTooLong) 'type' => 'permRedirect', // ok, permRedirect, tempRedirect, pagePathError, pageNotFound or pathTooLong 'errors' => [], // array of error messages indexed by error name 'redirect' => '/es/hola/bar/baz/pagina3', // suggested path to redirect to, blank otherwise 'page' => [ // info about page that was matched 'id' => 1237, 'parent_id' => 1232, 'templates_id' => 12, 'status' => 1, 'name' => 'hello' ], 'language' => [ 'name' => 'spanish', // name of language detected 'segment' => 'es', // segment prefix in path (if any) 'status' => 1 // language status where 1 is on, 0 is off ], 'parts' => [ // all the parts of the path identified [ 'type' => 'language', 'value' => 'es', 'language' => 'spanish' ], [ 'type' => 'pageName', 'value' => 'hello', 'language' => 'default' ], [ 'type' => 'urlSegment', 'value' => 'bar', 'language' => '' ], [ 'type' => 'urlSegment', 'value' => 'baz', 'language' => '' ], [ 'type' => 'pageNum', 'value' => 'page3', 'language' => 'default' ] ], 'urlSegments' => [ // URL segments identified in order 'bar', 'baz' ], 'urlSegmentStr' => 'bar/baz', // URL segment string 'pageNum' => 3, // requested pagination number 'pageNumPrefix' => 'page', // prefix used in pagination number 'scheme' => 'https', // blank if no scheme required, https or http if one of those is required 'method' => 'pagesRow', // method(s) that were used to find the page ] While you might never need to use this method yourself, ProcessWire now uses it on every request. And now that this logic is isolated from ProcessPageView and has a dedicated $pages API, it's much more open and we can, for example, more easily apply tests to this logic. I've covered a few updates here but there's more if you feel like digging in the commit log https://github.com/processwire/processwire/commits/dev. Thanks for reading and have a great weekend!
    26 points
  46. ProcessWire 3.0.219 on the dev branch focuses primarily on a major overhaul to the core $modules system. The Modules class had grown into more than 5000 lines of code — all related to modules, but with lots of different areas of focus within that. It had become a little bit messy, fragile and hard to maintain at that size. I've had @todo notes in the class to "clean it up" for quite awhile, but this week I finally got to it. The approach is similar to that of our $pages API (Pages class), which is split into separate classes for page finding, loading, editing, caching, etc. The Modules class has been split into several much more focused classes for module information, installers, loaders, files, flags, duplicates, and configurations. This leaves the main Modules class as the gatekeeper, making it less fragile and easier to maintain. If this overhaul was perfect, you shouldn't notice any difference, and the public $modules API remains as before. But with such significant overhaul that took a full week to complete, there's also an increased potential for temporary errors, so please let me know if you encounter any. Thanks for reading and have a great weekend!
    25 points
  47. This week we've got a new core version on the dev branch, version 3.0.208. This version includes 15 new commits with a combination of issue resolutions and new features. See the dev branch commit log for full details. In addition, we have new versions of the following ProFields modules: FieldtypeTable, FieldtypeTextareas, FieldtypeMultiplier and FieldtypeFunctional. (Last week a new version of FieldtypeCombo was released as well). These are all posted in the ProFields downloads thread. These versions all add support for the new InputfieldTinyMCE module, in addition to other regular improvements and updates. I think that completes the list of modules I've developed that needed an update to support InputfieldTinyMCE. Speaking of InputfieldTinyMCE, it also received updates this week, primarily focused on resolving reported issues. Thanks for reading and have a great weekend!
    25 points
  48. I figure this is an off topic board among friends here (like our pub, or a real one) and it’s okay to write with honesty and engage in spirited or sometimes heated conversation. So just want to be clear that I like and respect @MoritzLost and value his opinions, even if I don’t agree with all of them. We will all have a diversity of views and everyone should feel free to share them, and likewise others should feel free to question those or state their own, which is what I’m trying to do. But I’m glad MoritzLost shares his opinions here. Plus, a little controversy also helps to engage the community, and drags me out of the code for a bit, so I can’t fault anyone for that. Once I can state my thoughts, I carry no concerns, but thought I should defend the things that deserved defending, as I always should. As a caretaker of this project, part of my responsibility to add my honest point of view to a conversation like this, especially when something is posted where I don’t fully agree. I think many in our community know the subjectivity of statements here (whether OP’s, mine, or another’s), but an equal number may not, as this is a public board. So some counterpoint or back-and-forth is not only clarifying to the larger audience, but also useful, as we explore different facets of it all, and lead to the most important takeaways. I appreciate that MoritzLost continues to defend his point of view on parts, despite my own defense or heated questioning of those parts. It helps me to better understand and know which are the parts that he thinks are most important and deserve the attention. We obviously don’t have the resources to pursue everything Craft does, so narrowing in on the most important parts—the meat of it—is helpful here. I’m traveling right now so can’t participate as much as I’d like to at the moment but just didn’t want MoritzLost or anyone else to think I was angry or something. Likewise I didn’t want anyone else to think they had to moderate themselves either. When I’m among friends I feel comfortable to express and be honest, and so should you. If you get a heated reply from me it’s because I care about it and value your opinion. I’ll spend more time reading everything when I get back, but I can see already it’s a good and helpful discussion.
    25 points
  49. This week we have some very useful new additions to both the core Repeater Fieldtype and the ProFields Repeater Matrix Fieldtype. This post covers all the details along with a couple of brief demonstration videos— https://processwire.com/blog/posts/new-repeater-and-repeater-matrix-features/
    25 points
  50. It took a little more time this week to wrap up what I was finishing last week (and the week[s] before). But I think it's now at a good spot to move on to something else, and so I started with some Repeater and RepeaterMatrix updates. In Repeater, a new feature was added that's been requested for awhile: the ability to add items anywhere you choose. Previously you could do it only by adding items to the bottom, and then drag them in place. Now you can click insert before/after [icons] in each repeater item header and it will add the new item in place. It is also depth-aware. This is something that I think will also be useful especially for people using repeaters for page builder type contexts. (Note: the feature does not [yet] work if you have all Ajax features turned OFF in your repeater settings). There's a GIF screencast below that shows you how it works. You can click the "insert before" or "insert after" actions and it inserts a new item in place. Alternatively, if you hover either action for a second, it'll show you where it's going to insert an item and you can click either the the item, or the action, to complete the insertion. There's still likely some optimizations and improvements to make in the JS here but so far it seems to be working well. (I made this as a GIF but for some reason IP.Board won't accept it, so here's a YouTube embed instead): Everything in Repeater also gets inherited by RepeaterMatrix, so you'll find this feature there too. But it's not fully functional there just yet. That's because RepeaterMatrix items also have a "type", so I'm working on a new version of RepeaterMatrix that lets you select a type once you've chosen where to insert the item. Another related feature in progress in RepeaterMatrix at the same time is a dropdown/select option for choosing what matrix type you want to add for items. This is an alternative to the current list of links that appears at the bottom. The dropdown also gives you the option of having groups of related types. More on that soon, potentially next week. Thanks for reading and have a great weekend.
    25 points
×
×
  • Create New...