Leaderboard
Popular Content
Showing content with the highest reputation on 03/19/2025 in Posts
-
Hey everyone! Finally I have time to post a detailed developer walkthrough for PAGEGRID. I have the feeling that many people don't know how flexible PAGEGRID actually is as a developer tool. I think this is mainly due to the fact that most videos are showing the no-code features of PAGEGRID. But since these features are completely optional and PAGEGRID has a lot more to offer, I've put together a video walkthrough to show you exactly what I mean. My hope is that it'll give you a clearer picture of how PAGEGRID can fit into your projects and help you decide if it's the right tool for you. Please take a look at the video below! I think you'll be surprised at what PAGEGRID can do. PAGEGRID's core concepts (video summary): Your markup: Unlike many other site builders PAGEGRID gives you complete control over the markup and structure of your frontend. You can use PAGEGRID to build specific sections or parts of your custom coded website or you can use it as a full-blown site Builder that can work without any coding. Everything is a page: PAGEGRID items are ProcessWire pages that are defined through native ProcessWire templates and fields. Control what clients can edit: PAGEGRID offers an intuitive editor experience that's easy to learn for clients. Editing and design features can be controlled through ProcessWire’s native roles and permission system. Your CSS: You can use your code editor to write CSS or you can bring your own CSS framework. PAGEGRID makes no assumptions about your CSS code. And it’s not just for Grids, display properties like Flexbox, Block or Inline-Block are also supported. Nesting: A powerful feature of PAGEGRID is nesting and while this feature is completely optional it's quite useful for a lot of cases. You can define a block as a container and can define what kind of templates are accepted for the children. This can be used for layout purposes or to group items together, another example might be a slider or gallery block that the user can add items to or basically any repeatable content that you might want to put inside a block. Developer walkthrough: Developer Documentation: https://page-grid.com/docs/developer/ How to create a custom block: Documentation for creating blocks: https://page-grid.com/docs/developer/blocks/ Try PAGEGRID for free PAGEGRID is not free. 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. Installation PAGEGRID (FieldtypePageGrid) is listed in the modules directory, you can install it like any other module. See the install guide for more information. Recent Updates (2025) Group Blocks Now Linkable (april) Performance improvements (markup cache integration) (march) Quick add feature (february) Symbols and Patterns (january)6 points
-
@Robin S - you are great. That's fantasticially helpful. I don't think I've used findRaw before but it worked a treat. According to Profiler rebuilding the markers has gone from 31 seconds to 0.3 secs: It took me a couple of goes to convince myself it was actually rebuilding the markers and not pulling from cache - but it is!3 points
-
$pages->findRaw() will be helpful here. It's hugely faster than $pages->find() and easier to work with than SQL queries. You could do a couple of searches and connect the resulting data by repeater page ID. Example: // Ensure PagePaths module is installed so that URL is available to findRaw() $shop_data = $pages->findRaw("template=shop", ['title', 'url', 'locations'], ['nulls' => true, 'flat' => true]); // Here you could also get other fields from the repeater pages as needed $location_data = $pages->findRaw("template=repeater_locations, check_access=0", ['location'], ['nulls' => true, 'flat' => true]); $shops = []; // Loop over the shop data and get the lat/lng for each location, matching by repeater page ID foreach($shop_data as $id => $item) { $data = [ 'title' => $item['title'], 'url' => $item['url'], 'locations' => [], ]; $location_ids = explode(',', $item['locations.data']); foreach($location_ids as $location_id) { if(!isset($location_data[$location_id])) continue; $data['locations'][] = [ 'lat' => $location_data[$location_id]['location.lat'], 'lng' => $location_data[$location_id]['location.lng'], ]; } $shops[$id] = $data; } db($shops);2 points
-
Work continues on the new processwire.com website. I’ve nearly finished developing most of the modules directory this week and next week will be working on the development side of the API reference and sites directory. Some more good news to share is that when the new site launches, the new admin look and feel will launch as well. The website and admin share a similar design language in some areas, and I’m confident you will love them both. When we use screenshots of ProcessWire in the new site design, they will be from the new admin look and feel. It is still admin AdminThemeUikit, but with a new face that is beautiful, modern and professionally designed. I’ve been using for more than a week and it’s fantastic in my opinion. If for some reason you end up wanting to keep the current look of AdminThemeUikit (perhaps a client doesn’t like change), it will remain as an option too. If you are extending AdminThemeUikit or using the admin.less feature (developed by Bernhard) to custom style the admin, all of that will continue working too. What will likely be changing is that we’ll be moving the older AdminThemeDefault and AdminThemeReno out of the core and into the modules directory. I’d rather keep the core efforts focused with AdminThemeUikit, but continue to support the older admin themes as installable options. Prior to this, most of what you’d seen in ProcessWire’s core admin and website has been designed by me (excluding AdminThemeReno). And I haven’t worked full time as a designer since 2005 or so. If I ever had any site design skills, they are long gone. So PW has always had a “designed by a developer” look. Having professional designers take over the design of both the admin and the website just feels like a major upgrade to ProcessWire all around. More than I could have guessed. I look forward to when I can share the new site design, admin look and feel, and the designers with you. Thanks for reading and have a great weekend!1 point
-
Visit the GitHub repository or module directory for usage instructions. I believe there might be an issue with the module directory structure, as I'm unable to add the module. echo vite(['assets/css/app.css', 'assets/js/app.js']); Important Notes: For bug reports, feature requests, or contributions, please use the GitHub repository. The module is under active development, and updates may be frequent. Make sure to check the repository regularly for the latest version and improvements.1 point
-
Back when I started this thread I tried multiple ways, modules, and custom exports. From JSON to AppApi to GraphQL and everything in-between. I still use basic JSON in some projects or just grab what I need via HTMX nowadays. I pull in only simple data via JSON I might need on build time or fully rendered HTML with HTMX in my AstroJS projects. Whenever I start a new project and need a MVP-like skeleton of it, I go with static content in Markdown/MDX in AstroJS, later on I'll migrate to 100% ProcessWire in most cases. It just works, I feel home, know how to handle stuff, have everything I need and with ProCache, LoginRegisterPro, and FormBuilder I can keep everything on my server and don't need things like Supabase, Neon, FormSpark or whatever. So to finally answer your question: no, not anymore1 point
-
@thausmann then 13A (line 320-323) is the right one for me. I have just done it, it works fine!1 point
-
@kaz it depends what you want: To redirect www to non-www, uncomment 13C (lines 332-333) To redirect non-www to www, uncomment 13A (lines 320-323) OR (if that doesnt work) 13B (327-328)1 point
-
I think URL hooks can be a good solution for this case: https://processwire.com/blog/posts/pw-3.0.173/1 point
-
With regard to the documentation, it would be quite good to have newer features that have been mentioned in this weekly update or blog posts incorporated in the main documentation. Possibly also some of the pro modules stuff, as at the moment documentation is scattered around either in the forum or on the shop pages to purchase the pro modules. While it makes sense for discussion to be in the forum for people with up to date subscriptions, having documentation in one place might be a help, and might even convince people to buy pro modules if documentation for how they can use them is available. I notice with Lister Pro, there is some documentation in the API reference under Lister with core lister methods and properties and Lister Pro methods listed together but pro methods and properties identified.1 point
-
1 point
-
Looks like I've got some catching up to do here 🙊 Thanks for reporting this. It should now be fixed in the latest version of the module!1 point
-
Hey Ryan! First of all, this all sounds very promising. In my humble opinion you are vastly underplaying your own skill in terms of design, but that's also why we can trust that you'll recognize amazing design once you see it. Looking forward to seeing what the team working on the design has cooked up 😉 Now, please forgive me for jumping directly into asking for stuff, but... I know this is small thing, but it would be quite nice if the new admin made use of CSS variables wherever it makes sense; colors, font sizing, etc. (Or provided them as an alternative for non-core tools to use, in case it is not feasible to use them for actual admin styling.) The reason I'm saying this is that I've built various admin tools that I wanted to look like the admin theme, and since there is (to my knowledge) currently no simple way to access existing colors etc. in CSS, any non-Uikit elements I've had to "hard-code" to use current styles. This includes the default green/cyan/blue color theme, current spacing and font size practices, etc. As a result said custom elements may look out of place once the theme is updated 🙂 (Just for the record: SCSS/LESS might be an option, but that feels like a lot of unnecessary overhead and complexity where vanilla CSS would easily suffice. I'd really like to avoid that if possible, and to me it seems like CSS variables are an easy and well supported alternative.) Additionally: it would be awesome if accessibility was a consideration while creating this new admin theme. I know it has been considered to a point in the past, but has never been a major goal. Hopefully we can push things forward in this regard in the future. If there's something I can help, I'd be happy to 🙏1 point
-
@wbmnfktr It's funny that you say that, because this is exactly what I told the designers the first time I saw what they did with the admin theme. I told them that what they did with ProcessWire feels like home. Not just home, but a nicer and more modern home. 🙂1 point
-
A nice weekend to everyone! It’s bin a while since I posted a decent showcase of one of my projects but I’ve been rather busy with other things (as ProcessWire development is not my main job). I’ve noticed that the showcases that are posted here are featuring sites that are really stunning. The ProcessWire community is producing better and better results as it seems. Really hard for me to catch up 🙂 Anyway I want to share a really interesting project: The Relaunch of the community portal for the district “Barmbek Nord” of the city of Hamburg, germany. This showcase is quite large because of all the technical concept description and markup details. If you want a quick look how the editing of the page looks like I included a video demonstration right here: Bildschirmaufnahme 2025-01-18 um 14.22.27.mp4 To keep it brief here is what this community portal is all about: The website Barmbek-Nord.info is a community portal for the Barmbek-Nord neighborhood in Hamburg, Germany. It features local events, cultural activities, initiatives, and resources, such as volunteer opportunities, social projects, and a calendar of events. It also highlights neighborhood news, collaboration opportunities, and community meetings to enhance the area's quality of life. This project was a collaboration of three parties: 1. the district council (providing the future page structure, contents and design ideas) 2. a web designer (https://www.andre-kraemer.de/) 3. me as the developer (https://www.thumann-media.de/) 1. Redesign Concepts Here is a picture of the old website (based on Word Press). This of course was a bit outdated (design wise) at the end of its lifecycle. The district Barmbek-Nord presents itself as a vibrant, exciting and diverse part of Hamburg and this version of the website was not representative at all. Luckily the district council hat a pretty clear vision of what the new version should look like. Basically it was all about “boxes and shapes”. So this was the final design concept hat I took as a blueprint for developing the page: 2. Layout anatomy If you are living in Germany you know the famous chocolate brand “Ritter Sport”. They have an even more famous slogan it’s called “Quadratisch. Praktisch. Gut.” which would translate to “Square. Practical. Good.” So you can describe the whole design concept with these three words 😉 There are two main page templates: Tile Page A Tile Page is divided into multiple grid boxes. There are 8 different grid box layouts available which can be placed in any order. Each grid box is horizontal and has the same dimensions but is divided into subboxes. Subox dimensions vary: There are squares, and rectangles in different alignments and in different amounts. Each of those “sub boxes” inside the grid can hold a specific design element. These design Elements include for example - Plain Images - Textfields - Speech Bubbles - Image and Text combined - and so on Everything is handled with the RockPageBuilder (more about that in the next points) Content Page As the name suggests, a Content Page is used for text-heavy pages. This page template does not feature any box grid layouts. Instead the page structure is predefined: 1. Header area with headline and image fields 2. Content area 3. Content area with specific content blocks that can be chosen using the RockPageBuilder. 3. Technical difficulties The four main challenges when developing the page were: 1. Creating Pages based on multiple stacked grid box layouts 2. Offer individual content elements for each of the grid boxes subboxes 3. The Boxes all have a fixed aspect ratio and should retain it on desktop and mobile devices 4. Make everything user friendly to edit (!) I made a small proof of concept using the RockPageBuilder and it worked out of the box! Let me explain: Here is what the 8 different grid box layouts look like: 4. Technical solutions How do you handle nested content blocks in the RockPageBuilder? Well this is no problem at all and actually quite straight forward. Step 1: Create a RPB Field for the main grid layout blocks and add it to your template Step 2: Create a new Block Type for each of the individual grid box layouts for this field: Now you can add multiple grid layouts to your page templates. But not without some markup of course. The markup of a grid box looks like this: <?php namespace ProcessWire; use RockPageBuilderBlock\LayoutA; /** @var Page $page */ /** @var LayoutA $block */ ?> <section class="rpb-layouta" <?= alfred($block,["trash" => false, "clone" => false, "widgetable" => false])?>> <div id="div1" class="col-2x1"><?= $block->rpb_cell_2_1_a->render(true); ?></div> <div id="div2" class="col-1x1"><?= $block->rpb_cell_1_1_a->render(true); ?></div> <div id="div3" class="col-1x1"><?= $block->rpb_cell_1_1_b->render(true); ?></div> <div id="div4" class="col-2x2"><?= $block->rpb_cell_2_2_a->render(true); ?></div> </section> As you can see you have to create individual RockPageBuilder fields for each sub box and and include them into the markup! Based on the design concept we have four different sub box aspect ratios (width/height) 1x1, 1x2, 2x1, 2x2 There can be two sub boxes with the same aspect ratio in one grid layout, so it was necessary to create two variants (named with the suffix “a” and “b”) of the same RPB field. The CSS for a layout box looks like this. I make massive use of the “grid layout” feature as this offers me an relative easy way to control the layout as well as keeping the aspect ratio. .rpb-layouta { min-height: 50px; display: grid; grid-template-rows: repeat(2, 1fr); grid-template-columns: repeat(4, 25%); gap: 0; #div1 { grid-area: ~"1 / 1 / 2 / 3"; } #div2 { grid-area: ~"2 / 1 / 3 / 2"; } #div3 { grid-area: ~"2 / 2 / 3 / 3"; } #div4 { grid-area: ~"1 / 3 / 3 / 5"; } @media @max-m { grid-template-rows: repeat(4, 1fr); grid-template-columns: repeat(2, 50%); #div1 { grid-area: ~"1 / 1 / 2 / 3"; } #div2 { grid-area: ~"2 / 1 / 3 / 2"; } #div3 { grid-area: ~"2 / 2 / 3 / 3"; } #div4 { grid-area: ~"3 / 1 / 5 / 3"; } } } Step 3: Creating sub box content blocks for the nested RPB fields Now this was the main part of the work. Based on the design concept I had to create individual content elements for the sub boxes (all RockPageBuilder Blocks). All together with markup, styles and configuration options (!) As I mentioned before these content elements consist of: - Plain Images - Textfields - Speech Bubbles - Image and Text combined - and so on Let’s take this sub box content element as an example. It is called “Image Bubble Horizontal”: The markup of this Blocks view file looks like this: <?php namespace ProcessWire; use RockPageBuilderBlock\ImgBubbleHorizontal; /** @var Page $page */ /** @var ImgBubbleHorizontal $block */ ?> <?php $bubbleForm = $block->settings('bubbleForm'); $bubblePosition = $block->settings('bubblePosition'); $bubbleColor = $block->settings('bubbleColor'); $customColorBubble = $block->settings('customColorBubble'); if ($bubbleColor === 'custom') { $bubbleColor = $customColorBubble; } $linkColor = $block->settings('linkColor'); $customColorLink = $block->settings('customColorLink'); if ($linkColor === 'custom') { $linkColor = $customColorLink; } ?> <section class="rpb-imgbubblehorizontal uk-height-1-1 uk-position-relative" data-id="<?=$block->id;?>" <?= alfred($block,["addTop" => false,"addBottom" => false,"clone" => false]) ?>> <?php $image = $block->img_single; if ($image) { ?> <img srcset="<?= $image->size('horizontal')->srcset() ?>" src="data:image/svg+xml;charset=utf-8,%3Csvg xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg' viewBox%3D'0 0 <?=$config->imageSizes['horizontal']['width']?> <?=$config->imageSizes['horizontal']['height']?>'%2F%3E" sizes="auto" width="<?= $config->imageSizes['horizontal']['width']?>" height="<?= $config->imageSizes['horizontal']['height']?>" alt="<?= $image->description ?>" loading="lazy" /> <?php } ?> <div class="speech-bubble uk-flex uk-flex-center uk-flex-middle bubble-form-<?=$bubbleForm;?> <?=$bubblePosition;?>"> <div class="speech-bubble-content"> <?= $block->body(); ?> </div> <img style="color: <?=$bubbleColor?>;" src="/site/templates/images/bubbles/bubble-<?=$bubbleForm;?>.svg" uk-svg/> </div> <style> section[data-id="<?=$block->id?>"] a { color: <?=$linkColor?> !important; } </style> </section> I don’t want to go into details too much but one of the many great features of the RockPageBuilder module is that you can include and block specific setting fields directly into the Blocks PHP file without the hassle of creating those fields in the backend in the first place and adding them to the block manually. What a time saver this was! For example the content element “Image Bubble Horizontal” should have several config options. - the shape of the speech bubble - the position of the speech bubble - the color of the speech bubble - the color of the link text (if included) This is all done in the settingsTable method (https://www.baumrock.com/en/processwire/modules/rockpagebuilder/docs/settings/) public function settingsTable(\ProcessWire\RockFieldsField $field) { // You can set default settings for all blocks via hook. // See docs for details or leave this line unchanged. $settings = $this->getDefaultSettings($field); $settings->add([ 'name' => 'bubbleForm', 'label' => 'Sprechblase Form:', // the first parameter must match the name of the setting!! // in this case the setting's name is "demo", so we use "demo" here as well 'value' => $field->input( 'bubbleForm', // use either "radios" or "radios-inline" 'select', [ '*hexagon' => 'sechseckig', // the star marks the default option 'square' => 'viereckig', 'round' => 'rund', ] ), ]); $settings->add([ 'name' => 'bubblePosition', 'label' => 'Sprechblase Position:', 'value' => $field->input( 'bubblePosition', // use either "radios" or "radios-inline" 'select', [ 'uk-position-top-left' => 'Oben links', // the star marks the default option 'uk-position-top-center' => 'Oben mitte', 'uk-position-top-right' => 'Oben rechts', '*uk-position-center-left' => 'Mitte links', 'uk-position-center' => 'Mitte', 'uk-position-center-right' => 'Mitte rechts', 'uk-position-bottom-left' => 'Unten links', 'uk-position-bottom-center' => 'Unten mitte', 'uk-position-bottom-right' => 'Unten rechts', ] ), ]); $settings->add([ 'name' => 'bubbleColor', 'label' => 'Sprechblase Farbe:', 'value' => $field->input( 'bubbleColor', // use either "radios" or "radios-inline" 'select', [ '*#e61b7b' => 'Magenta', // the star marks the default option '#1cae8d' => 'Grün', '#dea500' => 'Gelb', '#646363' => 'Grau', 'custom' => 'Eigene Farbe', ] ), ]); $settings->add([ 'name' => 'customColorBubble', 'label' => 'Eigener Farbwert Sprechblase (HEX Code)', 'value' => $field->input('customColorBubble', 'text'), 'showIf' => 'bubbleColor=custom', ]); $settings->add([ 'name' => 'linkColor', 'label' => 'Linktext Farbe:', 'value' => $field->input( 'linkColor', // use either "radios" or "radios-inline" 'select', [ '*#e61b7b' => 'Magenta', // the star marks the default option '#1cae8d' => 'Grün', '#dea500' => 'Gelb', '#646363' => 'Grau', 'custom' => 'Eigene Farbe', ] ), ]); $settings->add([ 'name' => 'customColorLink', 'label' => 'Eigener Farbwert Linktext (HEX Code)', 'value' => $field->input('customColorLink', 'text'), 'showIf' => 'linkColor=custom', ]); return $settings; } The edit mask of the block then looks like this: 4. See it in action Heres a quick demonstration video of how the page editing with the layout bocks and nested blocks is working in real time: Bildschirmaufnahme 2025-01-18 um 14.22.27.mp4 5. Modules and other tech - UIkit 3 as frontend framework - RockFrontend (https://www.baumrock.com/processwire/module/rockfrontend/) - RockPageBuilder (and lots of thanks to @bernhard for helping me out on this project) (https://www.baumrock.com/processwire/module/rockpagebuilder/) - Cronjob Database Backup (https://processwire.com/modules/cronjob-database-backup/) - ProcessWire User Activity (https://processwire.com/store/pro-dev-tools/user-activity/1 point
-
TextformatterFootnotes Github: https://github.com/eprcstudio/TextformatterFootnotes Modules directory: https://processwire.com/modules/textformatter-footnotes/ This textformatter adds footnotes using Markdown Extra’s syntax, minus Markdown About This textformatter was primarly created to ease the addition of footnotes within HTML textareas (CKEditor or TinyMCE) using Markdown Extra’s syntax. It will also retain any inline formatting tags that are whitelisted. Usage To create a footnote reference, add a caret and an identifier inside brackets ([^1]). Then add the footnote in its own line using another caret and number inside brackets with a colon and text ([^1]: My footnote.). It will be put automatically inside the footnotes block at the end of the text. Notes the identifier has to be a positive number (int) if a reference has no corresponding footnote (or vice-versa) based on their identifier, both will be ignored and left as is by default references/footnotes are sequenced per field, meaning if you are outputting several fields with this textformatter each footnotes group will start from 1 block elements are not guaranteed to work in footnotes and are thus removed by defaut Options In the module settings you have some options regarding the generated markup: you can change the icon (string) used for the backreference link you can change the classes used for the wrapper, the reference and backreference links you can decide to sequence the footnotes from different fields continuously, instead of restarting from 1 you can edit the list of whitelisted HTML tags that won’t be removed in the footnotes Hook Dynamically change options If you want to have a more granular control over the footnotes (e.g. per field), you can use this hook: $wire->addHookBefore("TextformatterFootnotes::addFootnotes", function(HookEvent $event) { $str = $event->arguments(0); $options = $event->arguments(1); $field = $event->arguments(2); if($field != "your-field-name") return; // Say you want to change the icon for a <svg> $options["icon"] = file_get_contents("{$event->config->paths->templates}assets/icons/up.svg"); // Or change the wrapper’s class $options["wrapperClass"] = "my-own-wrapper-class"; // Put back the updated options array $event->arguments(1, $options); }); Check the source code for more options. Group all footnotes in a page Since a textformatter is applied per field, its footnotes are appended right after its content. If you have multiple fields and want to ouput all footnotes in the same place you can use the outputAsArray option: // in init/ready.php $wire->addHookBefore("TextformatterFootnotes::addFootnotes", function(HookEvent $event) { $options = $event->arguments("options"); $options["outputAsArray"] = true; $event->arguments("options", $options); }); $wire->addHookAfter("TextformatterFootnotes::addFootnotes", function(HookEvent $event) { if(empty($event->return["footnotes"])) return; $footnotes = setting("footnotes") ?: []; $footnotes = [ ...$footnotes, ...$event->return["footnotes"] ]; setting("footnotes", $footnotes); }); // in your template file echo $modules->get("TextformatterFootnotes")->generateFootnotesMarkup(setting("footnotes")); Note: if you are using this method to output your footnotes and want to dynamically change the markup/icon, please use the TextformatterFootnotes::generateFootnotesMarkup hook instead of TextformatterFootnotes::addFootnotes1 point