Search the Community
Showing results for 'runtime'.
-
Proper place to store a value for the duration of a request?
bernhard replied to Michael Lenaghan's topic in General Support
I'm not sure I understand ... For one request you can store your variable wherever you want (and where it is allowed), for example you can set $config->foo = "Foo!" somewhere and later you can access that runtime property. On the next request $config->foo will be null again unless you set it somewhere. Until PHP8.2 it was possible to use $wire for that as well, eg $wire->foo = "Foo!", but since PHP8.2 setting dynamic properties is deprecated. -
I've done some searching for an answer on this as I can't believe it has not come up before: My problem is that I would like to use a "selector" field (i.e. the interactive selector builder) to compare, for example, a datetime field with 'today'. The issue is that I want it to operate dynamically, so the comparator needs to be stored as 'today' and only converted when the selector is run. This works fine if I just use a text field instead of the selector field and allow the user to input a selector string - e.g. "myDatetime>today", but in the selector builder the comparison field requires an absolute date - a string entry of a relative date is impossible. However, for many reasons, I would prefer not to have a user attempting to enter a selector string. Has anyone tried something similar? If not, I'm toying with the idea of writing a module to extend InputfieldDatetime. That would permit the storing of a valid string as well as an actual date. The string would be only converted to a date at runtime. This would allow entering a date as, for example, 'last month' such that when the field is formatted it would be displayed as a date one month before the execution date. This contrasts with the present module where, although you can default to 'today', I think that is converted to a timestamp at the time of entry and stored as such. Any thoughts on that?
-
On the dev branch this week we have a good collection of issue fixes and feature requests. The dev branch commit log has all the details. One feature added this week which I think could come in very handy is #520 (via @Jonathan Lahijani) which adds the ability to hide individual images in an images field. When an image is hidden, you can see and work with it in the admin, but it gets removed from the field value on the front-end of the site at runtime, effectively hiding it. I know I'll use this a lot, particularly on photo galleries where I may want to remove an image or two from appearing in the grid of photos, but don't necessarily want to delete them. Images can be hidden (or unhidden) from the Actions select of each image, where you'll see a "Hide" option (or an "Unhide" option if the image is already hidden). Hidden images are also dimmed out when viewing the images field in the admin. On the API side, you can hide or unhide images and files using $image->hidden(true) to hide, $image->hidden(false) to unhide, and $image->hidden() to get a true or false as to whether or not the image is hidden. Though this will only be useful on unformatted field values, since hidden images are automatically removed from formatted field values. The same can be used with regular file fields, but we don't currently have a UI/interface for hiding or unhiding items from regular (non-image) file fields. Likely we'll add one soon, but I figured it's likely to get more use with image fields than file fields, so figured we'd start there. More next week. Thanks for reading and have a great weekend!
- 1 reply
-
- 30
-
-
-
This is my approach to combining CSS files for improved page speed. The implementation is straightforward; you only need to add one PHP file to your server and make a few tweaks. My setup: -site/ -templates/ - inc/ - styles/ _init.php Include the CombinedCSS.php file at the top of my _init.php: include_once('./inc/CombinedCSS.php'); _main.php Add the following to the <head> section: <?php $cssFiles = [ paths()->templates . 'styles/uikit.min.css', paths()->templates . 'styles/uikit.ext.css', paths()->templates . 'styles/main.css' ]; echo CombinedCSS::CSS($cssFiles); ?> CombinedCSS.php <?php namespace ProcessWire; /** * Import the RuntimeException class for throwing runtime exceptions. */ use RuntimeException; /** * Class CombinedCSS * * A class for combining and minifying CSS files and generating a link tag for the combined CSS. */ class CombinedCSS { /** * @var string|null The output directory for saving the combined CSS file. */ private static $outputDirectory; /** * @var string|null Hash of the concatenated content of CSS files. */ private static $filesHash; /** * Initializes the output directory if it is not already set. */ private static function initOutputDirectory() { if (!isset(self::$outputDirectory)) { self::$outputDirectory = paths()->templates . 'styles/'; } } /** * Checks if a directory is writable. * * @param string $directory The directory path to check. * @return bool True if the directory is writable, false otherwise. */ private static function isWritableDirectory($directory) { // Check if the directory exists if (!is_dir($directory)) { return false; } // Check if the directory is writable if (!is_writable($directory)) { return false; } return true; } /** * Combines and minifies an array of CSS files. * * @param array $files An array of CSS file paths. * @return string The combined and minified CSS content. */ private static function combineAndMinifyCSS(array $files): string { $combinedCss = ''; foreach ($files as $file) { // Read the content of each CSS file $cssContent = file_get_contents($file); // Minify the CSS content (you can replace this with your preferred minification logic) $minifiedCss = self::minifyCSS($cssContent); // Append the minified CSS to the combined CSS content $combinedCss .= $minifiedCss; } return $combinedCss; } /** * Minifies CSS content. * * @param string $css The CSS content to be minified. * @return string The minified CSS content. */ private static function minifyCSS(string $css): string { // Replace this with your preferred CSS minification logic // Example: removing comments, extra whitespaces, etc. $minifiedCss = preg_replace('/\/\*.*?\*\//s', '', $css); $minifiedCss = preg_replace('/\s+/', ' ', $minifiedCss); return $minifiedCss; } /** * Generates a unique filename for the combined CSS file based on the provided file paths. * * @param array $files An array of CSS file paths. * @return string The generated combined CSS filename. */ private static function generateCombinedFilename(array $files): string { return md5(implode('', $files)) . '.css'; } /** * Saves the combined CSS content to a file. * * @param string $filename The filename for the combined CSS file. * @param string $content The combined CSS content to be saved. * * @throws RuntimeException If there is an error writing the file to disk. */ private static function saveToFile(string $filename, string $content): void { // Save the combined CSS content to the specified file $filePath = self::$outputDirectory . DIRECTORY_SEPARATOR . $filename; if (file_put_contents($filePath, $content) === false) { $error = error_get_last(); $errorMessage = isset($error['message']) ? $error['message'] : 'Unknown error'; throw new RuntimeException('Failed to write the combined CSS file to disk. Error: ' . $errorMessage); } } /** * Generates a link tag for the combined CSS file based on the provided file paths. * * @param array $files An array of CSS file paths. * @return string The link tag for the combined CSS file. * @throws RuntimeException If the output directory is not writable. */ public static function CSS(array $files): string { // Initialize the output directory self::initOutputDirectory(); // Ensure the output directory is writable if (!self::isWritableDirectory(self::$outputDirectory)) { throw new RuntimeException('The output directory is not writable.'); } // Combine and minify CSS files $combinedCss = self::combineAndMinifyCSS($files); // Check if the files have changed by comparing their hash $currentFilesHash = md5($combinedCss); $combinedFilename = ''; if ($currentFilesHash !== self::$filesHash) { // If the files have changed, generate a unique filename for the combined CSS file $combinedFilename = self::generateCombinedFilename($files); // Save the combined CSS content to a file self::saveToFile($combinedFilename, $combinedCss); // Update the files hash self::$filesHash = $currentFilesHash; } else { // If the files have not changed, check if the file exists on disk $combinedFilename = self::generateCombinedFilename($files); $filePath = self::$outputDirectory . DIRECTORY_SEPARATOR . $combinedFilename; if (!file_exists($filePath)) { // If the file doesn't exist, save the combined CSS content to a file self::saveToFile($combinedFilename, $combinedCss); } } // Return the link tag return '<link rel="stylesheet" href="' . urls()->templates . 'styles/' . $combinedFilename . '">'; } } Notes: Ensure that you have writing permissions for the $outputDirectory location. The combined CSS file will be generated and saved only if the files have changed or if the file doesn't exist on disk. The order of the combined file will adhere to the sequence specified in the array. Modify the hardcoded paths based on your requirements. Any suggestions for improvements are welcome! Cheers!
-
Hi @Robin S. The module is really neat and I've forked and hacked it a couple of times to simplify it for use in my own modules (with accreditation!). I came across an interesting feature of PW recently regarding Page Table fields. If I create a Runtime field and include it as a column in a PageTable field, I do not need to include it in any of the templates managed by the Page Table field (assuming it is only needed to display in the Page Table field). This is a really handy way of providing useful information in the Page Table listing and saves a bit of effort, especially if there are lots of templates. However, the js and css is not then automatically picked up. I fixed this by moving the code in InputfieldRuntimeOnly::renderReady() to FieldtypeRuntimeOnly::renderMarkup() and also moving getPath() and getUrl(). Everything seems to work like this in both the normal (field in template) and novel use. I wondered whether there was a particular reason for putting it in renderReady(), as it would seem more usable in renderMarkup()?
-
How to add a markup field to a repeater item?
szabesz replied to Jonathan Lahijani's topic in API & Templates
Hello, Maybe this can be a good starting point: https://processwire.com/talk/topic/21756-field-access-on-runtime/?do=findComment&comment=187094 -
As part of site development I somethings need to change crop sizes / settings in the codebase after the client has started to add content. This means I can end up with a lot if image variations having been created (and still being used) but with old settings. A good example might be an image variation that was created using a quality of 50 but now the code is set to 100. A new variation is not created under these situations, only if the variation size is changed. So, is there a way to purge all variations leaving the original uploaded image so new variations can be created at page runtime? I have tried the 'PageimageRemoveVariations' module (later v4 version) but that doesn't seem to remove images that are in repeater fields or RepeaterMatrix fields. I'm guessing a solution might have to work at a file level rather than field level? Any help here would be appreciated!
-
Hey @Robin S this is working great so far ? I have one issue when using RockMigration's filesOnDemand feature. That feature hooks into Pagefile::url and Pagefile::filename and if the image does not exist it tries to grab it from the remote server. This is extremely handy when using "rockshell db:pull", because then you have a copy of the remote database on your local dev environment which could have several references to images in the database that do not exist on the filesystem. RockMigrations then downloads those images from the remote server as soon as you visit a page with images that do not exist. This feature can be enabled at runtime by setting $config->filesOnDemand = false; Do you think you can add this to your module to make it work with RockMigrations? Or do you see another way to make it work out of the box? Maybe I can check if the item is in the queue before trying to download it from the remote. See the code here https://github.com/baumrock/RockMigrations/blob/9d9609904647e7b2cfc14a81b2da411edfdca814/RockMigrations.module.php#L2536-L2583 Thx for your thoughts!
-
Admin: prompt user with a choice before saving page
bernhard replied to da²'s topic in General Support
Yes. myrace is just an inputfield that we added during runtime. It is not a field of the page, so $page->myrace will not work. But it is an inputfield of the form that is submitted, so the input post data will have that information available and we can use it for our further processing. -
Admin: prompt user with a choice before saving page
bernhard replied to da²'s topic in General Support
Thx for the additions ? That would be a great use case for using $page->meta() ! It's already there without doing anything and it's also a lot easier to manipulate meta data from within a hook than updating a hidden field's content. Everything needed is doable with just a few lines of code without any modules. The modules do not help here. Quite the contrary, they make things more complicated and less robust. To add a runtime markup field to page edit: <?php $wire->addHookAfter("ProcessPageEdit::buildForm", function (HookEvent $event) { /** @var InputfieldForm $form */ $form = $event->return; $page = $event->process->getPage(); // custom rules where to add the field // eg add it only to pages with template "home" // otherwise exit early and do nothing if ($page->template !== 'home') return; $form->insertAfter([ 'type' => 'markup', 'name' => 'mymarkup', // we need that later ;) 'value' => '<h1>I am a custom markup field</h1>', ], $form->get('title')); }); In this markup field you can show any HTML/PHP markup you want. Now we can also add dynamic selects - also just a single and quite simple hook: <?php $wire->addHookAfter("ProcessPageEdit::buildForm", function (HookEvent $event) { /** @var InputfieldForm $form */ $form = $event->return; $page = $event->process->getPage(); // custom rules where to add the field // eg add it only to pages with template "home" // otherwise exit early and do nothing if ($page->template != 'home') return; // data coming from your file $races = ['foo', 'bar', 'baz']; // add all options $f = new InputfieldRadios(); $f->label = 'Choose your race'; $f->name = 'myrace'; foreach ($races as $race) { $f->addOption($race, "Use Race '$race' for something"); } // now add it after our markup field: $form->insertAfter($f, $form->get('mymarkup')); }); And finally we save the data: Another small hook ? <?php $wire->addHookAfter("Pages::saveReady", function (HookEvent $event) { // get the selected race from the radio input // we sanitize the input to make sure it is a string $race = $this->wire->input->post('myrace', 'string'); if (!$race) return; // save selected race to page meta data $page = $event->arguments(0); $page->meta('myrace', $race); }); Of course we have now two hooks for the same process, so we can combine those two to one and we end up with two hooks which should all you need: <?php $wire->addHookAfter("ProcessPageEdit::buildForm", function (HookEvent $event) { /** @var InputfieldForm $form */ $form = $event->return; $page = $event->process->getPage(); // custom rules where to add the field // eg add it only to pages with template "home" // otherwise exit early and do nothing if ($page->template != 'home') return; $race = $page->meta('myrace'); $time = $page->meta('chosenat') ?: ''; if ($time) $time = date("Y-m-d H:i:s", $time); $form->insertAfter([ 'type' => 'markup', 'name' => 'mymarkup', 'value' => '<h1 class=uk-margin-remove>I am a custom markup field</h1>' . "<p>Last Chosen Race: $race (@$time)</p>", ], $form->get('title')); // data coming from your file $races = ['foo', 'bar', 'baz']; // add all options $f = new InputfieldRadios(); $f->label = 'Choose your race'; $f->name = 'myrace'; foreach ($races as $race) { $f->addOption($race, "Use Race '$race' for something"); } // now add it after our markup field: $form->insertAfter($f, $form->get('mymarkup')); }); $wire->addHookAfter("Pages::saveReady", function (HookEvent $event) { // get the selected race from the radio input // we sanitize the input to make sure it is a string $race = $this->wire->input->post('myrace', 'string'); if (!$race) return; // save selected race to page meta data $page = $event->arguments(0); $page->meta('myrace', $race); $page->meta('chosenat', time()); }); No modules, no rocket science. Just ProcessWire awesomeness ? -
This week there are a few updates on the dev branch. Since I'm short on time today, I'll just briefly cover the most interesting ones. Support for OR-groups, sub-selectors, and match same (1) @item groups (see last example at that link) have been added to ProcessWire's in-memory page matching selectors, as used primarily by the $page->matches() method. Previously these features were supported only by the database selectors from $pages->find() and methods that delegated to it. The $page->matches() method that gained these features is used by the core in various places for runtime matching of pages, such as when finding/filtering in a PageArray, for example. Support has also been added for OR-groups to the base Selectors class, which makes it possible to use OR-groups on non-Page objects too. These database-specific selector features were added because there's been a recurring request to support OR groups in places that currently use memory selectors, so this should do that. Though it'll remain just on the dev branch until it's had more thorough testing. While there will always be some differences between database and memory selectors, this does narrow the gap a bit. Thanks for reading and have a great weekend!
- 1 reply
-
- 23
-
-
-
I know I'm sort of preaching to the choir here, but it's a shame you cannot add images in SF. I think what many devs, me included ,want to use this module for is for global settings which includes things like logo(s). It just makes so much more sense to have these things defined in a separate page rather than by adding fields to the home template but maybe I'm just being too neurotic. Please forgive me if this is a dumb question, but since I have to create a page under the admin page tree to get SF to work, why can't I add images to this very site's /assets/files/ folder? But I guess the answer does something like because the fields I create on the required php or json file don't really exist in the system and are create at runtime?
-
Checkbox Values Not Saving in Custom Module
froot replied to froot's topic in Module/Plugin Development
thanks @bernhard, yes I noticed that later on. I managed to get the module to serve my needs, it's here: https://github.com/dtjngl/MenuAllocator I (re)watched your hook-tutorial, it's interesting but I doubt it can help me in this very use-case since I do not really populate fields on the page and the fields in question are rendered at runtime (by a another hook), so I had a hard time saving the values to the page (luckily there's ->meta). I'm not sure if this is the best approach but it works. @everyone, please give it a try, I'd love to get feedback. -
As far as I know (and I haven't used this feature yet!) this should be possible (found here: https://processwire.com/modules/rock-frontend/) You can make use of the addIf() function to load a script only under special circumstances. Adding assets to your site (JS or CSS) While you can always add custom <script> or <link> tags to your site's markup it is recommended that you use RockFrontend's AssetsArray feature: $rockfrontend->scripts() ->add('/path/to/your/script.js') // you can add any custom flags to your $rockfrontend variable at runtime! ->addIf('/path/to/foo.js', $rockfrontend->needsFooScript) ->addIf('/path/to/slider.js', $page instanceof HomePage) ->addIf('/path/to/blogscript.js', $page->template == 'blogitem') ; $rockfrontend->styles() ->add(...) ->addIf(...) ; There are several reasons why this is preferable over adding custom script/style tags: addIf() keeps your markup file cleaner than using if / echo / endif It automatically adds timestamps of files for cache busting You can inject scripts/styles from within other files (eg PW modules) RockFrontend itself uses this technique to inject the styles and scripts necessary for frontend editing (ALFRED). Have a look at the module's init() method!
-
[solved] Tracy Console almost unusably slow
BrendonKoz replied to bernhard's topic in Tracy Debugger
Oh, specifically for the console. I believe I experienced this once as well. Will try to be more attentive. Hasn't happened since. I am not using any Rock modules currently, though since it's only happened once to me I would lean more toward PC performance over module in my instance. Have you tried a different browser just to see if it's primarily a Chrome (on Mac?) thing? Safari / Firefox... If you have access to a Windows or ChromeOS box, maybe try Chrome there too? Chrome has a task manager. When you notice the issue - assuming you've reduced your Chrome tabs to just the one (?), does the task manager give any additional insight? Similarly, Chrome's DevTools offers a runtime performance monitor (not the same thing as is shown in the above photo)... Just some things to look into. -
PW 3.0.210 broke InputfieldForm.getErrors() with error cache
ryan replied to da²'s topic in General Support
@da² This way of processing a form with the process() method was introduced in PW 3.0.205, so 3.0.210 would have been the first main/master version to support it. The method wasn't present in 3.0.200, so I'm thinking most likely you were using the processInput() method instead? (example here). But it may not matter, as the process() method is just a front-end to the processInput() method to provide a more convenient API usage, as it returns a true or false as to whether the form was processed without errors. But that distinction is significant for your example. It assumes processing the form is a one-time thing and that you aren't going to manually add errors to the form once it has successfully processed, as identifying errors is part of processing. In your case, you are doing a secondary validation manually, adding errors after the form has processed. That's new to me, but if you find it useful then I'm all for supporting it. With regard to "forever cache", the cache is a request cache, not a forever cache. It is a runtime memory-only cache for the one current request, as presumably the same exact form instance won't be processed multiple times in the same request (or at least shouldn't be). The reason for the cache value is that it has to recursively iterate through every single Inputfield in the form to ask each whether it had any errors. For a large form, it can be enough overhead to warrant caching that information, as the method may get called multiple times in the request and have to repeat the same task. But is it worthwhile enough? Maybe not, given your use case. I'll look into dropping it. When you set an error, it will remain in the session until it is displayed. This is because redirects are often involved in forms, in order to prevent re-post by re-load. Form errors would be lost if they weren't remembered until used. This is because the process() method determines if there are errors and returns true (no errors) or false (errors). The processInput() method does not tell you that information and so you'd have to check for errors manually afterwards. So your first example is checking for errors BEFORE you have manually added an error to it (which is what the process method does, and sets that cache), while your second example checks for errors AFTER you have manually added an error. This is why the results are different. Unrelated, but I recommend putting "<?php namespace ProcessWire;" (or any namespace) at the top of your template file, OR setting $config->templateCompile = false; in your /site/config.php file, just so that PW doesn't attempt to unnecessarily compile your template files. Maybe you are already doing this, but just mentioning it just in case. -
So I guess you are calling $page->save() somewhere in that endpoint, right? In that case you can just add whatever runtime property you want to that page and then use that as a flag in your hook: <?php // api endpoint ... $page->isFrontendSave = true; $page->save(); // hook, eg in init.php $wire->addHookAfter("Pages::saveReady", function($event) { $page = $event->arguments(0); if($page->isFrontendSave) { // do something } else { // do something else } });
-
I'm glad you asked this! This should be pretty much a drop in replacement. The only thing you'd have to do to upgrade to the new version would be setting the language associations again and transfer any settings (like global excluded strings and API key) in the module config. Upgrading later will not cause any loss of website content and the work for developers should be minimal. The new features are implemented behind the scenes, so you just get more cool stuff ?. ProcessWire makes it possible to change editors between TinyMCE and CKEditor without re-creating the field, so using CKEditor editor now and then switching to TinyMCE later shouldn't cause any headaches. It really should be as easy of a transition as you can get with how well TinyMCE is concurrently being implemented (props to @ryan). As for the module, Fluency doesn't care what field/editor you use or if it's changed at any time because fieldtypes are detected and initialized by JS at runtime when the UI loads, not on the back end. So good news- I don't believe that there is anything that keeps you from using Fluency now and upgrading later. I've been working regularly on the module lately, my goal is this month or next. I think a wildcard will be testing but I'll be requesting help from the community here when it's time. I want this version to be the one that breaks out of alpha. Many thanks and I'll report back here when I have updates.
- 262 replies
-
- 2
-
-
-
- translation
- language
-
(and 1 more)
Tagged with:
-
RockFinder3 - Combine the power of ProcessWire selectors and SQL
eydun replied to bernhard's topic in Modules/Plugins
I use RockFinder together with runtimemarkup-module https://processwire.com/modules/fieldtype-runtime-markup/ to display various page-data in the backend for the content-superusers. There are also some tables in the database that are non-processwire-tables, which I would like to display with RockFinder (if it is possible?). There is my code in the markup-field (it is working): $modules = $this->wire()->modules; $rockfinder = $modules->get("RockFinder3Master"); $rf = $rockfinder->find("template=categories")->addColumns(['id', 'title', 'created']); ob_start(); $rf->dump(); $result = ob_get_clean(); return (string)$result; -
Okay understood. About the dev environment, I must say that it's not intended to change your habits. For example, my usual directory structure of my web folder containing the index.php/site/wire is the following: . └── /var/ └── html └── example.com ├── build/ssr/ssr.js <-- run by NodeJS on the server ├── wwwroot <-- directory sent to server hosting │ ├── site │ │ ├── modules │ │ │ └── ViteJS │ │ ├── assets │ │ │ └── static <-- contain assets built by vite │ │ │ ├── layout.abc123.js │ │ │ ├── home.abc123.js │ │ │ ├── about.abc.123.js │ │ │ └── ... │ │ ├── ... │ │ └── templates │ │ ├── _func.php │ │ ├── home.php │ │ └── about.php │ ├── wire │ ├── index.php │ └── .htaccess ├── src <-- development directory & files │ ├── styles │ │ └── app.scss │ └── js │ ├── components │ │ ├── Button.svelte │ │ ├── Hero.svelte │ │ └── ... │ ├── pages │ │ ├── Home.svelte │ │ ├── About.svelte │ │ └── ... │ ├── app.ts │ └── ssr.js ├── .env ├── package.json ├── vite.config.ts ├── tailwind.config.cjs ├── tsconfig.json └── ... The structure can be tweaked as you want. In this example, I'm using TailwindCSS, and Svelte. When developing, the result in your browser will be refreshed when you will save the modified file (it's called HMR for Hot Module Replacement). The same thing will happen when you will modify your ProcessWire templates files. In dev mode, files are served by the vite's local dev server. About your question about the "autobuild", with Vite you have basically three commands. Take a look at the following scripts section of a package.json file: "scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview" }, When you will run `npm run dev` for example, vite will start the development server, the build command will build the production files, and the preview command will start a local server to serve the built production files. You are free to set up any workflow you are interested in, for example like throwing a build on each HMR event (I've given an example about that on stackoverflow, see https://stackoverflow.com/a/69632090/774432). On my side I am mostly working with private servers, but behind the scene the workflow is quite "the same". On my server, I run on closed environment a simple node command `node ../build/ssr/ssr.js` , where eg. on Vercel, you will want to use their Node.js Runtime features through an API. As I said, you can keep your habits in terms of editing content, let them log in ProcessWire backend and filling their body field. The update/preview is just a matter of refreshing or changing page on the site you are working on. The change is dynamic, you are not required to build or commit any change, and as always, you keep the freedom to make only the content you decide to be available - it's a bit , I made you an example on the bottom of the post. I do not use it and I don't remember how it works, but if you can pull data without markup, then all is ✅ even with markup it should work. I will test it. Enregistrement #52.mp4
-
Ok great, I thought I might be missing something, which can easily happen if you don't use the "regular" approach any more ? For me the runtime approach in general is a huge benefit. Once you start defining things in code all the little details that you would usually avoid because the GUI does not support it suddenly become a no-brainer and very easy to add. All the little customisations that you'd usually need a hook or a module for. They are simply there in your pageclass and you start to develop the backend (page editor) similar to how you'd develop your frontend. <?php public function editForm($form) { if ($f = $form->get('title')) { $f->appendMarkup(" <div>Custom field markup without any hooks...</div> <style>#wrap_Inputfield_title {border: 5px solid red;z-index:1;}</style> <script>console.log('js-demo');</script> "); } } PS: Note that this code will only be added on the page editor of pages having the pageclass where you defined your editForm() method! So if you added that in HomePage.php you'll only get the red border on the "home" page ?
-
Hey @gebeer working with fieldsets in migrations has always been a bit of a pain for me. That's why I'm using them only at runtime in all of my projects. I don't see any drawback with this. Using MagicPages it is as simple as doing this: <?php ... public function editForm($form) { ... $rm->wrapFields( // current form wrapper $form, // fields inside the fieldset [ 'foo' => ['columnWidth'=>33], 'bar' => ['columnWidth'=>33], 'baz' => ['columnWidth'=>33], ], // fieldset settings [ 'label' => 'Your Runtime Fieldset', 'icon' => 'bolt', ] ); } This is a lot quicker than messing around with fields, field order, moving the ending field to the correct place etc. I only place the fields on the correct template, which is necessary for the fields to be queryable by selectors etc and all the rest is done in code in the pageclass. That also makes it possible to switch layouts based on the user's role for example. So the approach is very powerful. I don't see that as a limitation. Or are there any things that I'm missing?
-
@bernhard At https://github.com/baumrock/RockMigrations#working-with-fieldsets you have e nice description on how to work with fieldsets. This seems to be limited to adding fields to fieldsets at runtime. Is there a way how we can define a migration that puts fields inside a fieldset in template context? Something like 'content-page' => [ 'tags' => 'content', 'fields' => [ 'title' => [ 'label' => 'Page Title', ], 'text' => [ 'label' => 'Subtitle', ], 'content_blocks' => [], 'tab' => [], // this adds the fieldsettab open and close 'fieldname' => [] // how to add this one to the tab fieldset ], It works fine with with $rm->addFieldToTemplate('fieldname', 'content-page' , 'tab', 'tab_END'). Just wondering if it is possible to do this inside the fields array in my example above.