Jump to content

ukyo

Members
  • Posts

    301
  • Joined

  • Last visited

  • Days Won

    10

Everything posted by ukyo

  1. @BitPoet Thank you for info 🙂 I updated video
  2. @gerritvanaaken @pideluxe Yes, it automatically takes over all mail sending. I've updated the module and added OAuth2 support, but I haven't tested Azure specifically. You can follow the instructions here: https://github.com/trk/WireMailPHPMailer#xoauth2-google-microsoft-yahoo-azure-support Please test it and let me know the results, or feel free to submit a PR if any fixes are needed!
  3. @Ivan Gretsky I don't know why but video is there and I can see 🙂
  4. If you love the simplicity of ProcessWire's API but want the reactive, SPA-like feel of modern frontend frameworks without writing complex JavaScript, this module bridges that gap. It brings native Server-Side Component state hydration, Out-Of-Band (OOB) swaps, and strict security to your ProcessWire application using HTMX. 🚀 What does it do? This module transforms how you write frontend components in ProcessWire: True Stateful Backend Components: It introduces Component and Ui base classes. Your PHP components automatically rehydrate their state (variables, dependencies, $page assignments) between HTMX AJAX requests! No need to manually parse POST payloads. Auto-Discovery: Just place your components in your site directories. The module automatically discovers and securely namespaces them (Htmx\Component and Htmx\Ui). Zero-Javascript Reactivity: You can handle form submissions, counters, validation, and multi-field updating dependencies directly from PHP using HTMX attributes. Cryptographic Security: The module uses strict HMAC-SHA256 signatures with TTL (Time-To-Live). This guarantees that bad actors cannot modify state payloads or trigger invalid endpoint logic in the browser. WebSockets & SSE Ready: It has built-in helpers to easily hook Server-Sent Events (SSE) and WebSockets onto your templates without exhausting PHP-FPM pools globally. 🛠 How it looks in your code You simply create a PHP component class, define some public properties, and write an endpoint action: <?php namespace Htmx\Component; use Totoglu\Htmx\Component; class ClickCounter extends Component { public int $count = 0; public function increase() { $this->count++; } } Then, you can render it anywhere in your site with a single line: /** @var Htmx $htmx */ echo $htmx->renderComponent(ClickCounter::class); View the documentation and examples on Github Feel free to try it out, run the tests included in the repo, and let me know your thoughts or feedback! htmx.mp4
  5. ukyo

    AiWire

    This is what I am working on :)
  6. 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.
  7. @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
  8. 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!
  9. Another one is Cockpit Headless CMS nice and light...
  10. 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
  11. I see :(, fork and update old module look like best solution for Pages2Pdf module users
  12. @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.
  13. 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 );
  14. @markus-th You can check @maximus module https://github.com/mxmsmnv/WirePDF
  15. 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;']);
  16. @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(); } });
  17. 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.
  18. @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.
  19. 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.
  20. 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.
  21. @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!
  22. 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.
  23. 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()
  24. @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
  25. 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.
×
×
  • Create New...