Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 06/22/2019 in all areas

  1. MarkupMenu is a markup module for generating menu trees. When provided a root page as a starting point, it generates a navigation tree (by default as a HTML "<ul>" element wrapped by a "<nav>" element) from that point onwards. If you've also provided it with current (active) page, the menu will be rendered accordingly, with current item highlighted and items rendered up to that item and its children (unless you disable the "collapsed" option, in which case the full page tree will be rendered instead). Modules directory: https://modules.processwire.com/modules/markup-menu/ GitHub repository: https://github.com/teppokoivula/MarkupMenu Usage As a markup module, MarkupMenu is intended for front-end use, but you can of course use it in a module as well. Typically you'll only need the render() method, which takes an array of options as its only argument: echo $modules->get('MarkupMenu')->render([ 'root_page' => $pages->get(1), 'current_page' => $page, ]); Note: if you omit root_page, site root page is used by default. If you omit current_page, the menu will be rendered, but current (active) page won't be highlighted etc. A slightly more complex example, based on what I'm using on one of my own sites to render a (single-level) top menu: echo $modules->get('MarkupMenu')->render([ 'current_page' => $page, 'templates' => [ 'nav' => '<nav class="{classes} menu--{menu_class_modifier}" aria-label="{aria_label}">%s</nav>', 'item_current' => '<a class="menu__item menu__item--current" href="{item.url}" tabindex="0" aria-label="Current page: {item.title}">{item.title}</a>', ], 'placeholders' => [ 'menu_class_modifier' => 'top', 'aria_label' => 'Main navigation', ], 'include' => [ 'root_page' => true, ], 'exclude' => [ 'level_greater_than' => 1, ], ]); Note: some things you see above may not be entirely sensible, such as the use of {menu_class_modifier} and {aria_label} placeholders. On the actual site the "nav" template is defined in site config, so I can define just these parts on a case-by-case basis while actual nav markup is maintained in one place. Please check out the README file for available render options. I'd very much prefer not to keep this list up to date in multiple places. Basically there are settings for defining "templates" for different parts of the menu (list, item, etc.), include array for defining rules for including in the menu and exclude array for the opposite effect, classes and placeholders arrays for overriding default classes and injecting custom placeholders, etc. ? MarkupMenu vs. MarkupSimpleNavigation TL;DR: this is another take on the same concept. There are many similarities, but also some differences – especially when it comes to the supported options and syntax. If you're currently using MarkupSimpleNavigation then there's probably no reason to switch over. I'd be surprised if someone didn't draw lines between this module and Soma's awesome MarkupSimpleNavigation. Simply put I've been using MSN (...) for years, and it's been great – but there are some issues with it, particularly in the markup generation area, and it also does some things in a way that doesn't quite work for me – the xtemplates thing being one of these. In some ways less about features, and more about style, I guess ? Anyhow, in MarkupMenu I've tried to correct those little hiccups, modernise the default markup, and allow for more flexibility with placeholder variables and additional / different options. MarkupMenu was built for ProcessWire 3.0.112+ and with PHP 7.1+ in mind, it's installable with Composer, and I have a few additional ideas (such as conditional placeholders) still on my todo list. One more small(ish) difference is that MarkupMenu supports overriding default options via $config->MarkupMenu. I find myself redefining the default markup for every site, which until now meant that each site had a wrapper function for MarkupSimpleNavigation (to avoid code / config repetition), and this way I've been able to leave that out ? Requirements ProcessWire >= 3.0.112 PHP >= 7.1.0 If you're working on an earlier version of ProcessWire or PHP, use MarkupSimpleNavigation instead.
    13 points
  2. This week I've been working on core 3.0.134 but am going to keep the version number at 3.0.133 for another week while I continue with updates there. Commit log. One of the updates already present that I'm finding quite useful is a nice upgrade to InputfieldSelector (like used by Lister and ListerPro) which simplifies the field selection process as there are no longer separate "Field" and "Field…" selections for fields that have subfields, and they are now bundled into one. This reduces the number of selectable fields and likewise speeds up the selecting/filtering process. I'll have more details on that next week, along with other updates. I'm going to be working remotely next week (not vacation) and do not yet know what the internet situation is going to be yet, so on the chance that I don't have good internet access, I may not have an update next week, but hopefully will. Have a great weekend!
    9 points
  3. I have no experience with this Tribe events thing, so can't speak for that, but for the bulk of the content I'd recommend skipping the database export idea and going with the built-in REST API. While the REST API has its quirks, going directly to database for exports is going to be a major pain in the a*s in comparison. Not entirely unlike exporting ProcessWire content with raw SQL... ? It would be best to have a separate copy of the site at hand first, but after that it's basically as simple as installing the ACF to REST API plugin (which adds ACF field data to the REST API results) – and of course making sure that the REST API is enabled in the first place. You should be able to query and export all your pages, articles, and any custom post types with this method. Once you have the data, importing it to ProcessWire is going to be a breeze. (Note: based on some quick googling the Tribe events plugin also provides REST API support. I haven't used this, but it looks promising.) Also, in case that idea won't pan out, you can always rely on existing solutions, such as WP All Export (which provides a GUI for exporting content, including ACF data). Admittedly I haven't worked with this plugin before, but it's the companion plugin for WP All Import, the de facto standard plugin solution for complex imports. WP All Import is a bit of a monster and can feel clunky (hence devs often prefer custom imports for long-running, often-used, scheduled stuff), but for one-off cases it's a really handy tool. -- Edit: in case anyone is wondering, the WP REST API was first announced on June 17th 2013, which would be a week or so after Ryan started this thread. It didn't make it's way into the core until 2015, and even then it was for a very long time considered "a work in progress". It's been more than five years since this thread was started, so it shouldn't come as a big surprise that some things have changed ?
    4 points
  4. In contrast to you and several others here my focus is more towards frontend development, some technical SEO stuff and only a few bits of real programming. Some hooks, some minor module stuff but nothing fancy. So I looked for something that is super easy to use with everything I need for my jobs. I used and tested a lot of tools but at the end I stayed with Laragon as it's doing exactly what I need. In the last few days I started to use PW Kickstart from @bernhard which adds another layer of comfort to my workflow. As it works as expected I feel no need to change my setup.
    2 points
  5. That's a nice addition to Bracket Pair Colorizer 2.
    2 points
  6. I discovered indent-rainbow today: https://marketplace.visualstudio.com/items?itemName=oderwat.indent-rainbow I don't like visual distractions when coding, and I thought this might contribute to that, but the colors are very very subtle so I'm keeping this one installed.
    2 points
  7. @teppo Thanks for your insights and links. I have stumbled over import-/export WP plugins, and tried out two or three that sounded promising, but none of them did a clean job with ACF pages. I will definitely take a closer look at WP REST. The task I will definitely hate the most are internal links. WP stores them as hardcoded links. If all URLs inside PW will change* (conceptual question of course), updating those countless links inside RTE fields to avoid broken links will be tedious. But I'm sure I'll whip out something that'll work (regex, .htaccess 301 redirects, storing old URLs in hidden PW fields too etc.) * i.e. in case we'll not keeping the old WP structure and hence not going to use /site/templates/includes/hooks.php like Ryan did (his first post in this thread)
    2 points
  8. Since you guys asked for it, I'll take a stab at a case study on the development process. Most of the development was done in about a week and a half. I started with the basic profile, but it ended up being something somewhat similar to the Blog profile in terms of how it's structured. Below I'll cover some details on the biggest parts of the project, which included data conversion, the template structure, the front-end development and anything else I can think of. Data Conversion from WordPress to ProcessWire One of the larger parts of the project was converting all of the data over from WordPress to ProcessWire. I wrote a conversion script so that we could re-import as many times as needed since new stories get added to cmscritic.com almost daily. In order to get the data out of WordPress, I queried the WordPress database directly (my local copy of it anyway) to extract what we needed from the tables wp_posts for the blog posts and pages, and then wp_terms, wp_term_relationships, and wp_term_taxonomy for the topics and tags. WordPress stores its TinyMCE text in a state that is something in between text and HTML, with the most obvious thing being that there are no <p> tags present in the wp_posts database. Rather than trying to figure out the full methodology behind that, I just included WP's wp-formatting.php file and ran the wpautop() function on the body text before inserting into ProcessWire. I know a lot of people have bad things to say about WordPress's architecture, but I must admit that the fact that I can just include a single file from WordPress's core without worrying about any other dependencies was a nice situation, at least in this case. In order to keep track of the WordPress pages imported into ProcessWire through repeat imports, I kept a "wpid" field in ProcessWire. That just held the WordPress post ID from the wp_posts table. That way, when importing, I could very easily tell if we needed to create a new page or modify an existing one. Another factor that had to be considered during import was that the site used a lot of "Hana code", which looked like [hana-code-insert name="something" /]. I solved this by making our own version of the Hanna code module, which was posted earlier this week. Here's an abbreviated look at how to import posts from WordPress to ProcessWire: $wpdb = new PDO("mysql:dbname=wp_cmscritic;host=localhost", "root", "root", array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'UTF8'")); $posts = wire('pages')->get('/posts/'); $sql = " SELECT * FROM wp_posts WHERE post_type='post' AND post_status='publish' ORDER BY post_date "; $query = $wpdb->prepare($sql); $query->execute(); while($row = $query->fetch(PDO::FETCH_ASSOC)) { $post = $posts->child("wpid=$row[ID]"); // do we already have this post? if(!$post->id) { // create a new post $post = new Page(); $post->template = 'post'; $post->parent = $posts; echo "Creating new post...\n"; } $post->of(false); $post->name = wire('sanitizer')->pageName($row['post_name']); $post->title = $row['post_title']; $post->date = $row['post_date']; $post->summary = $row['post_excerpt']; $post->wpid = $row['ID']; // assign the bodycopy after adding <p> tags // the wpautop() function is from WordPress /wp-includes/wp-formatting.php $post->body = wpautop($row['post_content']); $post->save(); echo "Saved post: $post->path\n"; } What I've left out here is the importing of images, topics, tags, and setting the correct authors for each post. If anyone is interested, I'll be happy to go more in depth on that, but didn't want to overwhelm this message with code. Template File Structure This site makes use of the $config->prependTemplateFile to automatically include the file _init.php before rendering a template file, and $config->appendTemplateFile to automatically include the file _main.php after. So the /site/config.php has this: $config->prependTemplateFile = '_init.php'; $config->appendTemplateFile = '_main.php'; You may recognize this as being the same setup from the Skyscrapers profile. The _init.php includes files containing functions we want to be available to all of our templates, and set default values for the regions we populate: /site/templates/_init.php /** * Include function and hook definition files * */ require_once("./includes/render.php"); require_once("./includes/hooks.php"); /** * Initialize variables populated by templates that get output in _main.php * */ $browserTitle = $page->get('browser_title|title'); $body = "<h1>" . $page->get('headline|title') . "</h1>" . $page->body; $side = ''; $renderMain = true; // whether to include the _main.php file The includes/render.php file that is included above includes several functions for generating markup of navigation and post summaries, or any other shared markup generation functions. Examples are renderPost(), renderNav(), renderTags(). This is similar to the blog.inc file from the Blog profile except that I'm letting these functions generate and return their own markup rather than splitting them into separate view files. I personally find this easier to maintain even if it's not as MVC. The includes/hooks.php sets up any hooks I want to be present for all of my templates. I could have also done this with an autoload module, but found this to just be a little simpler since my hooks were only needed on the front-end. The main hook of interest is one that makes all posts look like they live off the root "/" level rather than "/posts/" (where they actually live). This was in order to keep consistency with the URLs as they were in WordPress, so that the new site would have all the same URL as the old site, without the need for 301 redirects. /site/templates/includes/hooks.php /** * This hook modifies the default behavior of the Page::path function (and thereby Page::url) * * The primary purpose is to redefine blog posts to be accessed at a URL off the root level * rather than under /posts/ (where they actually live). * */ wire()->addHookBefore('Page::path', function($event) { $page = $event->object; if($page->template == 'post') { // ensure that pages with template 'post' live off the root rather than '/posts/' $event->replace = true; $event->return = "/$page->name/"; } }); Our /site/templates/_main.php contains the entire markup for the overall template used site wide, from <html> to </html>. It outputs those variables we defined in _init.php in the right places. For example, $body gets output in the <div id='bodycopy'>, $side gets output in the right <aside>, and $browserTitle gets output in the <title> tag. /site/templates/_main.php <?php if($renderMain): ?> <html> <head> <title><?=$browserTitle?></title> </head> <body> <div id='masthead'> // ... </div> <div id='content'> <div id='bodycopy'><?=$body?></div> <aside id='sidebar'><?=$side?></aside> </div> <footer> // ... </footer> </body> </html> <?php endif; ?> We use the rest of the site's template files to simply populate those $body, $side and $browserTitle variables with the contents of the page. As an example, this is an abbreviated version of the /site/templates/post.php template: /site/templates/post.php // functions from /site/templates/includes/render.php $meta = renderMeta($page); $tags = renderTags($page); $authorBox = renderAuthor($page->createdUser); $comments = renderComments($page); $body = " <article class='post post-full'> <header> <h1>$page->title</h1> $meta </header> $page->body $tags $authorBox $comments </article> "; if(count($page->related)) { $side = "<h4>Related Stories</h4>" . renderNav($page->related); } What might also be of interest is the homepage template, as it handles the other part of routing of post URLs since they are living off the root rather than in /posts/. That means the homepage is what is triggering the render of each post: /site/templates/home.php if(strlen($input->urlSegment2)) { // we only accept 1 URL segment here, so 404 if there are any more throw new Wire404Exception(); } else if(strlen($input->urlSegment1)) { // render the blog post named in urlSegment1 $name = $sanitizer->pageName($input->urlSegment1); $post = $pages->get("/posts/")->child("name=$name"); if($post->id) echo $post->render(); else throw new Wire404Exception(); // tell _main.php not to include itself after this $renderMain = false; } else { // regular homepage output $limit = 7; // number of posts to render per page $posts = $pages->find("parent=/posts/, limit=$limit, sort=-date"); $body = renderPosts($posts); } The rest of the site's template files were handled in the same way. Though most were a little simpler than this. Several were simply blank, since the default values populated in _init.php were all that some needed. Front-end development using Foundation 4 The front-end was developed with the Foundation 4 CSS framework. I started with the Foundation blog template and then tweaked the markup and css till I had something that I thought was workable. Then Mike and I sent the _main.php template file back and forth a few times, tweaking and changing it further. There was no formal design process here. It was kind of a photoshop tennis (but in markup and CSS) where we collaborated on it equally, but all under Mike's direction. After a day or two of collaboration, I think we both felt like we had something that was very good for the reader, even if it didn't originate from a design in Photoshop or some other tool like that. I think it helps a lot that Foundation provides a great starting point and lends itself well to fine tuning it the way you want it. I also felt that the mobile-first methodology worked particularly well here. Comments System using Disqus We converted the comments system over to Disqus while the site was still running WordPress. This was done for a few reasons: Disqus comments provide one of the best experiences for the user, in my opinion. They also are platform agnostic, in that we could convert the whole site from WP to PW and not have to change a thing about the comments… no data conversion or importing necessary. Lastly, ProcessWire's built-in comments system is not quite as powerful as WordPress's yet, so I wanted cmscritic.com to get an upgrade in that area rather than anything else, and Disqus is definitely an upgrade from WP's comments. In order to ensure that Disqus could recognize the relations of comment threads to posts, we again made use of that $page->wpid variable that keeps the original WordPress ID, and also relates to the ID used by the Disqus comments. This is only for posts that originated in WordPress, as new posts use a ProcessWire-specific ID.
    1 point
  9. Windows Terminal has been released and can be downloaded from the Microsoft Store. Article: https://www.theverge.com/2019/6/22/18701365/microsoft-windows-terminal-10-download-store-features WSL (WSL2 in the near future) + VSCode + Terminal... Microsoft is awesome.
    1 point
  10. IMPORTANT NOTICE!!! Hi everyone. I just discovered something pretty important with this module. If you are using the Role Name or Custom PHP Code option and matching a page name or path, you MUST be sure to check "No Access" for the "If no match, give all access or no access?" setting. If you don't do this, a site editor could change the name of their branch parent, which would result in no match and hence they could gain access to the entire page tree. I am tempted to actually remove these two matching options to improve security - is anyone using them? To make life easier, I have added a new "Role Specified Branch Parent" option which allows you to select a branch parent on a per role basis, rather than per user. I think in most cases this is the best option to use. Please be sure to check your settings and if you can please switch to the "Role specified" or "User specified" options in the new version. Let me know if you have any questions or concerns. Thanks, Adrian
    1 point
  11. If by this you mean stuff like "10 orders placed today..." or staff to staff messaging, etc, that's already planned and partly modelled. Notifications will mainly be shown on the main landing page and on orders. I haven't started on the GUI yet, that's why there is nothing to see currently :-). Most likely this is what we'll tackle last, before beta release.
    1 point
  12. For me the most helpful is Intelephense:
    1 point
×
×
  • Create New...