Leaderboard
Popular Content
Showing content with the highest reputation since 04/21/2025 in all areas
-
Hi all, I would like to propose a different format for https://processwire.com/api/ref/ The PW docs should be provided as markdown. Reasons: much easier to parse for machines easier to feed as context to AI assistants Most of popular modern frameworks / libraries (laravel, whole node ecosystem, etc.) have their docs in markdown nowadays. And I think it is exactly for the reasons mentioned. Me and also others here like @wbmnfktr are having a hard time providing meaningful context to AI assistants in IDEs like Cursor, Windsurf etc. Once those assistants have good context, they do an excellent job with PW specific code. But currently there is a lot of manual effort required to provide this context. ProcessWire would be more accessible to a wider range of users if development could be assisted by AI in an efficient way. I created markdown docs with python mkdocs at https://gebeer.github.io/mkdocs-processwire/ WARNING: this is in it's early stages and far from complete/perfect. It is merely for demonstration purposes atm and needs a lot of polish regarding structure, index pages for each class etc. I am using a separate local repo for scraping the API docs and parsing them to markdown. The whole process of scraping/parsing/deploying still involves manual steps (though those could be automated). And it needs to be repeated at least for every master release of PW. So I would prefer if @ryan could produce the API docs in markdown in the first place. Your feedback / opinions would be much appreciated. Cheers10 points
-
This week on the dev branch there are some issue fixes and new features. ProcessWire’s modal JS alert functions have been upgraded to use Uikit modals. Previously they were using Vex modals, but it appears that Vex is no longer maintained, so when we ran into an issue with them it just made sense to switch to Uikit for this, at least when AdminThemeUikit is the current admin theme. The JS functions affected are ProcessWire.alert(), ProcessWire.confirm() and ProcessWire.prompt(). All of which can be found in ProcessWire’s main.js file used by admin themes. ProcessWire’s Markup Regions output method was updated this week to support class removal by wildcard or regular expression. When you specify a class attribute with a class name that starts with “-“ that means that you want to remove that class from the element you are overriding/appending/prepending. Previously you had to specify the full class name you wanted to remove. Now you can specify a wildcard like this: <div id="content" class="-uk-width-*" pw-append></div> That would make it remove all classes from #content that start with "uk-width-". You may place the wildcard anywhere in the expression that you want to, enabling you to remove by prefix or suffix. But if that’s not enough, you can also specify a regular expression like this, which would do the same thing as the above: <div id="content" class="-/^uk-width-.*$/" pw-append></div> That's probably overkill for most, but between the “/“ delimiters, you may use any PCRE regular expression. Usually when we add a class to a markup region, we just specify it like a regular HTML class attribute. But if you want to add a class that would match what you are removing, you’ll want to prefix your class name with a plus sign. That tells the Markup Regions processor not to remove it even if it matches your rule. For example, the following would remove all uk-width classes and then add a uk-width-1-4 class: <div id="content" class="-uk-width-* +uk-width-1-4" pw-append></div> Regarding the new ProcessWire website: it’s nearly done except for the homepage. I’m saving the best part for last. I’m not saying the site will launch tomorrow, as there’s still a lot of detail work to take care of too. But I did want to say that a lot of progress has been made and hopefully it won’t be too much longer before we launch it. Thanks for reading and have a great weekend!8 points
-
A custom Fieldtype and Inputfield for ProcessWire that allows you to store a Twitch username on a page and view its live stream status. The module currently uses TailwindCSS for styling (but future versions may make the styles configurable). Inlcuded modules: FieldtypeTwitch: Stores the Twitch username. InputfieldTwitch: Input field with live status preview in admin. MarkupTwitchStream: For rendering Twitch information on the frontend. Github: https://github.com/TwoWheelDev/MarkupTwitchStream Module Listing: https://processwire.com/modules/markup-twitch-stream/ (Currently pending approval)6 points
-
Got completely stuck on some coding issues. As a last resort I started a conversation with ChatGPT (free version). I had 2 problems. Problem #1 took me & ChatGPT a while to solve but we got there in the end. Now working on Problem #2. Not yet solved but ChatGPT has given me some ideas. Here's how we ended up tonight: Not only did ChatGPT give me some great ideas, it was more polite, encouraging and patient than most people I know. 😍😂5 points
-
I think the docs are generated with a custom module: https://processwire.com/blog/posts/processwire-3.0.41-and-a-look-at-api-explorer/3 points
-
Answer: set this in site/config.php as PHP 7.4 doesn't support it and PHP 8.3 respects it and the default is false which will lead to different responses than what is expected. $config->dbOptions = [ \PDO::ATTR_STRINGIFY_FETCHES => true ];3 points
-
I totally support what @bernhardsaid and I just want to add: VS Code Insiders with Github Copilot - for free. Not sure why and how but the results feel a bit more nuanced at times. Combined with Cline/Roo you even could add a free Gemini 2.0/2.5 key which outperforms in conceptual tasks (PRDs/SOPs) quite often. All you need to do is installing VS Code Insiders, add Copilot through the interfaces provided (you can't miss them!). I enjoy writing mass content, like docs with it, as it doesn't ruin my credits in Windsurf/Cursor.3 points
-
It still feels crazy that we can talk to computers now, but it has already had a huge impact on my life. Not only my dev life. It's also still crazy to call given results an "idea", but I guess we don't have better words for it so far... If you liked the experience try https://www.cursor.com/ or https://windsurf.com/. I started with cursor, then gave windsurf a try for some weeks. I switched back to cursor, because cursor tab so so extremely well at predicting what I want to do next, that I feel it makes me extremely faster all the time while I'm working. Windsurf on the other hand felt better when asking it to create files, read the codebase, have a chat or use it for non-dev questions (like a web search or some other crazy tasks). While both are already next level compared to copy-pasting things to chatgpt, because they know "everything" about your codebase, I think cursor tab is a different beast. It's there for you all the time. You just type three letters and it predicts 3 lines of code. It's incredible powerful in my opinion and it's also great and fun to learn. Because sometimes it predicts new patterns and then you can look that pattern up and if it's a good pattern to know and use you can keep it in your mind and start using it on your own. If you use AI only actively when asking specific questions you miss out on this.3 points
-
+1 Docs in markdown have been working great for me. All my module docs are markdown, so they are part of the repo (could be another repo as well, as images for example bloat the module code, which is not ideal). I'm using RockFrontend's dom tools to enhance the markdown docs with some special features. That makes it possible to have working forms on my docs for RockForms, for example: https://www.baumrock.com/en/processwire/modules/rockforms/docs/ ## Working Example In this example we will build the following form. [rockforms=Quickstart] Or another example is rendering code blocks and showing copy buttons. In markdown it's just a plain code example, on the HTML page it's an interactive element with nice labels etc: ## Example This example shows how you can make every required field's label bold and add the note `optional` to every field that is not required. Note that NetteForms uses the terms `label` and `caption` for field labels. [rockforms=Optional] `label: /site/ready.php` ```php $wire->addHookBefore( "RockForms::renderField", function (HookEvent $event) { // get the field that is rendered // in netteforms its called "control" $control = $event->arguments(1); // make required fields bold if ($control->isRequired()) { $control->getLabelPrototype()->addClass('uk-text-bold'); } // add optional note if not required else { $renderer = $event->arguments(0); $label = $control->label->getText() . " <small>(optional)</small>"; $control->setCaption($renderer->html($label)); } } ); ```2 points
-
Version 1 is here! https://github.com/orgs/NativePHP/discussions/547 Mobile apps capability in the works: Not surprised; given that Caleb Porzio also wrote Alpine.JS 😊.2 points
-
Hello y'll, I so happy introduce technical template for ai generate templates on Tailwind from ProcessWire fields. How to work: Select templates you want to design interfaces for Copy the generated JSON structure Ask AI to "Create a Tailwind CSS design for displaying this ProcessWire data" Specify any preferences like: mobile-first, card layout, table layout, etc. Push button "Copy Prompt" Insert prompt to Claude AI, ChatGPT or another ai services. How to Setup: Use ftp for transfer lego.php to template folder On ProcessWire create template with same name. Create new page with select template. Enjoy. Note: It is not always possible to generate a template from the first time, but by debugging you can make even more or less excellent variants. On example screenshot finish page with adjusting elements, blocks on Tailwind. If you have questions or wishes ask me below. Thank you. UPDs: 04/14 Update prompt lego.php2 points
-
I hope you’ve had a good week. My kids have been on spring break from school for the last week, so we took them to the beach for a week. The weather was great, so I didn’t get much time at the computer. We’ve just returned and now I’m anxious to focus on ProcessWire. While I don’t have much to say this week, hopefully by this time next week I’ll have much more to write about, so stay tuned and have a great weekend!2 points
-
Learn more in the README.md on the GitHub page at https://github.com/clipmagic/LoginPassKey Version 0.0.2Beta released on GitHub. This version is more stable delivers tighter security supports LoginRegisterPro Stay tuned 🙂1 point
-
Correct. You have to test everything with your setup, workflow, and everything. Even between projects I have to tweak lots of things. I guess in total about 5 different variations of setup are in that repo. Test it. Tweak it. Even feel free to raise a PR.1 point
-
Woah nice! U the MVP, I guess it's the usual ProcessWire double edge sword, since possibilities are up to you, well, gotta work on your flavor of AI rules. 🦾1 point
-
Yup. From https://processwire.com/store/pro-dev-tools/api-explorer/ Could check and see what methods are hookable to potentially generate markdown instead of HTML. Current licensing seems to indicate that using it is solely for the purpose of the license holder though, so generating Markdown from it would be usable by the developer, but not shareable. Until or unless Ryan can consider if he would also not mind generating Markdown with the official docs, it may be a more practical option for those looking to do it now.1 point
-
(I’m not using AI assistants to code but) if this demand could go through the financial support of ProDevTools/@ryan that would be great1 point
-
Actually, Context7 was what made me start this thread. Someone has already submitted the PW repo to their scraper. But the resulting snippets are mostly garbage: https://context7.com/processwire/processwire Now why is that? They do not scrape the very well documented PHP source files. If docs were in markdown, they would. I tried the MCP and it didn't have context for simple things like WireHttp. Does anyone know what tooling is currently used to create the PW API docs, phpDocumentor, phpDox, ApiGen or something different?1 point
-
@bernhard I saw that but didn't click. I wasn't sure if that would clone everything and I should have tried it out.1 point
-
I don't think it's very elegant, but it works ^^ I think you can also use the GUI and click on "use one of the existing blocks" after clicking on the plus to create a new block, no?1 point
-
Hi @FireWire to "clone" or "mirror" a block it just needs an empty (!) php file with the same name. /site/templates/RockPageBuilder/blocks/Foo/Foo.php (all code) /site/templates/RockPageBuilder/blocks2/Foo/Foo.php (empty)1 point
-
1 point
-
I love this goddamn AI blackmagic too, miss PHPStorm like the dessert miss the rain but what to do, their AI integration is just subpar compared Cursor's. Anyone working with rules for ProcessWire?1 point
-
I'm trying to upgrade a site from ProcessWire 3.0.229 to 3.0.246 so that I can upgrade the PHP version the site is running on from 7.4 to 8.3. PDO Statements such as the one below used to return the value of type as a string, now they return the value as an integer. This is a breaking change as we have many literal comparisons in the site. Is this a setting that can be changed? E.g. $query = $this->database->prepare("SELECT fundraiser, `type`, `order` FROM deliveries WHERE `driver` = :driver AND `type` != 2 AND `removed` != 1 AND `fundraiser` IS NOT NULL ORDER BY time ASC"); $query->bindValue(':driver', $params['driverId']); $query->execute(); $result = $query->fetchAll(\PDO::FETCH_ASSOC); $ids = []; foreach ($result as $item) { if ($item["type"] === "0") { $ids[] = $item["business"]; } elseif ($item["order"]) { $ids[] = $item["order"]; } } Also this selector return ID as a int and not a string. $selector = "template=Group, sort=title"; $groups = $this->pages->findRaw($selector, ['id','title']); foreach($groups as $group) { $groupsArray[] = [ 'id' => $group['id'], 'title' => $group['title'] ?? '', ]; }1 point
-
Moin @bernhard, so good! :-). The last sentence got me going. I overlooked to adjust the js ajax endpoint url on language switch. Next beer on me, please 😉1 point
-
If I'm not missing something it should be easy: The ajax request will return data in the language that the request was sent to. A request to example.com/whatever would return english A request to example.com/de/was-auch-immer would return german You just have to make sure to send the request to the correct endpoint.1 point
-
Hi @BrendonKoz, thanks for reply! I tried exactly the same syntax but unfortunately that didn't work. I am working on a full text searchable archive of old special interest magazines where I just need an image per page, a large text field for the OCR'd text and maybe some tags. Meanwhile I stumbled upon the amazing plugin "RepeaterImages" by @Robin S which looks like it might just solve my problem 🙂1 point
-
1 point
-
I'm not sure what the technical reason is for the 404 not being handled from ready.php, but as a workaround you could set a custom property on the $page object to indicate that a 404 should be thrown and then throw the 404 later, e.g. from a template file or from _main.php. // Your conditional in ready.php if($foo === 'bar') $page->throw404 = true; // In template file or _main.php if($page->throw404) wire404();1 point
-
1 point
-
Please have a look at this thread. As far as I know this is still valid:1 point
-
I'm currently traveling so don't have anything major to report this week, but wanted to check in. I am posting this from my phone in the car (don't worry, I'm not driving) and I'm not great at typing from my phone so will keep it short. 🙂 Progress continues on the ProcessWire admin design and the website, and I can't wait till we can share it with you. We may soon get another master/main version merged from the dev branch, as there have been a few updates in the last several weeks that I think our stable version would benefit from, and a few more to come as well. More soon! Thanks for reading and have a great weekend!1 point
-
Reviving this for a slightly different use case. I need to serve a different templates folder based on a certain URL segment. My code goes in site/config.php. In there $input is not available yet. So the logic uses $_SERVER['REQUEST_URI'] /** * Switch ProcessWire templates directory based on the second URL segment. * Type the second URL segment as key and the name of the new templates folder as value. * Example: '/imaging/' maps to 'templates-magazine' folder. * We use the second segment because the first segment is always the language (en, de, etc.) */ $config->templateSegments = array( 'imaging' => 'templates-magazine', // first url segment => templates folder name // Add other segment => folder mappings here ); // Check the second URL segment directly from REQUEST_URI $requestUri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/'; $path = parse_url($requestUri, PHP_URL_PATH); $segments = explode('/', trim($path ?: '', '/')); $firstSegment = $segments[1] ?? null; if ($firstSegment && isset($config->templateSegments[$firstSegment])) { $folder = $config->templateSegments[$firstSegment]; $config->urls->templates = "/site/" . $folder . "/"; $config->paths->templates = $config->paths->site . $folder . "/"; } Works well. in site/init.php I have a ProcessPageView::execute hook so that PW can find the template file and offer the "View" link in the page edit screen in admin. $wire->addHookBefore("ProcessPageView::execute", function (HookEvent $event) { // set templates dir for magazine pages in admin /** @var Pages $pages */ $pages = wire()->pages; /** @var PagesRequest $request */ $request = $pages->request(); /** @var Page $page */ $page = $request->getPage(); $editPage = $pages->get(input()->get('id')); if($page->template == 'admin' && $editPage->template == 'magazine-page') { /** @var Config $config */ $config = wire()->config; $folder = 'templates-magazine'; $config->urls->templates = "/site/" . $folder . "/"; $config->paths->templates = $config->paths->site . $folder . "/"; } }); Not the most elegant implementation, but works. Could have put this in site/templates/admin.php but decided against for separation of concerns reasons.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
-
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! 🚀1 point
-
It took some investigating and it would be great to have this made more obvious in any documentation for $config->pagefileSecure... Behind the scenes pagefileSecure is using $files->send(): And $config->fileContentTypes forces download for certain extensions based on whether the content type is preceded by a + sign. You can override the default for the pdf extension in your /site/config.php and then the files should display in the browser: $config->fileContentTypes('pdf', 'application/pdf'); // No plus sign before the content type1 point
-
ProcessWire Concatenate Fieldtype Fieldtype that concatenates the values from one or more other fields at runtime. The value can contain additional formatting and/or words as needed, which you define in your Concat field settings. Example Problem: Your system has a first_name and last_name field, and you want to have a separate full_name field composed of first_name and last_name, without redundancy. Solution: You would create a new Concat field, click the details tab, and enter "first_name last_name" (the fields you want to concatenate) in the settings. Other Potential Uses Having a field that combines the value of two or more others, without the redundancy of separately stored data. Defining a custom “label field” for select boxes, like those used with the Page field. Defining a custom label for your Page List that includes your own formatting. Defining an alternate variation of a text field that uses a different text formatter. Considerations The value for this fieldtype is generated at runtime and thus no data is stored in the database. This is good because there is no duplication. However, it also means that you cannot directly query a Concat field from $pages->find(), for example. If you happen to change the name of a field being used in a Concat field, you will have to update the name in your Concat field settings as well. By design, Concat fields do not inherit the text formatters of the fields they concatenate. You define these separately with the Concat field. Because this is a runtime-generated field, there is no Inputfield associated with it. How to Install Install the module by placing FieldtypeConcat.module in /site/modules/. Check for new modules on the Modules screen in the ProcessWire admin. Click Install for the Concat Fieldtype. How to Create a Concat Field Under Setup and Fields create a new field using type Concat. After entering the new field name and label, click Save. Click the Details tab and enter one or more field names. Separate them with whatever spacing and punctuation is appropriate. Optionally choose one or more Text Formatters. If you are not sure which, “HTML Entity Encoder” is a good default to use. Save. Add your new field to one or more Templates. How to access the value of a Concat field This is no different than accessing the value of any other field. If your Concat field has the name “full_name” then you would output its value like this: echo $page->full_name; Download PW Modules Site: http://modules.proce...eldtype-concat/ GitHub: https://github.com/r...FieldtypeConcat1 point
-
This module is sort of an upgrade to my earlier ImageToMarkdown module, and might be useful to anyone working with Markdown in ProcessWire. Copy Markdown Adds icons to images and files that allow you to copy a Markdown string to the clipboard. When you click the icon a message at the top left of the screen notifies you that the copying has occurred. Screencast Note: in the screencast an EasyMDE inputfield is used to preview the Markdown. It's not required to use EasyMDE - an ordinary textarea field could be used. Usage: Images When you hover on an item in an Images field an asterisk icon appears on the thumbnail. Click the icon to copy an image Markdown string to clipboard. If the "Description" field is populated it is used as the alt text. You can also open the "Variations" modal for an image and click the asterisk icon to copy an image Markdown string for an individual variation. Usage: Files When you hover on an item in a Files field an asterisk icon appears next to the filename. Click the icon to copy a link Markdown string to the clipboard. If the "Description" field is populated it is used as the link text, otherwise the filename is used. https://github.com/Toutouwai/CopyMarkdown https://processwire.com/modules/copy-markdown/1 point
-
Example with a multidimensional array. RepeaterMatrix fieldname = 'fieldname' first level: page where the repeater field lives in, indexed by ID second level: per page repeater items indexed by ID third level: field inside repeater indexed by name $rp = $pages->find('fieldname.count>0'); $return = []; foreach ($rp as $p) { $return[$p->id] = []; // RepeaterMatrixPageArray foreach ($p->fieldname as $item) { if ($item->type != 'basic') continue; $return[$p->id][$item->id] = []; // RepeaterMatrixPage foreach ($item->template->fields as $repeaterField) { if ($repeaterField == 'repeater_matrix_type') continue; // we do not need this if ($item->$repeaterField === '') continue; // if you want to ignore empty strings $return[$p->id][$item->id]["$repeaterField"] = $item->$repeaterField; } // remove if empty if (empty($return[$p->id][$item->id])) unset($return[$p->id][$item->id]); } // remove if empty if (empty($return[$p->id])) unset($return[$p->id]); } var_dump($return);1 point
-
Since there are a lot of topics about the same theme, I decided to write a small module which overwrites the core function ___SetupPageName(). The module allows now to populate the page name using proprietary date() function. Works like PHP date() with follwing exceptions: Everything between the brackets is detected as format string, meaning no 2nd parameter possible. No need to use quotes around the format string. Spaces not allowed. from any page property, page field including subfields, parent property, parent field etc. Meaning everything what you get with $page->get(); including dot syntax. The function will give error and warnings in case of unproper settings, but creates the page with name 'untitled' anyway. Download here: http://modules.processwire.com/modules/process-setup-page-name/ Some Examples The following settings in parent template 'Name Format Children' will assign name immediately. date(Y) date('Y-m-d') parent.title parent.parent.title parent.id template.id assign name after page saving and/or population of depending field. Overwrites 'untitled' after all fields which are defined in parent template are populated. id (any other page property) pagefieldname, multiple pagefieldnames show a warning. pagefieldname (value not populated)show an error. date() // empty, no format assigned date(Y // missing closing bracket date(Y md) // unallowed space notexistingfieldname notexistingproperty existingfield.notexistingsubfield The function in the module ___SetupPageName() could be completely copied as is to the core class $pages. @Ryan Would be nice to see this in core. Related topics: https://processwire.com/talk/topic/8576-name-format-children/ https://processwire.com/talk/topic/8217-name-format-for-children-based-on-any-field-not-just-title/ https://processwire.com/talk/topic/11628-custom-page-name/ https://processwire.com/talk/topic/11349-page-add-step-not-being-skipped-when-name-format-for-children-is-set/ https://processwire.com/talk/topic/10701-automatic-page-name-using-processpageaddexecute-and-pagetable/ https://processwire.com/talk/topic/10208-pagetable-automatic-page-name-format-when-multiple-templates-allowed/ https://processwire.com/talk/topic/9979-name-format-for-children-for-multiple-allowed-templates-for-children/ https://processwire.com/api/modules/process-template/ Any recommandations after testing welcome. Download here: Download here: http://modules.processwire.com/modules/process-setup-page-name/ Edit: small enhancement to prevent display of warning twice. 23.12.15 multiple values possible now 24.12.15 made compatible with PW 3.x 27.12.15 Update Version 1.0.8 09.01.16 Update Version 1.1.01 point