Jump to content

All Activity

This stream auto-updates

  1. Yesterday
  2. I suck when it comes to designs, I am going to explore this and update, thanks so much because I code more than I design so you saying it's good has made my day.
  3. I love these clean minimal styles. Have you tried to add some nice monospace font? Could make a nice contrast in some places.
  4. As usual this has been my ritual for years, changing my website and it's always been Processwire as usual, this time I went for a minimal look.
  5. The custom AJAX endpoints that RockFrontend is using are basically PHP files located in: /site/templates/ajax/ So the requests go to: http://www.mysite.com/ajax/myEndpoint To prevent these requests from being blocked I tweaked this piece of module code: // WireWall.module.php line 1140 // Check if request URL contains /processwire/ or /admin/ or /ajax/ $requestUri = $_SERVER['REQUEST_URI'] ?? ''; if (stripos($requestUri, '/processwire/') !== false || stripos($requestUri, '/admin/') !== false || stripos($requestUri, '/ajax/') !== false) { return true; }
  6. @Robin S That looks great, thanks! Will take that as a starting point and see where it takes me. Probably make this configurable behind a proper inputfield setting.
  7. Another one is Cockpit Headless CMS nice and light...
  8. Last week
  9. 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
  10. I see :(, fork and update old module look like best solution for Pages2Pdf module users
  11. @ukyo Thanks for the suggestion! I considered that approach, but there is a significant technical blocker: Class Name Collision. The legacy Pages2Pdf module already defines a global class named WirePDF internally. The module you referenced also uses the global class WirePDF. If I were to build a wrapper or if a user tries to migrate from the old module to the new one, PHP would throw a Fatal Error: Cannot redeclare class WirePDF immediately if both files happen to be loaded (which can happen easily during uninstall/install processes or if site caches aren't perfectly cleared). My goal with Wire2Pdf is to cleanly separate the logic using Namespaces (e.g., ProcessWire\Wire2Pdf). This ensures: Zero conflicts: It can theoretically exist alongside the old module during migration/testing without crashing the site. Full Control: I can ensure the mPDF library is updated and configured specifically for ProcessWire's needs without depending on a third-party wrapper that relies on global class names. So, while extending would be "dryer", a fresh, namespaced module is the safer and more robust path forward.
  12. Maybe I have a few little goodies that you might like 😉 Wire2PDF (first Alpha) What's new? Custom fonts You can upload and use own fonts Set PDF metadata (defined with variables or custom by a specific templatefield title, subject, keywords, author and creator Create PDF/A-1b for font embedding Now i have to test before it is ready to publish.
  13. @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.
  14. I still have several projects that rely on it. Mostly on PHP 7.4 but one website uses Pages2Pdf 1.1.7 by @Wanze on PHP 8.4 just fine. Any pain points? Mhh... none that I can think of right now...
  15. Thanks for sharing. I’ve actually looked into that repository. The main issue I see is that it uses the class name WirePDF, which causes a naming collision because the original Pages2Pdf module already defines/uses a WirePDF class internally. This makes migration difficult and would crash the site if both are present. My goal with Wire2Pdf is to use proper Namespaces (e.g. ProcessWire\Wire2Pdf) to ensure PHP 8 compatibility without risking these kinds of class name conflicts.
  16. WireNPS is a comprehensive module for collecting and analyzing customer feedback via a modern Net Promoter Score (NPS) popup interface. Key Features: Elegant NPS Widget: A non-intrusive, mobile-friendly popup for ratings (0-10) and text feedback. Real-time Analytics: Dashboard featuring NPS score calculation, score distribution charts, and 30-day trend graphs. Multilingual Support: Built-in support for English, German, French, and Chinese with automatic browser/user language detection. Flexible Access: Support for both logged-in users and guests (Public/Private modes). Data Export: CSV export functionality for external analysis. Privacy Control: Configurable IP/User Agent tracking and cookie management. Requirements: ProcessWire 3.0+ PHP 8.2+ Quick Installation: Download or git clone into /site/modules/WireNPS/. Install via the ProcessWire admin. Create an AJAX handler page using the provided template. GitHub: https://github.com/mxmsmnv/WireNPS
      • 5
      • Like
      • Thanks
  17. 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 );
  18. Thanks @ukyo. I'm now thinking that the module itself should remove the XML declaration when setting the svg property in the formatted value, seeing as the most likely use would be for inline SVG. Do you agree? If so I'll release an update.
  19. @markus-th You can check @maximus module https://github.com/mxmsmnv/WirePDF
  20. @d'Hinnisdaël, I was curious about organising a PageArray into a hierarchy, so here is some hook code for modifying a normal InputfieldCheckboxes (when used with a Page Reference field) that you could experiment with. $wire->addHookBefore('InputfieldCheckboxes::render', function(HookEvent $event) { /** @var InputfieldCheckboxes */ $inputfield = $event->object; $field = $inputfield->hasField; if(!$field || $field->name !== 'YOUR_FIELD_NAME') return; function buildHierarchy($pagearray) { // Organise pages by their parent ID and collect all IDs $grouped = []; $itemsById = []; $allIds = []; foreach($pagearray as $page) { $itemsById[$page->id] = $page; $allIds[$page->id] = true; $grouped[$page->parent->id][] = $page; } // Find orphaned parents - parent IDs that don't exist in the PageArray $orphanedParents = []; foreach($grouped as $parentId => $items) { if($parentId !== null && !isset($allIds[$parentId])) { $orphanedParents[] = $parentId; } } // Recursive function to build children function buildChildren($parentId, &$grouped, &$itemsById) { if(!isset($grouped[$parentId])) { return []; } $children = []; foreach($grouped[$parentId] as $item) { $node = clone $item; // Not using "children" as the property name here to avoid clashing with native property $node->nodeChildren = buildChildren($item->id, $grouped, $itemsById); $children[] = $node; } return $children; } // Build trees for all orphaned parents $hierarchy = []; foreach($orphanedParents as $parentId) { $hierarchy = array_merge($hierarchy, buildChildren($parentId, $grouped, $itemsById)); } return $hierarchy; } function renderCheckboxesList($items, $inputfield) { $out = "<div class='nested-checkboxes-list'>"; foreach($items as $item) { $label = $item->getFormatted('title'); $checked = ''; if($inputfield->isOptionSelected($item->id)) $checked = " checked='checked'"; $out .= "<div class='nested-checkboxes-item'><label><input$checked type='checkbox' name='{$inputfield->name}[]' value='{$item->id}' class='uk-checkbox'><span class='pw-no-select'>$label</span></label>"; if($item->nodeChildren) $out .= renderCheckboxesList($item->nodeChildren, $inputfield); $out .= "</div>"; } $out .= "</div>"; return $out; } $options = $inputfield->getOptions(); $optionIdsString = implode('|', array_keys($options)); $selectable = $event->wire()->pages->find("id=$optionIdsString, sort=parent.sort, sort=sort"); $hierarchy = buildHierarchy($selectable); $out = renderCheckboxesList($hierarchy, $inputfield); $out .= <<<EOT <style> .nested-checkboxes-list:not(.InputfieldCheckboxes > .nested-checkboxes-list) { padding-left:25px; } .nested-checkboxes-item input { margin-right:0.5em; } </style> EOT; $event->replace = true; $event->return = $out; }); Before: After: There's no JavaScript (I'll leave that to you), but in any case you would likely need to use PHP logic to set the selection state of parent items on Pages::saveReady() or else the field value would get out of whack any time an option was set via the API rather than via the inputfield. That's why having the parents/grandparents in the field value is something that can't really be handled by a module that is an inputfield only and is probably best done in custom code that's specific to your project.
  21. 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;']);
  22. @hellomoto Not sure, sorry. Since you're outputting the token with ID `contact` and also checking the same one in your code, in theory it should work. Maybe the form validates its own token somewhere else? But yes, it might be a good idea to just have a placeholder for the entire form and render the form in the callback. That way, the form can manage its own CSRF token (and also other state like error messages, etc).
  23. I'm trying to use this with a form using InputfieldForm. I have a hook that replaces its CSRF after InputfieldForm::render... but since it generates a CSRF input automatically I assume I can't just overwrite it post-render, because it's expecting what it put out? Maybe I should just add a placeholder for the form itself? (ajax) // ready.php $this->addHookAfter('CachePlaceholders::getTokens', null, 'cachePlaceholders'); function cachePlaceholders(HookEvent $e) { $tokens = $e->return; // defaults $e->return = $tokens; }; // don't cache CSRF in forms on frontend: $this->addHookAfter('InputfieldForm::render', null, 'frontendFormsCSRF'); function frontendFormsCSRF(HookEvent $e) { if ($e->wire()->page->rootParent->id == 2) return; $pattern = '/<input ([^<>]*) class=\'_post_token\' \/>/'; $replace = '{{{csrf::contact}}}'; // bd($e->return); $replaced = preg_replace($pattern, $replace, $e->return); $CP = $e->wire()->modules->get('CachePlaceholders'); $return = $CP->replaceTokens($replaced); // bd($return); $e->replace = true; $e->return = $return; } Then to process the form I put // inquire.php if ($input->post->message) { $sessionCSRF->hasValidToken('contact'); $form->processInput($input->post); // ... } but it isn't submitting because of CSRF issue.
  1. Load more activity
×
×
  • Create New...