-
Posts
232 -
Joined
-
Last visited
-
Days Won
9
Everything posted by d'Hinnisdaël
-
module ProcessWire Dashboard
d'Hinnisdaël replied to d'Hinnisdaël's topic in Module/Plugin Development
I‘m not familiar with RockGrid but if I understand you correctly it can export pages? In that case, using a Collection panel should get you there. However I suspect what you‘re looking for is showing the RockGrid UI inside a panel — in that case you could look into rendering a template file inside a panel and implementing the custom logic there. -
@netcarver Yes, I've created a gist that should do.
-
@netcarver I've been using Latte in Laravel almost exclusively. Blade is great, but Latte is even better. There's a composer package for integrating Latte, but it was out of date when I last checked so I wrote my own which was quite straightforward. Interestingly, you can mix and match, calling Blade views from Latte and vice versa, which is a lifesaver when dealing with vendor files and third-party packages.
-
module ProcessWire Dashboard
d'Hinnisdaël replied to d'Hinnisdaël's topic in Module/Plugin Development
The PageList panel uses ProcessWire's internal ProcessPageList component. As far as I know, it works by displaying the actual children of a parent page and doesn't support rendering arbitrary pages that aren't siblings. -
Did you run into any breaking changes upgrading from Latte 2 to 3? I haven't yet updated the Latte renderer for TemplateEngineFactory as I haven't had enough time to give Latte 3 a go.
-
And a few macros as well: <?php use Latte\Compiler; use Latte\MacroNode; use Latte\Macros\MacroSet; use Latte\PhpWriter; /** * Latte macro provider * */ final class Macros extends MacroSet { /** * Install available macros. */ public static function install(Compiler $compiler): void { $me = new static($compiler); $me->addMacro('ifispage', [$me, 'macroIsPage'], '}'); $me->addMacro('icon', [$me, 'macroSvgIcon']); $me->addMacro('trim', '', [$me, 'macroTrimEnd'], null); $me->addMacro('minify', [$me, 'macroMinifyHtml'], [$me, 'macroMinifyHtmlEnd'], null, self::ALLOWED_IN_HEAD); } /** * {ifispage $page} */ public function macroIsPage(MacroNode $node, PhpWriter $writer) { return $writer->write(' $isObj = %node.word && is_object(%node.word); $isCustomPage = $isObj && is_a(%node.word, "ProcessWire\DefaultPage"); $isCorePage = $isObj && get_class(%node.word) === "ProcessWire\Page"; $isPage = ($isCustomPage || $isCorePage) && %node.word->id; if ($isPage) { '); } /** * {icon 'search'} */ public function macroSvgIcon(MacroNode $node, PhpWriter $writer) { return $writer->write(' $icon = %node.word; $ratio = "xMinYMid"; $ratioAttr = $ratio ? "preserveAspectRatio=\"{$ratio}\"" : ""; $html = "<svg class=\'icon icon-{$icon}\' {$ratioAttr} aria-hidden=\'true\'><use xlink:href=\'#icon-{$icon}\' /></svg>"; echo $html; '); } /** * n:trim */ public function macroTrimEnd(MacroNode $node, PhpWriter $writer) { $node->validate(false); $node->openingCode = "<?php ob_start(); ?>"; $node->closingCode = '<?php $__output = ob_get_clean(); ?>'; if ($node->prefix === MacroNode::PREFIX_INNER) { // Simple case: n:inner-trim -> just trim content $node->closingCode .= '<?php $__output = trim($__output); ?>'; } else { // Trickier case: n:trim // remove whitespace *inside* outer tags // while preserving whitespace *outside* of outer tags // Before: " <h1> Title </h1> " // After: " <h1>Title</h1> " $node->closingCode = $node->closingCode . // Remove whitespace after opening tag '<?php $__output = preg_replace(\'~^(\s*<[^>]+>)\s+~s\', "\$1", $__output); ?>' . // Remove whitespace before closing tag '<?php $__output = preg_replace(\'~\s+(</[^>]+>\s*)$~s\', "\$1", $__output); ?>'; } $node->closingCode .= '<?php echo $__output; ?>'; } /** * {minify} */ public function macroMinifyHtml(MacroNode $node, PhpWriter $writer) { return $writer->write(' ob_start(function ($s, $phase) { // 0: replace newlines //$html = preg_replace("/\r\n|\r|\n/", "", $s); // 1: remove whitespace from between tags that are not on the same line. $html=preg_replace(\'~>\s*\n\s*<~\', \'><\', $s); // 2: replace all repeated whitespace with a single space. static $strip = true; $html=LR\Filters::spacelessHtml($html, $phase, $strip); return $html; }, 4096); '); } /** * {/minify} */ public function macroMinifyHtmlEnd(MacroNode $node, PhpWriter $writer) { return $writer->write(' ob_end_flush(); '); } }
-
Sure! Here's a few that might be of use to ProcessWire sites: <?php use Latte\Engine; /** * Latte filter provider * */ final class Filters { /** * Install available filters. */ public static function install(Engine $latte) { foreach ((new static())->provide() as $name => $callback) { $latte->addFilter($name, $callback); } } /** * @return array<string, callable> */ public function provide(): array { return [ // Sanitize values using ProcessWire's sanitizer API variable 'sanitize' => function ($value, $sanitizer, $options = null) { if (!$options) { return sanitizer()->$sanitizer($value); } else { $args = func_get_args(); unset($args[1]); // remove $sanitizer arg return call_user_func_array([sanitizer(), $sanitizer], array_values($args)); } }, // Render FormBuilder form, but allow prepending and appending fields 'form' => function ($form, $options = []) { if (!$form) { return ''; } $output = $form->render(); if ($options['append'] ?? false) { $output = str_ireplace('</form>', $options['append'], $output); } if ($options['prepend'] ?? false) { if (stripos($output, '<form') !== false) { $output = preg_replace('#(<form[^>]*>)#i', '\\1' . $options['prepend'], $output); } else { $output = $output . $options['prepend']; } } return $output; }, // URL slug / page name 'slug' => function ($str, $length = 128) { $str = sanitizer()->unentities($str); $str = sanitizer()->pageNameTranslate($str, $length); return $str; }, // Truncate string 'truncate' => function ($str, $length = 200) { return sanitizer()->truncate($str, $length, ['visible' => true]); }, // Render Markdown 'markdown' => function ($str) { $str = "{$str}"; modules()->get("TextformatterMarkdownExtra")->formatValue(new Page(), new Field(), $str); return $str; }, // Unwrap ProcessWire value objects 'value' => function ($object) { if (is_object($object)) { if ($object instanceof SelectableOptionArray) { return $object->implode(' ', 'value'); } else { return $object->value ?? ''; } } elseif (is_array($object)) { return implode(' ', $object); } else { return (string) $object; } }, // Join a string with a custom separator at the end 'join' => function ($list, $separator = ', ', $lastSeparator = ' & ') { if (count($list) > 1) { $last = array_pop($list); return implode($separator, $list) . $lastSeparator . $last; } else { return implode($separator, $list); } }, // Prettify URL by removing protocol and www 'prettyUrl' => function ($url, $options) { if (null === $url) { return false; } $url = trim($url); if ($options['www'] ?? true) { $url = str_replace('www.', '', $url); } if ($options['http'] ?? true) { $url = str_replace(array('https://', 'http://'), '', $url); } if ($options['slash'] ?? true) { $url = rtrim($url, '/'); } return $url; }, ]; } }
-
Latte is great indeed! I've been using it for all projects since discovering it. Everything is so much more concise and elegant, especially once your start building your own filters and macros. Quick note that you can also use Latte with Wanze's TemplateEngineFactory. I've found that the easiest way to get started; one composer install and you're ready to go ?
-
module ProcessWire Dashboard
d'Hinnisdaël replied to d'Hinnisdaël's topic in Module/Plugin Development
I'd love to hear how you accomplished the filtering part — I'm assuming you've built a panel emitting custom events that trigger panel reloads. Or is it plain old get params? -
module ProcessWire Dashboard
d'Hinnisdaël replied to d'Hinnisdaël's topic in Module/Plugin Development
If you create a panel group and add the tabs to that, it should work. -
module ProcessWire Dashboard
d'Hinnisdaël replied to d'Hinnisdaël's topic in Module/Plugin Development
Yeah, the migration guide looks pretty daunting. In that case I prefer leaving it as-is for now. If you find a simple fix, let me know and I'll happily review/merge any input. -
module ProcessWire Dashboard
d'Hinnisdaël replied to d'Hinnisdaël's topic in Module/Plugin Development
Realistically speaking, I won't get to refactoring the chart panel until later this year. Updating the chart.js library in the meantime sounds good, though. Do you happen to know if the update contains any breaking changes that would apply to its use in the context of this dashboard? I haven't been actively following along and I'd like to avoid breaking people's existing dashboards if possible. -
module ImageOptim — automatic image optimization
d'Hinnisdaël replied to d'Hinnisdaël's topic in Modules/Plugins
Yes, I have it working in production on a few up-to-date sites. If you find it doesn't work for some reason, feel free to raise the issue here or on GitHub. -
module ProcessWire Dashboard
d'Hinnisdaël replied to d'Hinnisdaël's topic in Module/Plugin Development
Sounds good, happy to integrate that if you submit a pull request on the GitHub repo. -
module ProcessWire Dashboard
d'Hinnisdaël replied to d'Hinnisdaël's topic in Module/Plugin Development
Good catch. Now that I think about it, returning all repeaters makes sense in the admin context — otherwise you wouldn't be able to edit all repeater items on page edit screens. But as you said, you'll have to remember to properly mark/mask unpublished repeaters yourself. -
module ProcessWire Dashboard
d'Hinnisdaël replied to d'Hinnisdaël's topic in Module/Plugin Development
@wbmnfktr I just created a quick test case and you're right, ProcessWire seems to have access checks disabled for repeaters in this context. Output formatting seems to be turned on, however. Is this usually the case in the admin? -
Another good option for partial HTML updates is morphdom which transforms the existing dom nodes to match the new incoming HTML without discarding any elements. That way, any dom events, scroll positions or css transition states will be kept on the existing elements. No specific markup required, nor changes to how you set up dom events. It's used in Phoenix LiveView which is pretty close to our use case (live updates from server-rendered templates).
-
Untested code, but along those lines: wire()->addHookAfter('ProcessPageEdit::buildForm', null, function (HookEvent $event) { $form = $event->return; $statusField = $form->find("name=status")->first(); if ($statusField) { $statusField->collapsed = Inputfield::collapsedHidden; } });
-
module ProcessWire Dashboard
d'Hinnisdaël replied to d'Hinnisdaël's topic in Module/Plugin Development
I think the correct way of doing it is a bit different with newer ProcessWire versions. Try this one: require $config->paths->core . 'admin.php'; -
module ProcessWire Dashboard
d'Hinnisdaël replied to d'Hinnisdaël's topic in Module/Plugin Development
@adrian I went ahead and added very basic tab support. Thanks for the nudge. If you want to give it a try, feel free to check out the tabs branch of the repo. Theoretically it supports nesting tabs inside groups and vice versa, however it's largely untested so let me know if you run into trouble. I'd like to do some more testing before releasing a new version. wire()->addHookAfter('Dashboard::getPanels', function ($event) { $panels = $event->return; $tab1 = $panels->createTab(['title' => 'Lorem ipsum']); $tab1->add(/* add panels here */); $tab2 = $panels->createTab(['title' => 'Dolor sit amet']); $tab2->add(/* add panels here */); $panels->add($tab1); $panels->add($tab2); }); -
module ProcessWire Dashboard
d'Hinnisdaël replied to d'Hinnisdaël's topic in Module/Plugin Development
Good point about making charts styleable via CSS. My experience has been that most clients and most dashboards don't need charts at all, so frankly I don't think I'll be investing too much time here, but if or when I decide to revamp the chart panel I'll keep that in mind. Adding tabs is definitely on my list. Groups are already supported, so adding tabs should be somewhat trivial. There's possible performance issues to keep in mind when rendering too many panels at once but that shouldn't be a blocker. -
@markus_blue_tomato I've noticed that blurhashes aren't regenerated when images are replaced via drag-and-drop in the admin. Have you run into this or is this a known limitation of the module? Best way to reproduce is: clone a page with a populated single-image field, drag a new image into the image field of the cloned page and save. The blurhash still looks like the old image from the page it was cloned from.
- 37 replies
-
- image
- lazy loading
-
(and 2 more)
Tagged with:
-
PageList swallows page labels within square brackets [solved]
d'Hinnisdaël replied to d'Hinnisdaël's topic in General Support
Thanks @Robin S, I managed to narrow it down to a specific module. Will file an issue on the repo. -
The admin page list seems to be hiding the content of page labels inside square brackets. For a page titled Some Title [old], it will display as Some Title, removing the content inside the brackets. Oddly enough, a page titled Some Title [old content] displays correctly as Some Title [old content]. My guess is that ProcessWire is trying to interpret the part in square brackets as dot-field notation and trying to access a field value. Since there's no field named 'old', it will drop the brackets and everything inside entirely. Is this documented behavior? Can this be turned off? (Tested on latest dev branch, 3.0.171 and 3.0.181)
-
Accepting arbitrary pages sounds like a nightmare to support. There's really no upside to it that I can see. Linking existing pages via page reference fields sounds much more doable and logical. You'll avoid all those problems you already recognized.