Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 04/25/2019 in all areas

  1. I have sent a pull request on github for webP support in the core: https://github.com/processwire/processwire/pull/141 Explanation of its usage: I implemented two new options for the imagesizer that can be set globally in the site/config.php or can be passed within an individual options array to any size-method: $options = [ "webpAdd" => true, // boolean, if true, an additional webp variation will be created "webpQuality" => 80 // integer range 0 - 100, 100 is best ]; This creates an additional webp variation file besides the regular JPEG, PNG or GIF variations. In the template files you have two new image properties to work with the webp variation: $image->hasWebp; // boolean, true indicates that a webp variation is available for that image variation ?> <img src="<?= $image->srcWebp ?>" alt="" /> So we can create conditional markup like in this example: $image = $page->images->first()->size(300, 300); ?> <picture> <?php if($image->hasWebp) { ?> <source srcset="<?=$image->srcWebp?>" type="image/webp" /> <?php } ?> <source srcset="<?=$image->src?>" type="image/jpeg" /> <img src="<?=$image->url?>" alt="Alt Text!" /> </picture> The pull request and my test branch are based on this weeks pw dev branch. So if someone want to test it, please grab a copy, play with it and report back here. I don't know what timeline @ryan has and what parts need to be modified / rearanged, etc. Maybe the property names or the option names will be changed, but I think the main functionality will be close to that in the current test branch. ----- And here are a filesize comparision with different quality settings and with both engines, GD-Lib and IMagick: EDIT: An approach without conditional markup for webp comes from @arjen see lazyload. ...
    14 points
  2. @Tom. Many thanks for your feedback! And, uhm, - GD does not use the compressed jpeg for webp creation, but currently IMagick does. But this is not intended and I locally have a version that uses the uncompressed src variation for webp creation. I will finalize this and commit an update to the test branch later this evening. So the intended usage is that you can define independend quality settings for jpeg and webp, and both final variations will be created from the uncompressed variation bitimage. ["quality" => 80; "webpQuality" => 90] If you simply want to test out GD-Lib when IMagick is installed with higher priority, you can pass the param "forceEngine" with the options array to the size method: $options = [ 'forceEngine' => 'ImageSizerEngineGD', // ImageSizerEngineGD | ImageSizerEngineIMagick 'forceNew' => true, 'webpAdd' => true, 'webpQuality' => 90, 'quality' => 80, 'sharpening' => 'none' ];
    3 points
  3. Thanks for posting that little trick. I took some inspiration there and made a small module that does the same without AOS. Should work with Default, Reno and Uikit themes. You can enable dev mode in the module's settings or by setting $config->devMode to true in site/config.php. Text and colors can be adapted in module settings.
    3 points
  4. Yes, once the webp variation is created and cached, it only will be served, but the PHP engine is invoked for every image call. This makes a difference compared to simply serve a static file directly by apache on sites with lots of images, like archives. Thats why I said it is a nice solution for sites with smaller amounts of images. For example: I maintain sites with 60k+ images, that presents 100 thumbs per page and have infinite scrolling, that may result fastly in 100 to 1000 thumbs on a single page view. There it makes a difference, if apache serves 1000 thumbs from static files, or if the webserver has to invoke 1000 times the PHP engine for serving static files. And multiply this with a decend amount of simultaneous visitors of the site, I bet you too definetly want to avoid invoking the PHP engine for serving static image files.
    3 points
  5. @Tom. I have downloaded all four images and what you are saying, at least how I do understand it, seems not to be completly valid. What exactly was the source for what variation, as you said without resizing? (The original is larger than the others) I found these images: The original image is 4x larger (in pixel) than the compared variations. And it only has 157k, whereas the other have 300k. The original image is from a source of 1920x1080 resized in Photoshop CC2017 to 1500x1074 px and is very strong compressed. That is NOT a useful choice for a master image! Masterimages should be saved in 100% quality, (or quality 12 in Photoshop), in every single item of a workflow chain. Once you saved with lesser quality, you damaged the image, regardless if you later on save it with higher quality. When I save your original image in Photoshop with quality 12, I get a 427k image file, not 157k. Another ProTip is: work with images in 16bit colordepth all the time, best in a larger color spectrum like AdobeRGB, not sRGB, use PSD or TIFF as your local master image, and only transform from this local master image to every desired target format according these steps, respecting the chronology: - 1) resize to target dimensions - 2) transform into target color space, (AdobeRGB into sRGB, or AdobeRGB into CMYK, etc) - 3) reduce the color depth from 16 bit to 8 bit, (the last step always!) - 4) save into your target file format, for ProcessWire (online) master images, with the max quality in JPEG. From these (online) master images you can create very good results with the GD-Lib and with the IMagick-Lib. If you do not resize the original or only resize a very small value, you may set the sharpening property to "none". Also when using the GD-Lib, you may set the "defaultGamma" to -1. Experimenting with this two properties should be enough to render good results for all possible use cases. At least this are my experiences. Can you tell me why the original is compressed so strong? And do you have an uncompressed version too? (uncompressed means: never ever compressed in its total live workflow chain!) ------------ There definitely is a difference when using the same sharpening values with GD-Lib and IMagick. This is due to the fact that IMagick internally already uses a sharpening algorithm when resizing images, and GD-Lib completely lacks about this feature. Looking at PWs image resizing history, there first only was GD-Lib without sharpening, resulting in blurry images. Than we introduced a sharpening algorithm with four available values: none | soft | medium | strong, (defaults to soft) A long time later, we introduced the ImageMagick-Lib with all the same params available as for the GD-Lib. At this time my thinking was, when switching from GD to IMagick, one may switch from medium to soft, or from soft to none in the site/config.php file, as it is a global decision what image library to use. But a review / rework of the used algorithm values in the core libs is possible, so that we maybe will have nearly no differences between IMagick none and soft, but keep the currently used differences within the GD-Lib.
    3 points
  6. @horst, it's perfect for me. I already had WebP since your code from late 2018 and use my own ImageSizer, which relies on LibVips. The Vips engine is very fast - about 5 times faster than ImageMagick. I hope WebP will make it into the core soon - many thanks for your hard work!
    2 points
  7. @horst Thank you so much, I'm playing with this now - I'll let you know if I come across anything. Appreciate the hard work. Edit: Looking good! Edit: One thing I've noticed is let's say you set the quality of the jpeg to 50 on resize, WebP will use the resized jpeg at 50 quality. Does this mean that using also webpQuality 50 would compress it twice? @horst
    2 points
  8. Just a side note: imo such things just increase the percieved page load time and are an unnecessary complication unless the page is expected to load slowly. I would avoid it if possible, even that I have done it a few times in the past.
    2 points
  9. I have not used Padloper yet, but I built a custom shop lately and it's a LOT of work ? The problem is not the payment integration, the problem is handling all the user interactions (shopping cart, taxes, product variations etc). So it depends a lot on your scenario. If you only want to collect donations without any other features, then you might not need padloper (but then you could also just use a payment button from one of the providers)
    2 points
  10. What about this? $wire->addHookAfter("Pages::added", function($event) { $page = $event->arguments(0); // check for page template if necessary here $page->setAndSave('check', 1); }); PS: Did you see @Robin S module?
    2 points
  11. $log->save('modulename', 'message to log'); will take care of it for you, putting it in /site/assets/logs/modulename.txt
    2 points
  12. I've done both so far. Since we use PW for our corporate intranet, there are a number of additional databases we pull data from, which is quite straight forward (just an include that instantiates a new WireDatabasePDO and wire()s it). We have a search interface to our test report database where all the data for on-site test runs of our machines are stored, pull in infos about the processing state of lab samples and display an ajax loading tree of standard parts lists for the project engineers from the ERP. Other DBs have been converted to PW pages, like e.g. the archive for the old support ticket system. We even have a mix at some places. For our sales department, we pull the contact information for our worldwide offices from the ERP, create pages if they don't exist yet, and the department augments these pages with additional infos like contractual details that the ERP can't store without climbing through major hoops. We have a semi-live attendance list (the most visitied page by far) filled with data combined from PW's user list (synchronized from Active Directory), attendance status from the time accounting software and person info like the building/room number from the ERP, with the HTML output being generated every five minutes by a background job to ease server load a bit. We have some additional, non-PW tables in the PW database for speeding up things, like caching the listings of large directories with parts drawings, and even wrote our own CKEditor plugins to easily embed links or image tags for these files. You could say PW runs as a big "spider web" that connects all the apps in our company, with data being pulled in wherever necessary. There are a few apps where we thought about storing the content in PW but decided against it due to the sheer amount of data and the performance impact PW's normalization would bring. Right now we're at around 25k pages and a little over 100 fields in 50 templates. We use the UserGroups module and template settings to limit backend access to individual templates and keep the list of available templates short for our editors (~60 editors who never see more than 6 templates in their selection list). Our intranet was built on a different CMS in 2006 and migrated to PW a few years ago, and the possibilities we gained were simply astonishing. It's always fun to have a sales rep for one of the intranet platform solutions on site and, after listing to their spiel about the great features they offer, show them what we have and ask innocently, "How much work would it be to implement all of this?" ?
    2 points
  13. @lokomotivan @Sergio I have now experimented with many options to solve this problem. However, there is only one way, which covers both behaviors. If you don't want segments to be counted, just enable the option as the behavior is current. But if segments are to be counted as well, the segments MUST be defined in the template configuration. For dynamic segments with RegEx. For this I have inserted a passage in the readme with the link to the PW help. There is currently no other option. The problem is that the Page Hit Counter hooked into the PageNotFound process (But I also tried PageRender, etc.). If URL segments are allowed but not defined, a 404 is never triggered from Processwire. This means that the Page Hit Counter cannot be called. For the tracking of segments, so simply define the segments. ? I also released an update with version 1.2.5, which improves the behavior when tracking 404 errors. All infos and download as always in the first post.
    2 points
  14. Hey just bringing up as a point for discussion. I find in various areas of the admin there is quite a lot of reflowing of content and flash of unstyled content (FOUC). Examples are: the pages listing animation pushes the footer down the page every time, and this can also be seen on various pages where content doesn't load immediately (footer will fly down the page) basic pages with no fields – sit there and refresh a basic page a few times, you will see a flash of all content before tabs are created pages with lots of fields – progressive loading and rendering of different fields and groups causes lots of reflow, particularly loading wysiwyg and image fields modules eg lister-pro – loads the add new button initially on the left then it jumps to the top right as the lister loads, then columns reflow and resize as content loads in and classes are applied Whilst pw is very fast to render admin page html, to me the above makes it feel quite jarring and heavy. Generally speaking you want to aim for no reflow or FOUC whatsoever for a good user experience. I know there is a lot going on here and it is not a simple thing to manage an admin like this, but does anyone notice the same thing and have any ideas about how this can be optimised? Below are some relevant links. Minimising animations is the best place to start, although before anyone suggests it just using AdminOnSteroids to disable animations doesn't fix a lot of what I am talking about. https://developers.google.com/speed/docs/insights/browser-reflow https://gist.github.com/paulirish/5d52fb081b3570c81e3a
    1 point
  15. Thought I'd share this quickly as it helps me get to grips with whether I'm looking at the dev site or live site when working on any client project. Dev site only has AdminOnSteroids module. I add the below to sites/templates/admin/admin.css and add that url the admin css field in the module. Currently only works with default adminTheme. #masthead{ background: rgb(153, 12, 94) !important; } #logo:before{ content: 'DEV '; color: white; margin-right: 1em; vertical-align: middle; display: inline-block; font-size: 1em; letter-spacing: 0.05em; margin: -15px 0.6em 0em 0; } Simply adds a DEV string and changes the header color so I can see I'm on Dev not live. I guess you could do the opposite if you wanted adding content: "LIVE" instead in the CSS.
    1 point
  16. The TracyDebugger module also has a "Module Disabler" panel that can easily disable modules.
    1 point
  17. On the current page, in incognito mode (guest, i.e. not logged in), I see: page id 10207 is NOT trashable On the same page, as superuser, I see page id 10207 is trashable (simple if/else in my template) So, I'm not sure why your setup behaves different. I run the exact same PW version.
    1 point
  18. Are you sure you've assigned the necessary user/role rights? d($page->trashable()); If I visit a frontend-page as superuser, this always returns true. (Tracy Debugger)
    1 point
  19. 1 point
  20. Thanks @horst. Hopefully @ryan will include this quickly into the core.
    1 point
  21. Hm... Thinking a little more about it, I think having a setting "set checkbox to true by default for newly created pages" would be the best option. Changing the default setting during lifetime of your website is unlikely and if so, this setting would just impact newly created pages and there would not be the need for updating existing pages (as they already have the correct value, which was set manually at that time). It would be a very easy hook, as shown above: https://processwire.com/talk/topic/2199-checkbox-default-value/?tab=comments#comment-184564 Sorry for hijacking your thread robin and thanks for the discussion ?
    1 point
  22. I guess this should do it: $selector .= "title|headline|lead|body|matrix.repeater_field.body%=$q";
    1 point
  23. https://github.com/processwire/processwire/blob/649d2569abc10bac43e98ca98db474dd3d6603ca/wire/core/Modules.php#L55-L85 The flags are binary flags with their values being defined in the core files. To uninstall remove the flag for 1 or 2. There's not really something to be documented, as the database is not meant to be directly interfaced anyways. Documentation means people rely on the information and therefore changes can no longer be easily made.
    1 point
  24. A drawback to that approach is that users will not necessarily only add the field to templates that don't yet have any pages - they could add the field to templates that have many existing pages and then confusion results when they see that their pages don't have the checkbox checked by default. Ryan discussed this here: And a module can't get around this problem by itself because of what I mentioned in the readme: So some separate API script needs to be run as needed to populate the checked state on pages immediately after the field is added to a template. But certainly you could create a module around this approach and perhaps include an example script that users could refer to as a starting point if they feel confident using the API.
    1 point
  25. Couple of extensions I've been using for a few weeks that haven't been mentioned here yet. (as far as I can tell) Bracket Pair Colorizer : Nested Brackets can be a nightmare to debug and is a pet hate of mine. This extension will save you, unless you're colourblind in the default colours . But I'm sure you can tweak the colours if it comes to that. ext install CoenraadS.bracket-pair-colorizer Path Intellisense : If I have to explain what this does, we're definitely not on the same path. ext install christian-kohler.path-intellisense Material Icon Theme: I know Material Design was mentioned before. This is an alternative one. Not sure what the difference is between the 2 but I'm using the Winter is Coming theme, and there seems to be an overlap between this and the Material Design theme. ext install PKief.material-icon-theme Winter is Coming theme: Any GoT fans out there? ext install johnpapa.winteriscoming
    1 point
  26. Thank you very much for the explanation, this is really useful information. I provided the images provided to me by the designers and forward their complains on image quality. However, this has always been a difficult argument for me as I only look at it technically. I can now pass this information on to them and make sure they are outputting images correctly. Thanks again, I've worked on a small webP converter in the meantime, until we see something implemented into the core.
    1 point
  27. Since I did not see it mentioned, I think this extension might be useful for coders (at least it is for me) where I can easily prettify my code by saving it or using the shortcuts. As per some videos, it works fine with php, java/script etc. and is called Prettier - Code formatter As far as VSCode would by default analyze the file opened and use those settings for tab spaces etc. if you want to change the settings of an existing file, you would need to disable the default behavior and add your preferred options here. Now, if I decide to change the tab indentation from default 4 spaces to anything else, besides aligning the code nicely, prettier would re-adjust the tab spacing as well ?
    1 point
  28. What I've done so far, is a small implementation of webp-convert. Currently without a ProcessWire specific module or something, but that's planned. Just added a file called webp-on-demand.php in the ProcessWire root directory with following content: <?php // docs https://github.com/rosell-dk/webp-convert require 'webp/webp-on-demand-1.inc'; use WebPConvert\WebPConvert; $docRoot = rtrim($_SERVER["DOCUMENT_ROOT"], '/'); $requestUriNoQS = explode('?', $_SERVER['REQUEST_URI'])[0]; $source = $docRoot . urldecode($requestUriNoQS); $destination = $source . '.webp'; // Store the converted images besides the original images (other options are available!) $options = [ // Tell where to find the webp-convert-and-serve library, which will // be dynamically loaded, if need be. //'reconvert' => true, 'require-for-conversion' => 'webp/webp-on-demand-2.inc', 'quality' => 'auto', 'max-quality' => 75, 'fail' => 'serve-original' // see https://github.com/rosell-dk/webp-on-demand/blob/master/docs/api.md for more info ]; WebPConvert::convertAndServe($source, $destination, $options); Then in the folder webp the two on-demand files from here. After that I added following to the .htaccess: <IfModule mod_rewrite.c> RewriteEngine On # Redirect images to webp-on-demand.php (if browser supports webp) RewriteCond %{HTTP_ACCEPT} image/webp RewriteCond %{REQUEST_FILENAME} -f RewriteRule ^(.*)\.(jpe?g|png)$ webp-on-demand.php [NC,L] </IfModule> AddType image/webp .webp And that's it. If the browser supports webp, all requests of .jpg and .png are redirectet to the webp-on-demand.php which desides if it needs to convert the image or serve the already converted one. A small caveat: the standard installation of Plesk has no webp support included, so on webservers with Plesk you either need to wait for their implementation or compile it yourself.
    1 point
  29. I don't want to downplay anything here but... how often do you all see FOUC in PW admin? I just started to look closer while admin pages load/reload... but to be honest... I see only some minor reflow issues here and there but nothing that annoys me. Right now I'm working on three different systems... local (Laragon) Remote (webgo) Remote (Dreamhost) While Dreamhost is remote and kind of slow only reflow issues appear on bigger pages there once in a while. No FOUC or something similar to that. Laragon and webgo are fast and those reflow issues are gone even before I really notice them. I don't use AOS, Tracy, Lister Pro or similar modules that may cause those... FOUC issues. Nonetheless... count me in for optimizing the backend to make it even better. Tested on: PW version 3.0.123 and 3.0.130
    1 point
  30. @Mikie - I completely agree - the PW admin interface is full of these jarring FOUCs (it's not just the UiKit theme either). I hope Ryan will give this some serious consideration going forward.
    1 point
  31. General thoughts on this... I never installed Matomo into a subfolder on a ProcessWire website. Therefore I can't tell if this works out of the box or if ProcessWire tries to protect that folder in some way. I'd suggest installing matomo somewhere else - a subdomain for example. https://matomo.domain.tld/ - would probably be the easiest way here. After that tracking should work - unless you misconfigured Matomo or block tracking with DNT or the Ghostery extension. But... there is one thing I don't understand. You are saying "place the map in the template folders" - what map?
    1 point
  32. Checking for empty value is usually enough $emptyPages = $pages->find("myField=''") // note the empty string with single quotes $filledPages = $pages->find("myField!=''")
    1 point
  33. After this tutorial you'll have learned how to: Build a Process module Make an AJAX request to backend Serve JSON as response Let's say you want to display the latest orders in a dashboard that you can access from admin panel. And you want it to refresh its content with a button click. Most straightforward and proper way (that I know of) is to create a Process module, as they're built for this purpose. First, create a directory under /site/modules/, call it ProcessDashboard, and create a file named ProcessDashboard.module under that directory. Following is about the least amount of code you need to create a Process module. <?php namespace ProcessWire; class ProcessDashboard extends Process { public static function getModuleInfo() { return [ 'title' => 'Orders Dashboard', 'summary' => 'Shows latest orders', 'version' => '0.0.1', 'author' => 'abdus', 'autoload' => true, // to automatically create process page 'page' => [ 'name' => 'order-dashboard', 'title' => 'Orders', 'template' => 'admin' ] ]; } public function ___execute() { return 'hello'; } } Once you refresh module cache from Modules > Refresh, you'll see your module. Install it. It will create an admin page under admin (/processwire/) and will show up as a new item in top menu, and when you click on it, it will show the markup we've built in execute() function. All right, now let's make it do something useful. Let's add create a data list to display latest orders. We'll change execute() function to render a data table. public function ___execute() { /* @var $table MarkupAdminDataTable */ $table = $this->modules->MarkupAdminDataTable; $table->setID($this->className . 'Table'); // "#ProcessDashboardTable" $table->headerRow([ 'Product', 'Date', 'Total' ]); // fill the table foreach ($this->getLatest(10) as $order) { $table->row([ $order['title'], $order['date'], $order['total'] ]); } // to refresh items $refreshButton = $this->modules->InputfieldSubmit; $refreshButton->name = 'refresh'; $refreshButton->id = $this->className . 'Refresh'; // "#ProcessDashboardRefresh" $refreshButton->value = 'Refresh'; // label of the button return $table->render() . $refreshButton->render(); } where getLatest() function finds and returns the latest orders (with only title, date and total fields) protected function getLatest($limit = 5, $start = 0) { // find last $limit orders, starting from $start $orders = $this->pages->find("template=order, sort=-created, limit=$limit, start=$start"); // Only return what's necessary return $orders->explode(function ($order) { return [ 'title' => $order->title, 'date' => date('Y-m-d h:i:s', $order->created), 'total' => $order->total ]; }); } When you refresh the page, you should see a table like this Now we'll make that Refresh button work. When the button is clicked, it will make an AJAX request to ./latest endpoint, which will return a JSON of latest orders. We need some JS to make AJAX request and render new values. Create a JS file ./assets/dashboard.js inside the module directory. window.addEventListener('DOMContentLoaded', function () { let refresh = document.querySelector('#ProcessDashboardRefresh'); let table = document.querySelector('#ProcessDashboardTable'); refresh.addEventListener('click', function (e) { // https://developer.mozilla.org/en/docs/Web/API/Event/preventDefault e.preventDefault(); // Send a GET request to ./latest // http://api.jquery.com/jquery.getjson/ $.getJSON('./latest', { limit: 10 }, function (data) { // check if data is how we want it // if (data.length) {} etc // it's good to go, update the table updateTable(data); }); }); function renderRow(row) { return `<tr> <td>${row.title}</td> <td>${row.date}</td> <td>${row.total}</td> </tr>`; } function updateTable(rows) { table.tBodies[0].innerHTML = rows.map(renderRow).join(''); } }); And we'll add this to list of JS that runs on backend inside init() function public function init() { $scriptUrl = $this->urls->$this . 'assets/dashboard.js'; $this->config->scripts->add($scriptUrl); } Requests to ./latest will be handled by ___executeLatest() function inside the module, just creating the function is enough, PW will do the routing. Here you should notice how we're getting query parameters that are sent with the request. // handles ./latest endpoint public function ___executeLatest() { // get limit from request, if not provided, default to 10 $limit = $this->sanitizer->int($this->input->get->limit) ?? 10; return json_encode($this->getRandom($limit)); } Here getRandom() returns random orders to make it look like there's new orders coming in. protected function getRandom($limit = 5) { $orders = $this->pages->find("template=order, sort=random, limit=$limit"); return $orders->explode(function ($order) { return [ 'title' => $order->title, 'date' => date('Y-m-d h:i:s', $order->created), 'total' => $order->total ]; }); } And we're done. When refresh button is clicked, the table is refreshed with new data. Here it is in action: 2017-04-29_19-01-40.mp4 (227KB MP4, 0m4sec) Here's the source code: https://gist.github.com/abdusco/2bb649cd2fc181734a132b0e660f64a2 [Enhancement] Converting page titles to edit links If we checkout the source of MarkupAdminDataTable module, we can see we actually have several options on how columns are built. /** * Add a row to the table * * @param array $a Array of columns that will each be a `<td>`, where each element may be one of the following: * - `string`: converts to `<td>string</td>` * - `array('label' => 'url')`: converts to `<td><a href='url'>label</a></td>` * - `array('label', 'class')`: converts to `<td class='class'>label</td>` * @param array $options Optionally specify any one of the following: * - separator (bool): specify true to show a stronger visual separator above the column * - class (string): specify one or more class names to apply to the `<tr>` * - attrs (array): array of attr => value for attributes to add to the `<tr>` * @return $this * */ public function row(array $a, array $options = array()) {} This means, we can convert a column to link or add CSS classes to it. // (ProcessDashboard.module, inside ___execute() method) // fill the table foreach ($this->getLatest(10) as $order) { $table->row([ $order['title'] => $order['editUrl'], // associative -> becomes link $order['date'], // simple -> becomes text [$order['total'], 'some-class'] // array -> class is added ]); } Now, we need to get page edit urls. By changing getLatest() and getRandom() methods to return edit links in addition to previous fields protected function getLatest($limit = 5, $start = 0) { // find last $limit orders, starting from $offset $orders = $this->pages->find("template=order, sort=-created, limit=$limit, start=$start"); return $orders->explode(function ($order) { return [ 'title' => $order->title, 'date' => date('Y-m-d h:i:s', $order->created), 'total' => $order->total, 'editUrl' => $order->editUrl ]; }); } protected function getRandom($limit = 5) { $orders = $this->pages->find("template=order, sort=random, limit=$limit"); return $orders->explode(function ($order) { return [ 'title' => $order->title, 'date' => date('Y-m-d h:i:s', $order->created), 'total' => $order->total, 'editUrl' => $order->editUrl ]; }); } and tweaking JS file to render first column as links function renderRow(row) { return `<tr> <td><a href="${row.editUrl}">${row.title}</a></td> <td>${row.date}</td> <td>${row.total}</td> </tr>`; } we get a much more practical dashboard.
    1 point
  34. I'm missing this feature - in my case I have an "Active" checkbox on some items, and want new items to be active by default. "Inactive" in my case wouldn't make much sense, and I'm also not fond of the double negation... "not inactive" - that's poor semantics.
    1 point
  35. PHP's date expects a Unix timestamp. $page->closing most likely returns a formatted date that is set in the fields output formate setting. Try: <?php echo date("Y-m-d", $page->getUnformatted("closing")); ?> The created and updated system values return unix timestamps by default i think, so this is why they work in your example.
    1 point
×
×
  • Create New...