Leaderboard
Popular Content
Showing content with the highest reputation on 09/11/2023 in all areas
-
Hi, What's it all about I'd like to share some thoughts and approaches i took to meet the customers requirements when i built the website. Maybe someone has (or will have) similiar needs and can benefit from it, hopefully. I will focus mostly on technical stuff. And because of that i am not sure, if the showcase section is the place to post it (?). Its more like a testimonial. About the organisation/website https://vdp-ev.de (german language only) The Verband Deutscher Puppentheater e.V. (VDP) is the cultural policy respresentation for professional puppet stages in Germany. The VDP gives advice, shares knowledge and represents currently 140 member stages. They publish a magazine frequently, giving awards, workshops and organize events for their members. As the VDP was founded many years ago, they have lots and lots of contents, each with individual structural needs . So the first goal was to pack the vast of content in a good structure and make sure visitors has access to it as straight as possible. Therefor the grafical presentation is rather plain, not really stand out or comes with fancy stuff ?. Some random screens of the public website: Some random screens of the (secured) members marketplace (explanation s. below): Seeing no way to set skip links, here is a short plain TOC: 1. Building a marketplace for VDP members 1.1 Requirements and conceptual thoughts 1.2 Building the menu 1.3 Keep it maintainable 2. Publish selected members content on the public website 2.1 Clients request 2.2 Starting, first approach 2.3 Solution 1. Building a marketplace for VDP members 1.1 Requirements and conceptual thoughts Before the relaunch, members (they have a special user role) yet could login into the backend and edit their contents (data for their stage + add/edit productions). With help of @adrians AdminRestrictBranch they only see their own stage and productions. Those users have no access to other menu trees. With the relaunch the VDP wants to have something different/more elaborated: A secured member area (e.g. marketplace) where all members can share and swap ideas, informations, leading discussion related to their profession, offer or search for profession related material (like puppets, technical gear, services etc.) and some more. Just like some kind of a simple forum (with maintaining their own entries, s. below), but without the overhead of a standalone forum software. There was a (only grafical) approach how to do this by the former developer. His idea was to establish a secured member frontend where the loggedin user have (frontend) presentations to manage their own marketplace content, their stage and productions and even their profile a least. Well, some kind of reinventing the backend... Beside the fixed budged, i thought of a better way to do this. First, i wanted to seperate the marketplace output from the input strictly. Without real advantage member users shouldn't be forced to learn something they're already familiar with (Backend login and add/edit their stage and productions). Second, all marketplace content should live within the PW ecosystem, e.g. are template base pages that lives in the page tree. At least for administrators to see/edit... (s. below also). So, where to establish the CRUD actions for the members content of the marketplace section for each member user (with that special user role)? It all leads to adapt the PW Backend Menu. The Module CustomAdminMenus by @Robin S seems to give a good start for this. The setup for static pages is rather simple. But for my use case i had to deal with a much more dynamic approach. Luckily the module is hookable... I wanted to have two Menus: Mein Markplatz which displays all CRUD actions for the sections (Figuren, Bühne, Material, Tipps & Tricks a.s.o., s. image above) and Zum Markplatz Portal which links to the members marketplace overview page. 1.2 Building the menu First we get the current user content, populate the dynamic menu and build two menus at least. /** * Build Marketplace Admin Menu * * Backend interface for vdp members to add/edit pages marketplace * sections. * * Each member only will see entries that they created. */ $wire->addHookAfter('CustomAdminMenus::getMenuChildren', function(HookEvent $event) { // The menu number is the first argument $menu_number = $event->arguments(0); // Handle Menu 1 if($menu_number === 1) { // Get the current user $user = $event->wire()->user; // Hide admin menu except for vdp members if ($user->isSuperuser() || !$user->hasRole('theater')) { // Do not show the menu $event->return = false; } else { // init $hints_children = []; $offers_children = []; $blog_children = []; $discussions_children = []; // Get all hints for current user $hints = $event->wire()->pages->findRaw("template=members-marketplace-hint, created_users_id=$user.id", ['title', 'url', 'id']); // Allow to add a new hint // Adapt values to fit your environment $hints_children = [ [ 'icon' => 'plus-circle', 'label' => 'Neuen Eintrag anlegen', 'url' => $event->wire()->config->urls->root.'vdpadm/page/add/?parent_id=4232', 'newtab' => false, ] ]; // Build from results // Adapt values to fit your environment foreach($hints as $hint) { $hints_children[] = [ 'icon' => 'file-text-o', 'label' => $hint['title'], 'url' => $event->wire()->config->urls->root.'vdpadm/page/edit/?id='.$hint['id'], 'newtab' => false, ]; } // ... repeat for other marketplace sections ... // Build the menu $menu1 = [ [ 'icon' => 'thumbs-up', 'label' => 'Tipps und Tricks', 'url' => '', 'newtab' => false, 'children' => $hints_children, ] // ... repeat for other marketplace sections ..., don't forget the `,` ;-) ]; // Return the menu $event->return = $menu1; } } // Handle Menu 2 (Link to marketplace portal) if($menu_number === 2) { // Get the current user $user = $event->wire()->user; // Hide admin menu except for vdp members if ($user->isSuperuser() || !$user->hasRole('theater')) { // Do not show the menu $event->return = false; } else { $menu2 = []; // Return the menu $event->return = $menu2; } } }); FYI: Code is part of a ready.php file which lives in /site (s. also https://processwire.com/docs/modules/hooks/) After that the PW top header looks like this: If you click on Mein Markplatz, you get the verbose list*. *) The member user for the demo has no existing entries, so you see none (only the link to add a new entry). 1.3 Keep it maintainable As an administrator i must be able to identify which entry is related to which member. So i prepend the user name to pages names programmatically. This is done by a little helper hook. All relevant templates has a hidden field named `creator`, which gets populated with the user name on page save. To avoid overriding the value when a adminstrator edits an entry, its packed in a condition. Very important, because otherwise the assignment (entry <> member) gets lost! All entries edited by a administrator would have a new owner ?. Definitely not what we want... Btw. the user role `theater` in this example represents the special role for members mentioned above. /** * Utility hook for `Build Marketplace Admin Menu` * * Field `creator` is a hidden page field, that is populated * on page save with the username of the created user. Only related * to members marketplace pages. * * Prevent superuser to change `created user` while edit/save * the page for administration purposes. * * We need this hook on page save, because we want to show * the username within the admin page sidebar list ($page * only provide the ID of the created user). */ if (!$user->isSuperuser() && $user->hasRole('theater')) { $wire->addHookBefore('Pages::saved', function($event){ $page = $event->arguments(0); if($page->hasField("creator")) { // Get the current user $user = $event->wire()->user; // Populate template field for use in the admin sidebar list $page->creator = $user->name; // Save back to $page $page->save('creator'); } }); } FYI: Code is part of a ready.php file which lives in /site (s. also https://processwire.com/docs/modules/hooks/) The complete ready.php vdp-marketplace-demo-ready.php So this is it, mainly. As there are two ways to go after a successful login (Backend view to edit/add content or Frontend view of the members marketplace overview page) the members must decide where they want to go first. Btw, after the user is logged in, the navigation changes within the member frontend scope. So the user can switch between frontend and backend view each with one single click. 2. Publish selected members content on the public website 2.1 Clients request After the relaunch was online, the customer had another request: They wanted to let members decide if their entries (of particular sections of the member marketplace) should also be integrated/viewable on the public website. Visually identical to the views within the member marketplace. Those (public) pages should be available in the navigation and also be indexable by search engines. While this seems not quite a too difficult task at first glance, it turns out to be a bit more tricky. The reason are the restrictions of page paths when dealing with the (secured)members and public scope. Note: All sections in the members marketplace are applied as blog pages, e.g. each has a parent page and associated children. 2.2 Starting, first approach First, i add a conditional checkbox to all related marketplace templates, which was needed in any case. But than it gets a bit annoying. Having kind of a (public) placeholder template (and page based on it) and then iterating over the member blog entries was a dead end. The `children->url` path of course leads to the member section (at that time i wrote a post about this because i hoped this could be solved somehow) As i have to have a real template/page(s) for this (s. 2.1 why), it isn't possible to hook paths here. I realized, i had to find a complete other approach... 2.3 Solution Finally i set up clones for all relevant members section templates (e.g. parent + child template), built public available pages from it and mirror allowed* member entries to the appropriate public pages on a regular base. To avoid disruption, those public pages are readonly for editors. *) Entries with a particular option set (e.g. Beitrag auch auf der öffentlichen VDP Website anzeigen?, s. picture above) The synchronisation between the members area and the public clones is done by cronjobs for each section. Example sync file for one section (VDP Blog): <?php namespace ProcessWire; /** * Synchronize members < > public blog pages * * This file is meant to get executed by a cronjob * * 1. Synchronize (add, update, delete) * 2. Synchronize (delete) * 3. Log, Mail */ // bootstrap processwire, adapt to your path include("../index.php"); // init $d = array(); $o = array(); $totalcountUpdate = ''; $totalcountNew = ''; $totalcountDeleted = ''; // Get member blog items $members_marketplace_blog_items = $pages->find('template=members-marketplace-blog-item'); // Iterate over member blog items, save `name` foreach($members_marketplace_blog_items as $item) { $d[] = $item->name; } reset($d); /** * 1. Synchronize (add, update, delete) * * Perfom synchronisation based on existing member blog items */ // Iterate over members blog items for ($i=0; $i < count($d); $i++ ) { // Get member blog item by given name from the `d` array. $members_blog_item = $pages->findOne("template=members-marketplace-blog-item, name=$d[$i]"); // Get the related public clone (if exists) $public_blog_item = $pages->findOne("template=blog-vdp-item, name=$members_blog_item->name"); /** * Conditional actions * * 1. if * Update present page * * Verbose: Is yet present in public section && is * allowed to be displayed * * 2. elseif * Add new page * * Verbose: Is not present in public section && is * allowed to be displayed * * 3. elseif * Delete page * * Verbose: Is yet present in public section && is * not allowed to be displayed */ if ($public_blog_item->name == $members_blog_item->name && $members_blog_item->options_members_marketplace_entry_publish_to_public == 1) { $public_blog_item->title = $members_blog_item->title; $public_blog_item->name = $members_blog_item->name; $public_blog_item->headline = $members_blog_item->headline; $public_blog_item->blog_date = $members_blog_item->blog_date; $public_blog_item->page_blog_tags_vdp_blog = $members_blog_item->page_blog_tags_vdp_blog; $public_blog_item->body = $members_blog_item->body; // Save page to get an ID for adding images, s.below $public_blog_item->save(); // Make sure we process an array $public_blog_item->of(false); // Delete images first to avoid duplication $public_blog_item->images_members_marketplace_entry->deleteAll(); // Add images foreach ($members_blog_item->images_members_marketplace_entry as $img) { $public_blog_item->images_members_marketplace_entry->add($img); } // Save page again (just for the sake of it) $public_blog_item->save(); // Save iteration for mail, s. below $totalcountUpdate = $i; } elseif ($public_blog_item->name != $members_blog_item->name && $members_blog_item->options_members_marketplace_entry_publish_to_public == 1) { // Add new page $public_blog_item_add = new Page(); $public_blog_item_add->template = 'blog-vdp-item'; $public_blog_item_add->parent = '/wissenswertes-ueber-puppentheater/vdp-blog/'; $public_blog_item_add->title = $members_blog_item->title; $public_blog_item_add->name = $members_blog_item->name; $public_blog_item_add->headline = $members_blog_item->headline; $public_blog_item_add->blog_date = $members_blog_item->blog_date; $public_blog_item_add->page_blog_tags_vdp_blog = $members_blog_item->page_blog_tags_vdp_blog; $public_blog_item_add->body = $members_blog_item->body; // Save page to get an ID for adding images, s.below $public_blog_item_add->save(); // Make sure we process an array $public_blog_item_add->of(false); // Add images foreach ($members_blog_item->images_members_marketplace_entry as $img) { $public_blog_item_add->images_members_marketplace_entry->add($img); } // Save page again (just for the sake of it) $public_blog_item_add->save(); // Save itereation for mail, s. below $totalcountNew = $i; } elseif ($public_blog_item->name == $members_blog_item->name && $members_blog_item->options_members_marketplace_entry_publish_to_public == 0) { // Delete page $public_blog_item->delete(); } } /** * 2. Synchronize (delete) * * Delete outdated public blog item pages, e.g. do not * exists in the member section any longer. */ // Get all member blog items $_members_marketplace_blog_items = $pages->find('template=members-marketplace-blog-item'); // Iterate over member blog items, save `name` foreach($_members_marketplace_blog_items as $_member_item) { $o[] = $_member_item->name; } reset($o); // Get all public blog items $_public_blog_items = $pages->find("template=blog-vdp-item"); // Delete public blog item if not found in members array. foreach ($_public_blog_items as $_public_blog_item) { if(!in_array($_public_blog_item->name, $o)) { $_public_blog_item->delete(); $totalcountDeleted++; } } /** * 2. Log, Mail (delete) * * Log results in PW and send status mail */ // $log->save("cron", "Update public blog posts - New: $totalcountNew, Updated: $totalcountUpdate, Deleted: $totalcountDeleted"); // $emailTo = '<your-email-address>'; // $emailFrom = '<your-email-address>'; // $emailSubject = "VDP Blog | Cron | balance public to member sections "; // $header = ''; // $header .= "From: ".$emailFrom. "\r\n"; // $header .= "X-Mailer: PHP/" . phpversion(); // $message = ''; // $message .= "Am: ".date('d.m.Y H:i')."\n"; // $message .= "Aktualisierte Blog Beiträge: ".$totalcountUpdate."\n"; // $message .= "Hinzugefügte Blog Beiträge: ".$totalcountNew."\n"; // $message .= "Gelöschte Blog Beiträge: ".$totalcountDeleted."\n"; // mail($emailTo, $emailSubject, $message, $header); die(); ?> FYI: This example codefile can live anywhere, just adapt the path where your PW instance is located Example cronjob sync file: vdp-cronjob-sync-blog.php.zip Have fun ?1 point
-
Just have a look at httpUrl() ? Or should the link be hardcoded?1 point
-
that's why i choose your module!1 point
-
Admin Style Chroma This module provides a user interface to control the colors and typography of the AdminThemeUIKit backend theme for ProcessWire 3.0. The requirements are: PHP >= 7.x ProcessWire >= 3.0.179 AdminThemeUikit >= 0.3.3 Less >= 4 InputfieldColor >= 1.1.6 Installation The module can be installed from the Modules Directory or from the zip file archive from the main branch. When you first install the module, you will be taken to the configuration page that consists of four panes: Chroma Scheme Colors Using the color selectors, you can select the first color - your main color - and a second color if you wish - your accent color. Only the first color is required. The default color scheme installed by the module is a grayscale dark mode theme. Your main color does not got modified and gets applied to principal interface elements. If you are currently using the rock.less style as your admin style, this color gets applied to the @rock-primary LESS variable. Your second color gets desaturated. Current Palette Results In the background, depending on your mixer type either one or both of your color selections will be calculated and applied to eight master colors. These colors are displayed here. Your first color choice is applied without any modifications to palette color 3. Your second color choice (when applicable) is desaturated and treated according to your mixer type selection and applied to palette color 6. In general, colors 1-4 are applied to interface elements and their hover states. Colors 5-8 are applied to backgrounds and muted states. In UIKit parlance: Primary color = @chroma-lum-sat-3 Secondary color = @chroma-kum-sat-1 Muted color = @chroma-lum-sat-7 Default color = @chroma-lum-sat-6 Contrast rules are them applied to these colors to get regular and strong labels that are used to assure correct contrast to applied. Please Be Aware: The accessibility contrast threshold of 43% (the LESS default) is applied, but it is still possible to select color combinations that will evade readability scores from Google Lighthouse. Below the current palette dots you will se sample swatches and their hover states can be activated. Chroma Scheme Options The selections made here will alter the LESS files imported into the final admin.css and will either calculate a second color from the first one your select or use the second color. Color Mixer Type There are several mixers included. I'm always interested in other viable additions. Future versions of this module will likely include an ability to add your own custom select options to the interface to reference your own LESS include files. Single : This mixer mode takes your first color, and desaturates it in order to get the second color needed to build the theme palette. Contrast : This mixer mode takes your first color, negates it and desaturates it in order to get the second color needed to build the theme palette. Duotone : This mixer mode takes your first color and uses it to build out the top half of the palette, and takes your second color, desaturates it slightly, and uses it to build out the bottom half of the palette. Cool Harmony : This mixer takes your first color and spins its hue counterclockwise on the color wheel to get the second color and uses it to build out your color palette. Warm Harmony : This mixer takes your first color and spins its hue clockwise on the color wheel to get the second color and uses it to build out your color palette. Luminance Direction Light to Dark (Dark Mode) : This mode sets the palette to run from light colors at 1 to dark colors at 8. When using duotone, the light to dark ordering applies to each 4-color block individually. When using single color mode, the secondary color is a darkened version of the main color. Dark to Light (Light Mode) : This mode sets the palette to run from dark colors at 1 to light colors at 8. When using duotone, the dark to light ordering applies to each 4-color block individually. When using single color mode, the secondary color is a lightened version of the main color. Vibrance Level While the secondary color is always somewhat desaturated, you may wish to dial down or dial up the saturation depending on the text contrast requirements of your color theme. Subdued : The most aggressive desaturation level. Standard : Reasonable desaturated for most applications. Vibrant : The least desaturated settings, though still slightly desaturated. Chroma Scheme Fonts The drop down selectors here will detect css stylesheets found in your ste/assets/fonts or site/templates directory. If you use RockFrontend to download your Google Fonts, it will detect these fonts as well. If you select "No Custom Font" for either the Header Font or the Body Text Font, the default AdminThemeUiKit font rules will apply. Add Google Fonts This feature makes use of a modified version of Bernhard Baumrock's method (found in RockFrontend) for procuring Google Font files and saving them on your server. After looking at his references on CSSTricks is was pretty clear that the header manipulation approach was going to be the best one. When first installed and run, the Admin Style Chroma module will download json lists of Google Font options and cache them in your site/assets directory. There is currently no method in place to check for new fonts, so if for some reason you are not seeing a Google Font you want to use, deleting this file should force the module to repopulate it: /site/assets/AdminStyleChroma/google_fonts.json The Google Fonts are downloaded by individual family and saved along with their CSS file in: /site/assets/fonts/{family}/{family}.css If you select font variants beneath the dropdowns, these values will be passed to the request. If you do not specify which font variants you want, Google will return the defaults for that font family. If you which to include special italic/oblique variants for each weight, set the option appropriately. If a variant does not exist for a given weight, Google will attempt to serve the closest weights available. If you make selections (or don't) and select a font that you have already downloaded before, the previous family files will be overwritten. PLEASE NOTE : If you download a lot of fonts, this process could take some time. Style Compatibility A lot of styles have already been corrected. A number of styles within the ProcessWire core that use plain css or scss have been overwritten via specificity. A 'chroma' class is also added to the body tag, which drives many of the newly inherited classes, but due to the design of some features of certain modules there are other classes defined outside of the heirs of this class. I'm not always happy with how warnings appear. Future versions will address these issues. I've included many rules to provide support for the following areas: Tab Wrappers Page lists and actions Radios, Checkboxes and Selection Colors Selections, Marked text Panels and widgets Image related popups Awesomeplete RepeaterMatrix TinyMCE Interface Tracy Debugger Page Hit Counter Release Note Changes Admin On Steroids Admin Helper Search Engine Color Spectrum Easy Repeater Sort Page View Statistics Nette Tests All changes here are entirely superficial quality of life style improvements. The functionality is not altered. Depending on your TinyMCE settings you may see these improvements but you may not. I've made changes that address quirks that I have personally seen. I am always open to adding rules for other modules where the styles are off or assume a white background. I hope one day we will have a proper discussion of less/css normalization for module authors, but even when that occurs it is hard to say how we will retrofit older modules, etc. For now, this is a patchwork process.1 point
-
junge, wie genial!? thanks a lot for your fast & brillant support! as a newbie i'm impressed by a community that seems to be not to big at first sight but is quick, friendly & helpful!1 point
-
Hello herr rilke! I have added an additional wrapper for the CAPTCHA input field, so you can style it according to your needs. You have to replace the following 2 files with the one from Github (alternatively you can also download and install a fresh copy from Github). AbstractTextCaptcha.php AbstractImageCaptcha.php Now the following wrapper will be added: <div class="captcha-input-wrapper"> <input id="contact-captcha" name="contact-captcha" type="text" class="input" required=""> </div> As you can see a new wrapper with the class "captcha-input-wrapper" will be added arround the CAPTCHA input field. Hope this helps. Best regards1 point
-
I thought that repeater pages have their own template, sorry. If that's not the case then just modify my hook to check the page's parents: Here the $page is a repeater item. You can inspect its parents like shown in line 1 and you can add something like shown in line 2 into your hook to only fire that hook that overrides the title if the parents have your repeater parent page. If not, then early exit of that hook. <?php $wire->addHookProperty("Page::title", function ($event) { $page = $event->object; if(!$page->parents->has("name=for-field-157") return; $event->return = $event->object->headline; });1 point
-
Hello herr rilke. CSRF is implemented by default using the Processwire CSRF API. Take a look here, so no need to inplement it by your own. Best regards1 point
-
If you compare both approaches it would be interesting if you share your findings. https://processwire.com/blog/posts/debugging-tools-built-in/ (Debug::timer) might be helpful and memory usage could also be interesting.1 point
-
No problem, $config->userAuthSalt is created when you install PW and stays unchanged. So it only needs to be copied along with the database the first time you import it.1 point
-
@Jim Bailie, if you update the module to v0.2.3 there is a hookable method that fires when a file is about to be replaced. $wire->addHookAfter('FilesRenameReplace::replaceReady', function(HookEvent $event) { /** @var InputfieldFile $inputfield */ $inputfield = $event->arguments(0); /** @var Pagefile $pagefile */ $pagefile = $event->arguments(1); /** @var Pagefile $replacement */ $replacement = $event->arguments(2); // Do something here... }); I don't think you could do anything useful to the Pagefile objects at this point given that the original file is about to be deleted and the replacement file is about to have all its properties overwritten from the original file, but you could set some other field value on the page like this: $wire->addHookAfter('FilesRenameReplace::replaceReady', function(HookEvent $event) { /** @var InputfieldFile $inputfield */ $inputfield = $event->arguments(0); /** @var Pagefile $pagefile */ $pagefile = $event->arguments(1); /** @var Pagefile $replacement */ $replacement = $event->arguments(2); // Set a date field on the page $page = $inputfield->hasPage; if($page && $page->id) { $page->setAndSave('my_date_field', time()); } });1 point
-
I've heard of Astro and a bunch of other JS heavy frontend frameworks where ProcessWire has to become an API for it to work. Hard pass for me for so many reasons. This article is exactly what I was looking for. Thank you for sharing it! It seems nesting is available on all the latest browsers, but I'd worry about people on old versions of Safari due to being on old versions of macOS (Macs seems to last a while), so yea I agree with using a build process for now if nesting is critical to the way you write CSS, which it is for me as well. Also, doing breakpoints in SCSS in much more intuitive than vanilla CSS.1 point
-
thank you, bernhard, that was very enlightening! I think the third approach (with an own table) is the most suitable for me. i also understood better how i can implement such an approach.1 point
-
I'm using it in a couple of places on one site, but I guess I could revise to the usual FEE.1 point
-
How many documents? How many users? If you only have a limited number then a simple page reference field would be all you need. Just add a field "readby" to your documents page and add users to that field via API. You just need to make sure that you don't have too many pages stored in the page reference field. So if you have many documents but just a few users add the page field to documents. If you have many users but just a few documents add the page field to the user template and add read documents to the field. If you have many documents and many users go with the custom table solution that I showed here:1 point
-
@bernhard This works pretty well for static content, but unfortunately not for content filtered by URL parameters, for example. I really like to use HTMX to dynamically load and replace content on pages. Also asynchronous loading of partial parts of the page becomes very easy. Here is a simple example of the output of a product tile with URL hook <?php foreach ($items as $key => $item) { $pos = $start + $key + 1; ?> <div :hx-get="'/getproduct/'+<?= $item->id ?>" hx-trigger="revealed" hx-indicator=".loader" class="product-tile"> <div class"loader"> Some fancy loader :-) </div> </div> <?php } ?> The hook: (getproduct.php) <?php namespace ProcessWire; $wire->addHook('/getproduct/{prodid}', function ($event) { $id = $event->arguments('prodid'); $item = $event->pages->get("template=product, id=$id"); ?> <div> <p>Producttile</p> </div> <?php exit(); ?> And of course in ready.php include 'templates/functions/getproduct.php'; if (array_key_exists('HTTP_HX_REQUEST', $_SERVER)) { $config->appendTemplateFile = ''; $config->prependTemplateFile = ''; $config->htmxRequest = true; } Maybe that's what @Jonathan Lahijani means by gymnastics. For me personally, this is not a special effort and is actually already part of my workflow. Disclaimer: I know this is not Markup Regions ?1 point
-
My approach from today: $wire->addHookProperty("Page::myFooLabel", function($event) { $page = $event->object; if($page->template != 'foo-page') return; $event->return = $page->title . " (foo label)"; } Then just set the label field to "myFooLabel" and enjoy. This also has the benefit of having the dynamic label always available easily via $page->myFooLabel1 point
-
I added this to the dev branch last week, so that you can now pass additional things to $page->render(): https://github.com/ryancramerdesign/ProcessWire/blob/dev/wire/modules/PageRender.module#L212 One of the things I wasn't thinking about before is that $page->render() was already capable of accepting an array of options (though not commonly used, mostly internal). So we weren't starting from a blank slate, and had to make something that was compatible and complimentary to what was already there. In terms of API calls, you can now do any of these: $page->render($filename); // $filename assumed in /site/templates/ $page->render($pathname); // $pathname is full path, but must resolve somewhere in web root $page->render($options); // array of options and/or your own variables $page->render(array('foo' => 'bar')); // same as above $page->render($filename, $options); // specify filename and options/vars, etc. The $options variable was already something that $page->render() accepted before. Only it was used to specify some little-used modifiers to render(). Those are still there (and they are necessary), but now the utility of $options has been extended. When you want to specify options (outlined in the link above) you only need to specify the ones you want to change from the defaults (of course). Typically you don't need to change them, so I'm guessing that most would use $options to specify their own variables. Every rendered template now receives a copy of that $options array locally scoped. That $options array contains any variables you passed to $page->render(). It also contains PW's default options, should you want to examine any of them. If you made this render() call: echo $page->render('myfile.php', array('foo' => 'bar')); myfile.php could access the 'foo' variable like this: echo $options['foo']; // outputs "bar" One other addition that I'm thinking people might like is $options['pageStack']. That is an array containing a stack of pages that called render(). So if you are doing any recursive rendering of pages, any template can access $options['pageStack'] to see what page is rendering it, and any others before it. Previously this was not possible, and the only way a template could tell what other page was rendering it (if any) was for that renderer to tell the renderee, via $mypage->caller = $page; or something like that.1 point