-
Posts
293 -
Joined
-
Last visited
-
Days Won
8
Posts posted by ukyo
-
-
Also you can check out Sulu CMS based on Symfony Framework, Here is a skeleton project its like ProcessWire profile. its supports live-editing, block field [free] (its like repeater matrix), form-bundle [free] (like form builder), content-types, multil-language, multi-site, multi-database.....
You can set content types from xml config file, located at config/templates
You can set multi-domain and domain languages from xml config file, located at config/webspaces
also it has snippet support...
You can check the demo
-
I see :(, fork and update old module look like best solution for Pages2Pdf module users
-
If you're looking to update Pages2Pdf, I recommend creating a new module with the same name but extending WirePDF instead. It’s a much more efficient way to modernize the module without reinventing the wheel.
<?php namespace ProcessWire; // Instead of a full rewrite, just extend the new module class Pages2Pdf extends WirePDF { public static function getModuleInfo() { return [ 'title' => 'Pages2Pdf extends WirePDF', 'version' => '1.0.0', 'requires' => 'WirePDF' ]; } }
Tip: You can use WirePDF for all new projects. To support older projects without a full rewrite, simply create a Pages2Pdf wrapper that extends WirePDF. This way, you stay up to date with the latest PDF engine improvements while keeping your existing codebase intact.
-
A quick WireIconifyData class or your name it as IconifyWireData, IconifyValue
<?php namespace ProcessWire; class WireIconifyData extends WireData { public function __construct(?array $data = null) { parent::__construct(); // set defaults if (!is_array($data)) { $data = [ 'raw' => null, 'set' => null, 'name' => null, 'path' => null, 'url' => null, 'svg' => null, ]; } $this->setArray($data); } public function __invoke(array $attrs = [], bool $withHeader = false) { return $this->render($attrs, $withHeader); } public function render(array $attrs = [], bool $withHeader = false): string { if (!$this->get('svg')) { return ''; } try { $xml = new \SimpleXMLElement($this->get('svg')); foreach ($attrs as $key => $value) { if (isset($xml->attributes()->{$key})) { $xml->attributes()->{$key} = $value; } else { $xml->addAttribute($key, $value); } } // Return XML without the header declaration for clean HTML embedding $out = $xml->asXML(); return $withHeader ? $out : preg_replace('/^<\?xml[^?]*\?>/i', '', $out); } catch (\Exception $e) { wireLog('error', "SVG icon: {$this->get('name')} error => {$e->getMessage()}"); return ''; } } public function __toString(): string { return $this->render(); } }
Usage:
<?php // render echo $page->icon_field; // render with attrs echo $page->icon_field->render( attrs: ['width' => 40, 'style' => 'color: red;'] ); // render with attrs and header echo $page->icon_field( attrs: ['width' => 40, 'style' => 'color: red;'], withHeader: true );
-
-
You can use a IconifyValue class for customized output or here is a function for customize svg output
<?php function getIcon(?WireData $icon, array $attrs = []): string { if (!$icon) { return ''; } try { $xml = new \SimpleXMLElement($icon->svg); foreach ($attrs as $key => $value) { if (isset($xml->attributes()->{$key})) { $xml->attributes()->{$key} = $value; } else { $xml->addAttribute($key, $value); } } // Return XML without the header declaration for clean HTML embedding $out = $xml->asXML(); return preg_replace('/^<\?xml[^?]*\?>/i', '', $out); } catch (\Exception $e) { wireLog('error', "SVG icon: {$icon->name} error => {$e->getMessage()}"); return ''; } } echo getIcon($page->icon, ['width' => 40, 'style' => 'color: red; border: 1px solid blue;']);
-
1
-
-
Just use Page::render
$wire->addHookBefore('Page::render', function (HookEvent $event) { /** * @var Page $page * @var WireInput $input */ $page = $event->object; $input = $event->wire()->input; // Use get method: // example.test/about?segment=home $segment = $input->get('segment'); // Allow template url allow segments otherwise you will get 404 error $segments = $input->urlSegments(); // Validate by template $page->template->name != 'home'; if ($segment === 'home' || $page->template->name != 'home') { /** @var Page $home */ $home = $event->wire()->pages->get(1); $event->replace = true; $event->return = $home->render(); } });-
1
-
-
Do you mean something like this ?
$segments = ['earth', 'mars', 'jupiter']; $wire->addHook('(/.*)/(' . implode('|', $segments) . ')', function (HookEvent $event) use ($segments) { $path = $event->arguments(1) . '/'; try { $page = $event->pages->get($path); } catch (\Throwable $th) { $page = new NullPage(); } if ($page->id) { header('Content-Type: application/json'); echo json_encode([ 'path' => $path, 'segment' => $event->arguments(2), 'segments' => $segments, 'page' => $page->title ]); exit; } });
---
$segment = 'get'; $uri = $_SERVER['REQUEST_URI'] ?? ''; if (str_ends_with(rtrim($uri, '/'), $segment)) { $wire->addHook($uri, function (HookEvent $event) use ($segment, $uri) { list($path, $empty) = explode($segment, $uri); try { $page = $event->pages->get($path); } catch (\Throwable $th) { $page = new NullPage(); } if ($page->id) { header('Content-Type: application/json'); echo json_encode([ 'path' => $path, 'segment' => $segment, 'page' => $page->title ]); exit; } }); }
This will add a global url hook for url ends with requested segments and you can get page from first url segment than you can print anything you want.
-
1
-
-
@elabx There are several methods to use Vite or Alternatives with ProcessWire. @wbmnfktr's suggestion is one of them. You can make certain checks in your vite.config.js file and implement this within your config file as well. You can send extra parameters and check these parameters in your config file to perform operations accordingly, for example:
npm run build --my-parameter=tinymce.You can also use Dotenv and create multiple environment files like
.env-tinymce,.env-frontend,.env-another-style, and then pass a parameter to your build script:npm run build --env=tinymce. After that, you can do what you want inside your vite.config.js file.When you install this module you will have simple vite setup under site/templates directory. There is an .env file, we use this file for live reloading.example .env file also you can use it for your needs.
-
2
-
-
There are some issues about Pageimage class, when creating webp
https://github.com/processwire/processwire-issues/issues/2016
https://github.com/processwire/processwire-issues/issues/2048i don't want to do that :(, i am not using Pageimage class for creating images variations anymore.
-
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.
-
6
-
1
-
Yes, I know about it. However, I prefer not to use modules for these kinds of things, as my work approach would need to adapt to each module used.
I am using Vite for these purposes instead.
Before developing this script, I was using the AllInOneMinify module for ProcessWire. Later, I switched to JavaScript-based compilation solutions:
- First moved to Gulp with Browser Sync
- Then migrated to Webpack
- Currently using Vite
Among these tools, Vite has proven to be the lightest solution, and I'm getting exactly the results I want with my Vite configuration. While using Vite, I wondered "Why don't I implement my own browser sync solution?" This led me to write this lightweight script, which works remarkably well.
And I'm happy with this setup!
-
3
-
This hook is used to monitor file changes and page updates in your ProcessWire project. It operates when debug mode is active and returns file changes and latest page update information in JSON format.
The script hook is automatically injected before the </body> tag in your HTML. It periodically checks for any file or page updates in your system at the interval you specify. If the previous check time is older than the new check time (indicating changes), the page automatically refreshes.
~/site/ready.php
if ($wire->config->debug) { $wire->addHookAfter('Page::render', function (HookEvent $event) { $script = <<<HTML <script> const browserSync = { mtime: 0, init() { this.sync(); }, sync() { fetch('/browser-sync') .then(response => response.json()) .then(data => { if (!this.mtime) { this.mtime = data.max; } else { if (data.max > this.mtime) { location.reload(); } } }); } }; browserSync.init(); setInterval(() => browserSync.sync(), 2500); </script> HTML; $event->return = str_replace('</body>', "{$script}</body>", $event->return); }); $wire->addHook('/browser-sync', function (HookEvent $event) { $wire = $event->wire(); $root = $wire->config->paths->root; $siteRoot = $wire->config->paths->site; $extensions = '*.{php,js,css}'; $paths = [ "{$root}src/{$extensions}", // custom composer package "{$root}src/src/{$extensions}", // custom composer package "{$siteRoot}{$extensions}", // ready.php, init.php, finished.php "{$siteRoot}classes/{$extensions}", // page classes "{$siteRoot}templates/{$extensions}", "{$siteRoot}templates/*/{$extensions}", "{$siteRoot}templates/*/*/{$extensions}", "{$siteRoot}templates/*/*/*/{$extensions}", "{$siteRoot}templates/*/*/*/*/{$extensions}", ]; $files = []; foreach ($paths as $pattern) { // Try with GLOB_BRACE first $result = glob($pattern, GLOB_NOSORT | GLOB_BRACE); // If no results with GLOB_BRACE, try without it if (!$result) { $result = glob($pattern, GLOB_NOSORT) ?: []; } $files = array_merge($files, $result); } $filemtime = 0; foreach ($files as $file) { if (!is_file($file)) { continue; } try { $mtime = @filemtime($file); if ($mtime && $mtime > $filemtime) { $filemtime = $mtime; } } catch (\Exception $e) { continue; } } $latest = $wire->pages->get('sort=-created|-modified'); $latestTime = $latest->modified > $latest->created ? $latest->modified : $latest->created; header('Content-Type: application/javascript'); echo json_encode([ 'max' => max($filemtime, $latestTime), 'mtime' => $filemtime, 'modified' => $latest->modified, 'created' => $latest->created ]); exit; }); }
This development tool helps streamline your workflow by automatically refreshing the browser when you make changes to your files or content, eliminating the need for manual page refreshes during development.
-
12
-
-
Before class initialized you can't access the languages.
class HomePage extends DefaultPage { public function __construct(Template $tpl = null) { parent::__construct($tpl); // not initialized at here echo '<pre>' . print_r($this->wire()->modules->get('LanguageSupport'), true) . '</pre>'; } public function lang(){ // now you have some data on LanguageSupport module echo '<pre>' . print_r($this->wire()->modules->get('LanguageSupport'), true) . '</pre>'; // get default language echo '<pre>' . print_r($this->wire()->languages->getDefault(), true) . '</pre>'; // get available page languages echo '<pre>' . print_r($this->getLanguages(), true) . '</pre>'; } }
its same for modules. you can access languages inside init(), ready() methods. not inside __construct()
-
1
-
1
-
-
I pushed an update, this update has fix for attributes to string fix and added an example component. This component contains example on readme file with 2 different output solution.
Method 1 is commented
Method 2 classic way
-
1
-
-
Hi @aagd
Example code is combination of https://getuikit.com/docs/heading and https://getuikit.com/docs/text. Size, decoration, transform, color and align params adds classes to component. If you include uikit 3 on your project or if you check the output. You will see the result on component output.
-
The general problem in page builders is what frontend framework support they offer. Does the frontend framework support they provide align with your design, or do you need to learn and adapt to this frontend framework?
The same problem exists in ProcessWire as well. When we want to use the fields on the frontend, we often need to make numerous modifications to achieve the desired results.
I also have my own page builder structure created using the PageTable module, and I've been using this structure for about six years, continuously updating it on my end. The primary reason for creating the Mystique module is to dynamically create custom fields within the page builder and manage them as I like. My custom page builder is built on the Uikit 3 framework, but when I want to use Tailwind CSS or Bootstrap in the frontend, the current system doesn't support this. One of the reasons is that I primarily use the Component module and have created a separate module called UikitComponent to configure Uikit components within this module. I aim to apply this for Bootstrap, Tailwind CSS, or any other frontend framework or design.
In addition to this, another fundamental reason for writing the Component module is to be able to provide readable and understandable outputs for the requests sent to the page in the HTMX module that I am preparing for publication.
In the project I'm working on, I'm using the HTMX, Component, and Mystique modules together.
I created a component called 'product-list' and added extra settings to this component, including 'element' => true and 'fields' => array(). In the 'fields' section, I added configuration settings for the Mystique module. I added the 'element' => true setting to use this component in my page builder, making it accessible both in the code and through the admin panel. This way, the component can be used on the code side and added to the desired location by the administrator through the admin panel.
Also you can check a javascript solution: https://github.com/codex-team/editor.js I wrote a module and have some experience with this. Output is clean json data
-
7
-
-
Module help you to create and use set of components to utilize in your ProcessWire.
You can find more info and an examples on Github repo : https://github.com/trk/Component/tree/main
-
6
-
-
1 hour ago, Jan Romero said:
Robin’s suggestion of using a custom page class should work for that, just override get():
<?php namespace ProcessWire; class DefaultPage extends Page { private $cache = [ 'title' => 'look at me. i’m the title now.' ]; public function get($key) { return $this->cache[$key] ?? parent::get($key); } }
IRL you may want to add logic dealing with output formatting and what not, but that should be a complete working example. Unfortunately get() is not hookable, so I don’t believe this can be accomplished in a module.
That said, have you investigated other avenues? Why not cache the whole markup and be done with it?
This seems like the most reasonable solution.
When pages are updated or deleted, the data will be automatically updated or removed. I can manage the templates and fields that will be cached easily through module settings. The desired fields and templates are cached, while other fields and templates continue to function as usual in ProcessWire.
I have three reasons for obtaining the raw JSON output:
1. To fetch the data and display it directly in the desired format. I don't want the data to be influenced by HTML, so I can use it anywhere.
2. To generate static pages using the data (using libraries like Astro.js).
3. To make the saved data accessible for other applications through an API.

-
18 hours ago, Jan Romero said:
Maybe you can hook Fieldtype::loadPageField()? https://github.com/processwire/processwire/blob/6ff498f503db118d5b6c190b35bd937b38b80a77/wire/core/Fieldtype.php#L1108
When using this method, the data is retrieved in its raw form. It requires further processing to display the data. When $page->title is called, the data is initially fetched from the database using this method and undergoes certain operations before appearing as $page->title. The part I want to intervene in is as follows: If the data is present in the saved JSON, show it directly without processing; otherwise, retrieve the data from the database, process it, and display it.
-
Sure,
However, I don't want to make any changes to regular page displays. When this option is enabled in the module settings, I want it to directly reflect on the page.
I am looking for a solution while continuing to use the normal page API.
When I use $page->title and if the transfer is enabled for the title field in the module settings and it has been saved as JSON, I want this information to come from the JSON data.
When I remove the title field from the module settings, I want this information to be retrieved and displayed using SQL.
-
When I checked a page I created with ProcessWire, I noticed that it was using a lot of SQL queries when displaying a page (600+ SQL queries). This page contains a page builder and similar page links from other pages. The number of queries seemed excessive to me, and I believe it is because a separate table is created for each field.
Therefore, I have observed that the number of SQL queries increases based on the number of fields when accessing page data.
To address this issue, I have implemented a solution where I save or delete the page data as JSON when the page is saved or deleted (I also want to use this module when generating static pages).
If a field needs to be displayed on the page, I want it to be shown from the JSON output if it exists there, otherwise I want it to be retrieved with an SQL query.
-
I want to track and modify all called $page->* (fields) properties, before property get a value or after property get a value. is it possible to do it with a method like hooks ??
like: id, title, body, image
<?php $wire->addHookProperty('Page::*,Page::[id,title,body,image]', function(HookEvent $e) { $page = $e->object; // How to get name of property ??? $property = '???'; $isCached = true; if ($isCached) { $cachedValue = 'Cached value from somewhere'; $value = $cachedValue; } else { $value = $page->get($property); } if ($property === 'title') { $value = "<h1>{$value}</h1>"; } $e->return = $value; });
-
wire()->addHookAfter('InputfieldRepeater::render', function (HookEvent $e) { /** * @var InputfieldRepeater $repeater * @var Page $page Repeater page */ $repeater = $e->object; $page = $repeater->hasPage; if ($repeater->getAttribute('name') == 'repeater_name') { $return = $e->return; $e->return = $return . 'Hello World !'; } });-
1
-

Other ProcessWire-like CMSs
in Pub
Posted
Another one is Cockpit Headless CMS nice and light...