Jump to content

ukyo

Members
  • Posts

    296
  • Joined

  • Last visited

  • Days Won

    10

Everything posted by ukyo

  1. I’ve been using Contabo since March 2015. My journey started with a VPS M + cPanel setup, later upgrading to VPS L. When cPanel changed its pricing policy, I switched to a VPS XL paired with RunCloud. Handling email support directly from the server was a major time drain and source of headaches for me. One of the main reasons I chose RunCloud was to strictly host websites on the server. This allowed me to focus on my actual work without getting bogged down by email delivery issues. However, I eventually started experiencing sporadic issues with shared resources (RAM, CPU, network speed) on the standard VPS instances. I switched to their VDS (Virtual Dedicated Server) solutions and used them problem-free for a long time. Later, I needed a panel that could manage Docker, Node.js, and PHP all in one place, which led me to Ploi.io. After testing it thoroughly, I cancelled my long-standing RunCloud service and fully migrated to Ploi. During this transition, I took advantage of a Black Friday deal at Contabo where I could get a Dedicated Server for the same price I was paying for the VDS. Right now, I manage 3 servers via Ploi: Contabo Dedicated (Germany location) Contabo VDS (Australia location) SH VDS (Turkey location) My advice: You might run into "noisy neighbor" issues with Contabo's standard VPS plans, but in my experience, their VDS servers offer a great price/performance ratio. If you are planning to host 40 sites with heavy traffic on a single machine, I strongly recommend choosing between a VDS or a Dedicated server rather than a standard VPS. If you decide to give Ploi.io a try, feel free to use my referral link: https://ploi.io/register?referrer=3XC1ka9hnGeqGiAPX5va Also, if you need the Nginx configuration for ProcessWire, let me know—I can share the specific template I created for Ploi.
  2. @BrendonKoz breakpoint and aspect ratios not comma separated, its line by line config. its a copy paste input field issue. I am using this external library on all my project. I decide to make it a module. Now its a module. There is base Pageimage library problems for me and there is no solution until now: https://github.com/processwire/processwire-issues/issues/2048 https://github.com/processwire/processwire-issues/issues/2016
  3. Hi everyone, This module completely replaces the default ProcessWire image sizing engine with the powerful Intervention Image v3 library. The goal was to modernize how we handle images in ProcessWire, bringing in features like AVIF support, superior resizing quality, and strict aspect-ratio handling, while keeping the API compatible with what you already know. 🚀 What does it do? Replacement: It hooks into Pageimage. You can keep using $image->width(300), $image->size(800, 600), or $image->crop(...) just like you always have. Modern Formats: Automatically handles WebP and AVIF generation. Smart Responsive Images: It introduces a configuration-based approach where you define Breakpoints, Grid Columns, and Resizing Factors. The module uses these settings to automatically calculate and generate the perfect srcset for your layouts. ✨ New Methods: render() and attrs() While standard methods work as expected, I’ve added/updated methods to handle modern HTML output: 1. $image->render(string $preset, array $options) This outputs the complete HTML tag. It automatically handles: The <img> tag with srcset and sizes. The <picture> tag with <source> elements if you have enabled extra formats (like AVIF/WebP) in the settings. Lazy Loading & LQIP: It automatically generates a Low Quality Image Placeholder (pixelated/blur effect) and applies a base64 background to the image tag for a smooth loading experience. // Example: Render a 'landscape' preset defined in module settings echo $page->image->render('landscape', ['class' => 'my-image']); 2. $image->attrs(string $preset, array $options) Perfect for developers who use template engines like Twig or Latte, or prefer full control over their HTML. This returns an array of attributes instead of an HTML string. $data = $page->image->attrs('landscape'); // Returns array like: // [ // 'src' => '...', // 'width' => 1200, // 'height' => 675, // 'srcset' => '...', // 'sources' => [ ... ], // Array for picture tag sources // 'style' => 'background-image: url(data:image...);', // LQIP Base64 // 'class' => 'iv-lazy ...' // ] ⚙️ Configuration Strategy Instead of hardcoding sizes in your templates, you configure your design tokens in the module settings: Breakpoints (e.g., 1200px) Aspect Ratios (e.g., 16:9) Grid Columns (e.g., 1-1, 1-2, 1-3) Factors (e.g., 0.5, 1, 1.5, 2 for Retina support) The module calculates the necessary image dimensions based on these combinations. If you request a specific aspect ratio, it ensures strict adherence to it, preventing 1px rounding errors. 📝 A Note on Documentation I wanted to get this into your hands as soon as possible, but due to a heavy workload, I haven't finished writing the detailed README.md yet. Currently, you can grab the code from GitHub (link below). I will submit this to the official ProcessWire Modules Directory after add some other features and after update readme.md Download / GitHub: GitHub Repo I’d love to hear your feedback and suggestions!
  4. Another one is Cockpit Headless CMS nice and light...
  5. 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
  6. I see :(, fork and update old module look like best solution for Pages2Pdf module users
  7. @markus-th @Klenkes 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.
  8. 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 );
  9. @markus-th You can check @maximus module https://github.com/mxmsmnv/WirePDF
  10. 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;']);
  11. @Ivan Gretsky 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(); } });
  12. 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.
  13. @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.
  14. 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/2048 i don't want to do that :(, i am not using Pageimage class for creating images variations anymore.
  15. 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.
  16. @bernhard 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!
  17. 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.
  18. 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()
  19. @aagd 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 https://github.com/trk/Component/blob/8b973cca955a29fd60c933da9ff9a6d35f5e518b/components/example/templates/template-default.php#L11 Method 2 classic way https://github.com/trk/Component/blob/8b973cca955a29fd60c933da9ff9a6d35f5e518b/components/example/templates/template-default.php#L24
  20. 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.
  21. @MarkE 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 Ekran Kaydı 2023-10-12 13.33.49.mov
  22. 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
  23. 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.
  24. 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.
  25. 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.
×
×
  • Create New...