Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 10/20/2018 in all areas

  1. Those are some sweet updates Ryan! Seeing as you've been working on transferring the blog posts over to the new processwire.com site, do you think you could add a feature so that blog posts automatically get anchors on every <h3>? There could be an automatic table of contents from those anchors if you think that would be good, but having anchors is the main thing. Because there is so much useful documentation of features within the blog I often want to save references to specific sections within a blog post, or provide a link to a section in a forum reply. Having heading anchors would make it possible to do this.
    9 points
  2. Work continues on the new processwire.com website, while the core received several updates including support for Markup Regions "pw-optional" attributes, upgrades to WireArray that make it a lot more useful, and more. https://processwire.com/blog/posts/processwire-3.0.117-core-updates/
    6 points
  3. Yes! And a prev/next link on each post ? Great updates! Love them ?
    6 points
  4. I'm still fairly new here having switched to using ProcessWire for pretty much every project (hence the frequent questions ? ) from Concrete5. Concrete5 has had Gutenberg-esque block-based front-end editing for nearly 10 years longer than Wordpress. Although a finished site using C5 can look great for a site editor/frontend-only user with various drag-drop layout tools, we were finding c5 development had become very convoluted and was starting to make simple website projects unnecessarily complicated. C5's core weighs in at a hefty filesize too. This is why we started researching for alternatives and landed happily at ProcessWire. I already find WP development unnecessarily convoluted, especially compared to the simplicity of ProcessWire. And with Gutenberg, I can only foresee the same sort of headaches ahead for the WP community that we were finding with C5 - namely conflicts between blocks and the core and frontend UI and your design style and functionality being dictated to by the CMS in order to work in the Gutenberg features. Discovering ProcessWire has been a revelation for us - the clean API and design agnostic approach are making everything from simple website projects to complex web apps a breeze, with the added bonus of super simple frontend editing that not only wows client's used to site builder platforms but requires basically zero onboarding too. I would urge anyone thinking of building out Gutenberg inspired modules for ProcessWire to consider the above comments to ensure that what makes ProcessWire special is retained.
    5 points
  5. News Update - 10 October 2018 I know many of you are eagerly awaiting the next version of Padloper. I thought I'd give you a couple of updates regarding progress. First, I'd like to thank you for the feature requests and your support. As previously stated, it will not be possible to accommodate all requests and those that may be accommodated may have to wait till other releases. OK, to the update. The following have so far been achieved. FieldtypeProducts A new Fieldtype for storing products including their variants, if any. This allows for easy retrieval and storage of data and and API that makes it easy to filter, search, manipulate, update, etc product details. So..: $foo = $products->find("colour=red,quantity<10"); $bar = $product->first(); echo $bar->size; echo $bar->price; // etc Discounts We have a new discounts class that allows for 4 types of discounts each with generic and specific requirements. Percentage discount Fixed amount discount Free shipping discount Buy X Get Y discount In turn, as applicable, the discounts are subject to generic conditions including customer country,named customers, customers subscribing to your newsletter, global usage, customer usage limits, customers who abandoned carts, start/expiration dates, etc. There are also discount-specific conditions including whether to apply discount to entire order, specific products or specific categories/collections, minimum requirements (purchase amount or quantity of eligible products in cart), etc. Import/Export Products This class allows for importing products into your shop as CSV, JSON or arrays. It is 98% done. It will also allow for exporting products as CSV (and maybe in future, as JSON, if there is demand). MarkupPadloper This is WIP to eventually replace PadRender. This allows for retrieving products, product tags, product categories, etc, either as ready-to-render (i.e. includes the markup) vs retrieving the raw product details so one can use their own markup, anywhere, anyhow, to output products. Other A bit of work on customer notifications (including email templates) and FieldtypeOrders for orders plus some other stuff. I got a lot to do, so I better get cracking! ? Thanks for reading.
    4 points
  6. Hello @Michael van Laar, To answer you main question first: Yes, I don't see a performance issue in using either. The RepeaterMatrix and PageTable field are similar in that they use pages which can be used for content elements. I prefer the RepeaterMatrix field, because it has a better usability in that you can edit the content elements on the same page. Before there was the RepeaterMatrix field, I also used for a few times the PageTable field with the PageTableExtend module. But if you have bought the ProFields I would recommend using the RepeaterMatrix. On the back-end it is best when using a RepeaterMatrix field to have the default option on, that all repeater items should be loaded via Ajax. I assume the page you linked is not cached? According to WebPagetest the document was loaded after 670ms, which is good (best would be under 300ms). Everything after that are your styles and scripts which are not related to the RepeaterMatrix. For a regular XML sitemap, you would just output all visible pages on your website. So in your example that would be around 150 pages. But even more should be no issue. I usually use the module MarkupSitemapXML for this. With other sitemap formats I have not much experience, I have just learned about them: Image sitemap: You could output the link of a page and all it files like this. Video sitemap: This seems to work only with self hosted videos. So not embeded videos. But I could be done like mentioned before. All of those solutions mentioned are decoupled from the RepeaterMatrix and PageTable field. On every page you only output the content elements used for the page. So the total count of content elements wouldn't matter. If you have a page with 10 or 50 content elements, it should not really be a performance issue. I can think of no use case, where you would output all content elements of all pages at the same time. As mentioned before the sitemap example can be handled without have to deal with RepeaterMatrix or PageTable. Yes, thats what the RepeaterMatrix field is build for. Caching always helps if you don't have user input on your page. But depending on how you build your templates, without cache there should be no performance issue. For content elements I can't think of a better solution than RepeaterMatrix. If you have more questions, it would be best if you would share some screenshots of your set-up. ? Regards, Andreas
    3 points
  7. Those sound exactly like the migrations / helper functions my Migrations module does provide ?
    2 points
  8. Nice, "pw-optional". Right now I'm finally back in ProcessWire re-building some websites and Markup Regions is a breeze to work with.
    2 points
  9. Your posts sound like you have two problems here: One is code organization and the other is performant calculation of leaderboards. This is why people usually use some kind of separation between core domain logic (ranking of restaurants) and the website layer (actually serving a website) in their code. Duplicating such essential code as calculating the position of a restautrant because it‘s needed in multiple places of your website is a bad idea. The risk of both places getting out of sync over the time of development is one I'd not like to take. For the leaderboard calculation you seem to currently use the most simple but inefficient way: Load all the information needed into php and calculate the positions for the set of data in memory. There are two types of optimizations from here: Move the calculation logic nearer to the data it needs to work on, a.k.a. do the calculation in mysql instead of in php; This saves on the need to move all the data from mysql to php, which scales very poorly. Persist positions for as long as possible, a.k.a. caching; It seems you currently recalculate positions each time someone visits that page. But they do only change when a new ranking / comment is posted on your website. Also usually a little bit of delay between new comments and updating the ranks is not a problem. Given those two optimizations there are three concrete ways to handle things: Just Opt. 1: Write an SQL query, which can just return e.g. restaurantID + position in sorted order for all the leaderboards you have: global / per city / …. Just Opt. 2: Use some of ProcessWire's caching options to save your various leaderboards still calculated in php. Both Opt. 1 and Opt. 2: Write an SQL query and create a materialized view in the db to query. This way the calculation and caching happens in mysql, which is probably as performant as it can be. Edit: The missing puzzle piece here is treating those leaderboards as the data to store.
    2 points
  10. I understand now. There are a couple of ways you could do this - a simpler way and a more performant way. In the demo code below my selector is for sorting news items by title - you would adjust the selector as needed to find your restaurants. This code would go in the template file for your restaurant location template. 1. Simpler way: probably fine if there is not a huge number of matched pages // Function to get number plus ordinal suffix function ordinal($number) { $suffixes = ['th','st','nd','rd','th','th','th','th','th','th']; if(($number % 100) >= 11 && ($number % 100) <= 13) { return $number. 'th'; } else { return $number. $suffixes[$number % 10]; } } // Define the selector that finds your pages $selector = "template=news_item, sort=title"; // Get all the matched pages $items = $pages->find($selector); $position = $items->getItemKey($page) + 1; // add 1 to adjust for zero index $position_str = ordinal($position); // the position with ordinal suffix echo "This is the $position_str item."; 2. More performant way: if there are a large number of matched pages you can use an SQL query which avoids the overhead of loading all the pages First work out the SQL query for matching your pages: $query = $pages->getPageFinder()->find(new Selectors($selector), ['returnQuery' => true])->getQuery(); bdb($query); // Use Tracy barDumpBig() to dump the SQL query Now tweak the query a bit because you only need the IDs of the matched pages. The finished example for my sorted news items is below: $sql = "SELECT pages.id FROM `pages` LEFT JOIN field_title AS _sort_title ON _sort_title.pages_id=pages.id WHERE (pages.templates_id=45) AND (pages.status<1024) ORDER BY _sort_title.data"; $query = $database->query($sql); // Get the IDs in an array, flipped so the ID is the key and the position is the value $results = array_flip($query->fetchAll(\PDO::FETCH_COLUMN)); $position = $results[$page->id] + 1; // add 1 to adjust for zero index $position_str = ordinal($position); // the position with ordinal suffix echo "This is the $position_str item."; Credits: 1, 2, 3
    2 points
  11. "Now WireArray can be used on its own with just about any data type. No longer is this base class limited to working with just Wire-derived objects." I've been longing for this for quite a while. Sometimes dreams come true ? Thank you Ryan!
    2 points
  12. FYI, you can set the age the temp directory lives for with the maxAge option: $files->tempDir('RockPdf', ['maxAge' => 600]); // or $files->tempDir('RockPdf', 600);
    1 point
  13. Exactly. That's one of the reasons why I don't like site profiles and created ProcessWire Kickstart. It's not finished at all, but it works for me and it's just a showcase of a different and more flexible approach. Yes, you're right, and that would have been my suggestion for you to try. Module development is great and a lot of fun. From what I've read in your posts (this one was awesome btw) I guess you'll also like it a lot ? Creating pages, templates and fields can be a little tedious though. There is the Migrations Module that has some helpers. I'd love to have a nicer and cleaner API though for such tasks, also handling naming collisions etc., eg createField, createTemplate, addFieldToTemplate, removeFieldFromTemplate, editInContext, etc, etc Maybe you want to take that idea as inspiration and start something like this while playing around with pw module development? ?
    1 point
  14. Yes, that's what the code in my previous post is for. There's no foreach loop involved. Did you try it? That code provides a way of working out the position of any page relative to a set of pages it is a part of. When you put the code in the template file of a restaurant location page then the page is represented by the API variable $page. If you want to use it in some other template file (e.g. your account template) then you would substitute $page with $p where $p is a restaurant location page.
    1 point
  15. Module config settings are stored as a JSON string in a single field in the database. Module config fields don't store data in separate tables as per the fieldtypes you might be used to from using fields in a template. So certain core inputfields that need special support from a fieldtype to store their data can't be used in module configs: Files, Images, Repeater, PageTable, maybe a few more. BTW, personally I would use a standard page/template to store site settings rather than a module config for just this reason.
    1 point
  16. I have not tried this before, but I see that it has been brought up before here: Though perhaps @Macrura might be able to help out on this.
    1 point
  17. Just a note if anybody gets in trouble when creating complex PDFs... It seems that there is some bug related to the tempdir that is created for the pdf. If the PDF takes long to render it can happen that this folder is deleted automatically before the PDF is done. Then you'll get an error. In my case this solved it: $pdf = $modules->get('RockPdf'); $pdf->settings([ 'tempDir' => $this->config->paths->root . 'site/assets/cache/WireTempDir/.RockPdf/test/', ]); For sure just a quickfix, but I'm busy...
    1 point
  18. Thanks for sharing this. This is something I've heard before too while giving training. I guess the Explorer / Finder kinda sidebar gives people more comfortable feeling. Especially at first sight. Usually within the first session they understand the page tree and the need for the tree in the sidebar. Jup, I've heard this too. This is where ProDrafts comes in. Or one the versioning modules by @teppo. For clients hidden / unpublished isn't always obvious and I believe the need ProDrafts in your base install to give people a comfortable way of to create content. This is a paid module. The funny thing is that while I've heard all your points come up regularly, I have never heard the term messy. All clients find the CMS very clean, focused, easy to navigate since the page tree reflects there website structure. Most say the need to unlearn other experiences like WordPress with the Custom Post Types or Joomla.
    1 point
  19. That was not my intension, sorry. I never saw it like that - I always had to remove the unused profiles manually.
    1 point
  20. We recently finished a relaunch of our agency website: https://www.meuter.de/ Features: Minimalistic and modern design Page transitions and preloading for a really fast browsing expierence Custom content builder for blog posts, references and subpages Content is king: Many case studies and blog posts Under the hood: Webpack with ES6 and SCSS LQIP with Base64 Images Lazy loading with lazysizes Automatic generation of scrset images, Base64 placeholder images and background positions Page transitions with barba.js Preload pages when user hover over link Interactive components which reacts on scroll events. Built with ScrollMagic Handwritten template Modules used: ProFields Markup Sitemap XML Video embed for YouTube/Vimeo Our Content Builder for a reference: Clean page structure: The code for the image generator: <?php // ------------------------------ // Image modifications // ------------------------------ /** * Responsive Image Options */ class imageModifier { // Responsive Sizes from lowest to highest private $responsiveSizes = array(320,640,768,1024,1366,1600,1920,2560); private $base64ImageWidth = 20; private $imageOptions = array( 'upscaling' => true ); // Return a string with the srcset information public function getSrcSet($image){ // Create an emptry output string $srcSetOutputString = ''; // Create each image for each size foreach ($this->responsiveSizes as $responsiveSizeId => $responsiveSizeWidth) { // Check if the image is bigger then the final size if($image->width >= $responsiveSizeWidth){ // Create the image and put the url into the output string $srcSetOutputString .= $image->width($responsiveSizeWidth, $this->imageOptions)->url; // Put the width in the output string $srcSetOutputString .= ' ' . $responsiveSizeWidth . 'w' . ','; } } //When the original image is smaller then the highest quality if($image->width < $this->responsiveSizes[count($this->responsiveSizes) - 1]){ // Create the image and put the url into the output string $srcSetOutputString .= $image->width($image->width)->url; // Put the width in the output string $srcSetOutputString .= ' ' . $image->width . 'w' . ','; } // Remove last commata $srcSetOutputString = rtrim($srcSetOutputString, ','); // Return the srcSet information return $srcSetOutputString; } //Returns the lowest quality image public function getLowestQuality($image){ return $image->width($this->responsiveSizes[0]); } //Returns the highest quality image public function getHighestQuality($image){ // if image is bigger or same than max quality if($image->width >= $this->responsiveSizes[count($this->responsiveSizes) - 1]){ return $image->width($this->responsiveSizes[count($this->responsiveSizes) - 1]); } // When image is smaller then the highest quality else { return $image; } } // Returns the base64 data string public function getBase64($image){ // Get the image path $imagePath = $image->width($this->base64ImageWidth)->filename; // Get the image extension $imageType = pathinfo($imagePath, PATHINFO_EXTENSION); // Get the data of the image $imageData = file_get_contents($imagePath); // Create the base64 Code $base64Code = 'data:image/' . $imageType . ';base64,' . base64_encode($imageData); // Return the code return $base64Code; } // Returns the position point public function getPosition($image){ // Get distance from top $distanceTop = '' . intval($image->focus()['top']); // Get distance from left $distanceLeft = '' . intval($image->focus()['left']); // Return the position with percent return $distanceLeft . '% ' . $distanceTop . '%'; } } Thanks for the community and Processwire. It is a joy to use ?
    1 point
  21. I was thinking of the GUI but if the current one does not need re-thinking then that's ok, just ignore my comment please ?
    1 point
  22. It's all there. When I was starting with PW years ago, I very soon looked at modules and all the Process modules that make the admin. So I never really needed a tutorial, just look and learn copy and try. It was so simple and since it's all in the same fashion using the already familiar API and "normal" modules. So you have a ton of modules Ryan has already built that make ProcessWIre backend you can use as examples/learning. Isn't it nice?
    1 point
  23. $pages->getPageFinder()->find(new Selectors($selector), array('returnQuery' => true))->getQuery(); I seem to recall teppo posting a less verbose version of this, but I cannot find it currently.
    1 point
  24. You're welcome. I often use the cheatsheet to lookup stuff: http://cheatsheet.processwire.com/ Maybe the following works: $children = $page->parent->children(); $total = $children->count(); $pagePos = $children->getItemKey($page) + 1; // Assume the keys are zero based
    1 point
×
×
  • Create New...