Leaderboard
Popular Content
Showing content with the highest reputation on 03/01/2025 in all areas
-
Big thanks to everyone that shared your favorite ProcessWire features last week, it was very helpful for the new ‘features’ section of the website. Speaking of the website, I’ve been continuing to work on that this week, and was primarily focused on the modules directory. I’ve got plenty more to do there, but making good progress. The website is going to be the focus of the next few weeks, with some core updates along the way. This week the core updates were a few issue fixes on the dev branch, with more on the way next week. Thanks for reading and have a great weekend!9 points
-
We've been working on a project in ProcessWire for the x time, and the more we use it, the more amazed we are by what this incredible CMS can do. From an SEO, developer, usability, and customization perspective, it’s truly outstanding. My team was deeply involved with Joomla! for 10 years - since its foundation - so we’ve seen a lot. After years of using ProcessWire, I just want to thank @ryan and everyone who has contributed - whether through code, ideas, support, or anything else. What a beauty, what a powerful CMS! 🚀2 points
-
I resonate very deeply with this, especially in the last 2 years where I'm using ProcessWire as a web application framework. Maybe it's my impatience of having to write migration files or the fact that I'm usually a team of one, but modeling an app in this way and getting an admin interface "for free" with everything interconnected is peak productivity. I look at ProcessWire very differently as of 2 years ago. In 2006/7, not long after I decided to get into website development, I gravitated towards Ruby On Rails (which has a special place in my heart even though I haven't used it in over a decade). However given my lack of experience with programming in general at that time (I was more of a "hacker") and the fact that a web application framework lends itself to complex applications, OOP, software engineering, etc., it was too early for me to pursue that line of work, so I went down the CMS path and eventually found PW in ~2012 after searching for an alternative for WordPress for a few years. Two years ago, I had the opportunity to re-write an internal order and production system (a true web application... no frontend, purely admin) and I had to make a decision... should I write this in a web application framework like Rails/Laravel or can I actually do this in ProcessWire in the "ProcessWire Way"? This forced me to look at ProcessWire in completely differently and to make a long story short, I've proven it to myself, on a deep level, that ProcessWire is a very capable web application framework as well. Realizing and proving this to myself with this system I've developed is liberating because for me, I can use one system to do two very different types of projects.2 points
-
Hey @neophron thx for the update. You have old ->styles() and ->scripts() calls in that file that you have to remove. I tried to explain what to do and the reason for the breaking change as good as I can here: https://www.baumrock.com/en/processwire/modules/rockfrontend/docs/asset-tools/ I'm also updating the rockfrontend site profile that you can use for inspiration. Does that help?1 point
-
In this tutorial I want to show you how to set up a simple ajax-driven auto-complete search within ProcessWire. Something that looks like this: Requirements: 1. Use the Fieldtype Cache to create a search index For fast and easy search queries we will create a search index field. You can read more on this here: https://processwire.recipes/recipes/set-up-search-index-with-fieldtypecache/ First of all, go to Modules and install a Core module called FieldtypeCache. Add a new field “search_cache” and select “Cache” as it’s type. Save the field. On the Details tab choose the fields you want to make searchable. Save again. Add “search_cache” to all templates you want to include in your search results. Optional but recommended: use the “Regenerate Cache” option found from the Details tab of the field to make existing content instantly searchable. In my case i only want to search inside body and title fields, so I included those fields in the settings of the search cache field: 2. Install the Pages2JSON module We want to make ajax requests to the search template and those results should be returned in JSON format so that our java script can process the results inside the auto-complete dropdown. Therefore we make use of this module here: https://processwire.com/modules/pages2-json/ In the module settings we define what data will be included in the JSON object that is returned. Remember that this is the data that we want to display in our auto-complete dropdown. So for my case I only want the title and the url field. Now let's start: 1. Setting up the search template Set up a template file called „search“. Then create a page using this template. On most ProcessWire installations this is already the case and this template exits in the file system. This template will handle the search queries and list all the results for us and that in two ways: - you can send a search query and the search result page will list all the results (as you would expect) - you can send an ajax search query and the result page will return the results as a json object to be processed within javascript and displayed in the frontend in real-time search.php: <?php if($config->ajax) { // Return search results in JSON format $q = $sanitizer->selectorValue($input->get->q); $results = $pages->find('search_cache%=' . $q);; // Find all pages and save as $results header("Content-type: application/json"); // Set header to JSON echo $results->toJSON(); // Output the results as JSON via the toJSON function return $this->halt(); } ?> <main> <div class="uk-container"> <?php // look for a GET variable named 'q' and sanitize it $q = $sanitizer->selectorValue($input->get->q); // did $q have anything in it? if($q) { // Find pages that match the selector $matches = $pages->find('search_cache%=' . $q); // did we find any matches? ... if($matches->count) { echo "<h2>We found $matches->count results:</h2>"; echo "<ul class='uk-list uk-list-square'>"; foreach($matches as $match) { echo "<li><a href='$match->url'>$match->title</a>"; echo "<div class='summary'>$match->summary</div></li>"; } echo "</ul>"; } else { ?> <h2>No results found.</h2> <div uk-grid class="uk-flex uk-flex-center"> <div class="uk-width-1-1 uk-width-1-2@m"> <?= $files->render('elements/_searchbox'); ?> </div> </div> <? } } else { ?> <h2>Search:</h2> <div uk-grid class="uk-flex uk-flex-center"> <div class="uk-width-1-1 uk-width-1-2@m"> <?= $files->render('elements/_searchbox'); ?> </div> </div> <? } ?> </div> </main> Explanation: This part here at the top of the template handles the requests that are send via ajax. This is the important part for later on. <?php if($config->ajax) { // Return search results in JSON format $q = $sanitizer->selectorValue($input->get->q); $results = $pages->find('search_cache%=' . $q);; // Find all pages and save as $results header("Content-type: application/json"); // Set header to JSON echo $results->toJSON(); // Output the results as JSON via the toJSON function return $this->halt(); } ?> What this does: check if the current request is an ajax request if so, search inside the search cache field return the results in JSON format then quit processing the rest of the template (we don’t want to render any markup in that case!) Everything below this part is the normal search template logic. If you send a search request via a form somewhere on the website you want to be redirected to the result page and all the results will be listed on the page just like you would expect. 2. Make the search „work“ Create a template file called "_searchbox.php" (this file is also included in the search template code above, so adjust your paths/names accordingly). To make search requests you want to include this search form anywhere on your page. _searchbox.php: <div class="search-wrapper uk-padding uk-text-center"> <h4>Searchbox</h4> <form class="searchform uk-position-relative uk-flex" method="get" action="<?= $pages->get("template=search")->url ?>"> <input class="uk-input" id="searchInput" name="q" type="search" aria-label="Suchen" autocomplete="off" placeholder="z.B. Bagger"> <button class="uk-button uk-button-primary search-button" type="submit"> <span class="uk-visible@s">Search</span> </button> <div id="suggestions" class="uk-box-shadow-medium"> </div> </form> </div> With this simple search form you should now be able to do a basic search that leads you to a search result page. 3. Make the ajax-search „work“ Now comes the interesting part. We will add in a java script snippet into our just created _searchbox.php that sends ajax requests to the search template page while we are typing into the search intput field and it will display the results in a nice little dropdown. Feel free to adjust the code to your needs! <script> document.addEventListener("DOMContentLoaded", () => { const searchForm = document.querySelector('.searchform'); const searchUrl = searchForm.getAttribute('action'); const searchInput = document.getElementById('searchInput'); const suggestionsDiv = document.getElementById('suggestions'); let selectedSuggestionIndex = -1; // close the auto-complete container when clicked outside the element document.addEventListener('click', function(event) { if (!suggestionsDiv.contains(event.target)) { suggestionsDiv.style.display = 'none'; } }); searchInput.addEventListener('input', () => { const searchText = searchInput.value.toLowerCase(); // Immediately Invoked Function Expression (IIFE) (async () => { try { const response = await fetch(searchUrl+"?q="+searchText, { headers: { 'X-Requested-With': 'XMLHttpRequest' } }); if (response.status === 200) { const data = await response.json(); showResults(data); } else { console.log(response.status); console.log(response.statusText); } } catch (error) { console.error(error); } })(); function showResults(data) { // Show suggestions only if the input has at least two characters if (searchText.length >= 2) { const suggestionHTML = data.map(item => { // Highlight the matching characters using a <span> element with a CSS class const highlightedTitle = item.title.replace( new RegExp(searchText, 'gi'), match => `<span class="highlight">${match}</span>` ); return `<li class="suggestion"><a href="${item.url}">${highlightedTitle}</a></li>`; }).join(''); // Array to string conversion suggestionsDiv.innerHTML = ''; // Clear the suggestions if input length is less than two characters // create list and append search results const suggestionList = document.createElement("ul"); suggestionList.classList.add('suggestion-list', 'uk-list'); suggestionList.innerHTML = suggestionHTML; suggestionsDiv.appendChild(suggestionList); selectedSuggestionIndex = -1; // show the results suggestionsDiv.style.display = "block"; } else { suggestionsDiv.innerHTML = ''; // Clear the suggestions if input length is less than two characters } } }); // Event listener for arrow key presses searchInput.addEventListener("keydown", function (event) { const suggestions = document.querySelectorAll(".suggestion"); if (event.key === "ArrowDown") { event.preventDefault(); selectedSuggestionIndex = Math.min( selectedSuggestionIndex + 1, suggestions.length - 1 ); } else if (event.key === "ArrowUp") { event.preventDefault(); selectedSuggestionIndex = Math.max(selectedSuggestionIndex - 1, -1); } else if (event.key === "Enter") { event.preventDefault(); if (selectedSuggestionIndex === -1) { searchForm.submit(); } else { window.location = suggestions[selectedSuggestionIndex].querySelector('a').href; } } // Highlight the selected suggestion suggestions.forEach((suggestion, index) => { suggestion.classList.toggle( "selected", index === selectedSuggestionIndex ); }); }); }); </script> Keep in mind that you need some CSS styes to make it look good and actually work. These are my styles(in LESS format) but feel free to write you own stylesheet for this. search.less: .search-wrapper { position: relative; background: @secondary-blue; h4 { color: @primary-blue; } .highlight { color: @primary-red; } } #suggestions { display: none; position: absolute; top: 100%; left: 0; right: 0; z-index: 10; background: @white; .suggestion-list { margin: 0; li { transition: background-color 150ms ease-in-out; a { padding: 10px; display: block; text-align: left; &:hover, &:focus, &:active { text-decoration: none; } } &:hover, &:focus, &:active, &.selected { background: @secondary-blue; } } li + li { border-top: 1px solid @secondary-blue; } } } That's it! Again feel free to adjust all of the code to your needs. If you have any suggestions how to achieve things a bit easier just let me know.1 point
-
@ryan's ForgotPassword module has been in PW since Day Dot. It does it's job BUT it's onerous, especially for Frontend Users, eg those using LoginRegisterPro. My client, quite reasonably, wanted the FE users to be logged in automatically after a successful password reset. The module doesn't have many hooks. Here's one solution to automatically logging in frontend users after a successful password reset. In site/ready.php: // Force login after successful password reset $this->addHookAfter('ProcessForgotPassword::renderMessage', function(HookEvent $event) { $msg = $event->arguments(0); $input = wire('input'); $session = wire('session'); $pages = wire('pages'); $page = wire('page'); $users = wire('users'); $return = $event->return; // This uses the module's default text. // Adapt to suit on multi-language sites or if you want it to work for admins too if(stripos($msg, 'success') !== false && $page->template->name !== 'admin') { $userid = $input->get('u', 'int'); $user = $users->get($userid); // limit access to specific users or roles if(!$user->isSuperuser() && $user->hasRole('login-register')) { $session->forceLogin($user); $session->redirect($pages->get("name=mypage")->url); } } $event->return = $return; });1 point
-
1 point
-
Hi, after the recent update to 0.5.0 I cannot log out from the backend anymore, I get this error message on the logout page: "Tag parameter is missing" and I remain logged in. Double checked: Uninstalling: log out works. Re-installing: log out works not. Can you confirm this?1 point
-
For me the following features (in no particular order) set ProcessWire apart from other systems: Easy core upgrades, just replace the wire folder and you are good to go. Low (zero) maintenance costs for core and plugins (with WordPress you need to watch plugins and core every week or day and update them) Free and Open Source Custom fields in core Easy to learn API Multi-language out of the box (which needs support for multi-language images, but that is another topic. I also know they are possible via ugly workarounds) The welcoming and helpful community Custom modules that modify ProcessWire or add new functionality without modifying the core1 point
-
At this point it's a bit hard to put myself in the shoes of someone just getting here, but some highlights from the top of my head: The community is awesome 🙂 The ability to define and modify data structures in the admin using an easy-to-use GUI is still a huge deal. One can quite literally create a full-blown application just by clicking around the admin, especially when combining it with something like ListerPro (though that's a commercial tool, so may be a bit off scope here). Even for those with zero programming know-how, getting a simple ProcessWire powered site (or app) up and running is a (relatively) easy task. Selector engine makes querying data extremely easy, and selectors also scale exceptionally well for complex needs. Most beginners are unlikely to need to know how sub-selectors or OR groups or more complex operator types work, but there is a lot of flexibility hidden underneath. For those that have worked (or fought) with WP_Query, our selector engine is a major selling point. Right out of the box there's a lot of stuff there that even some of the most advanced content management systems don't have — custom content types, amazing language support, numerous field types and inputfields, etc. Honestly, the language support alone is more advanced than anything I've seen in other systems so far, with or without plugins. While it's impossible to compete in numbers with WP, there are actually a lot of high quality modules for ProcessWire. One probably won't be able to carve out a custom application just by slapping modules on top of modules, but there's a whole lot of stuff that they can do. Once you're familiar with the system, extending it with modules and/or hooks is easy. Did I mention the community? Also, ProcessWire is open source and free to use with no strings attached. Unlike some other systems. (Sorry, had to go there.)1 point
-
ProcessWire has a lot of great features that make it better than many competitors. I'm sure there's much more, but here's a start: Build anything Powerful field types and unlimited templates allow you to build anything. Simple yet powerful API ProcessWire gives you the tools to build what you want, easily and in record time. Headless or hybrid Create a REST or GraphQL API or a traditional website. Any template engine Twig, Blade, Latte or plain PHP? We've got you covered. Multi language Reaching an audience in multiple languages is not an afterthought but built right into the core. Powerful permissions Let users see only what they need to see, with a fine grained permission system. Easily Extensible Modules can change or extend almost any aspect of the system. Long-term backwards compatible We know you hate breaking changes. So do we.1 point
-
ProcessWires ❤️ features Custom Fields and templates Selector engine Multi language Freedom of output Tree hierarchy with clean urls Permissions system Extensibility: Modules and hooks Image handling. Thumbnails included Community1 point
-
Further to what @wbmnfktr said, I think ProcessWire can be described as a low code data designer. You still need a front end developer to build public facing pages, but potentially someone with no backend development skills can build a complex range of templates for different data. In more complex scenarios, yes, it might be necessary to write some modules or hooks, but a huge amount can be done without writing a single line of code, but later, if people want to package up and subject to source control, there are third party modules like RockMigrations that allow taking all the field and template definitions and storing them as migrations files. In a sense, ProcessWire in this respect is like a headless CMS of which there are a few out there that allow custom data design without coding. But wait there's more. ProcessWire can be used as a no code/low code headless CMS, but it also assumes that you'll want to output the data somehow, and give you all the tools to do it, without being opinionated. If you just want to output fields directly into HTML template files with some simple PHP codes, fine, you can do it. If you want to output as API calls and use a javascript frontend framework, you can do it. (Not a core feature, but there is a third party module to enable it) If you want to use a templating language, you can do it. (also via third party modules, or use your own)1 point
-
The main reason I switched to ProcessWire was the fact that I could add an unlimited amount of templates with 100% custom fields to my projects. Back in the days WordPress had two types of content: posts, pages - I remember when the feature to have pages was added. 😂 So I started using Textpattern which allowed me to have at least 10 custom fields and individual page templates. Which worked pretty well for a while but ... after some time I needed more fields, more templates, and found ProcessWire. In that moment I was able to create templates for books, restaurants, movies, musicians, whatever type of data I wanted and needed. Fields became more than just strings or dates. It was possible to have textareas, repeaters, tables whereever and whenever needed. That was pretty much 10+ years ago. 🤯 Oh... and of course having this was awesome as well: an unlimited amount of backend users, user roles, access management, multilanguage support, resource friendly and worked perfectly fine even on low-end cheap shared hosting.1 point
-
@ryan, is there any difference in terms of performance between using conditional hooks and doing the equivalent logic of checking the arguments and/or return value within the hook and returning early when a condition is not met? Personally I've preferred not to use the conditional hook syntax because I find it more readable to have PHP logic in the hook code, but if this was less performant I'd consider changing.1 point
-
Media Lister Lists images and files from across the site in a sortable and filterable table. For images you can choose between table, small thumbnails and large thumbnails view modes. The module retrieves the data using SQL queries so is able to efficiently list media information for all but the largest of sites. Possible use cases: Check that a nice variety of banner images is used for top-level pages. Find duplicate files/images by sorting by filesize or filename. Find images without descriptions if this is important for use in alt tags. Find large PDF files that would benefit from optimisation. Check for "inappropriate" images, or images that are not "on-brand". Images in small thumbnails view mode Files saved as a bookmark Controls Media type: Choose between Images and Files. View mode: When listing images you can choose between small thumbnails, large thumbnails and table view modes. When in one of the thumbnail view modes you can see information about the image in a tooltip by clicking the "i" icon, or edit the page containing the image by clicking the pencil icon. From pages matching: This field allows you to add filters to limit the pages that the media will be listed for. Add bookmark: Superusers can add bookmarks for the current settings that will be available from the flyout menu for all users. See the bookmarks section below for more information. Column visibility: Choose the columns that appear in the table and in the information tooltip (when in thumbnails mode). Search: Quickly filters the results to show only items that have the search text in any column, whether the column is visible or not. Custom search builder: For more advanced searches where you can combine conditions for specific columns with AND/OR logic. Pagination: You can navigate through the results and set the number of results per page. Reset: Click the "Reset" button at the top right to return to the default settings for Media Lister (or for the current bookmark if applicable). Editing the page that contains the media For any media result click the link in the "Page" column to open the page that contains the media item in Page Edit. When in thumbnail view mode you can click the pencil icon to achieve the same thing. The field that contains the media item will be focused. When a media item is contained within a Repeater field this is indicated by an asterisk at the start of the page title. When opening Page Edit for a media item within a Repeater field the Repeater item will be automatically expanded, including for nested Repeaters. Limitations for values that are merged in the database The module has limited support for multi-language values and custom fields for images/files. In order to be efficient enough to handle large sets of results the module retrieves raw values from the database, and in the case of multi-language values and custom field values ProcessWire stores these in JSON format in a single database column. The module improves the display of this JSON data by extracting the uploadName value into a separate column, substituting custom field labels for field IDs, adding language names where possible, and by transforming the data into a quasi-YAML format for better readability. Some limitation remain though – for example, if you use Page Reference fields in the custom fields then only the page IDs are displayed. Bookmarks Superusers are able to create a bookmark for the current Media Lister settings by expanding the "Add bookmark" field, entering a title for the bookmark, and clicking the "Add bookmark" button. Bookmarks will be visible to all users from the flyout menu. You can delete a bookmark from the module config screen. Module config In the module config screen you can define defaults for controls such as media type, view mode, pagination limit and column visibility. You can also delete bookmarks from the module config screen. https://github.com/Toutouwai/ProcessMediaLister https://processwire.com/modules/process-media-lister/1 point