Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 03/07/2018 in all areas

  1. Hi, So today I will writing a small tutorial on developing templates in Processwire using Twig Template, Processwire is a highly flexible CMS which gives developers/designers/users options and allows easy extension of the platform. So here goes the tutorial What is Twig Template ? Simply put in my own words, Twig is a modern templating engine that compiles down to PHP code, unlike PHP, Twig is clean on the eyes , flexible and also quite *easy* to have dynamic layout site with ease ,without pulling your hair out. Twig is trusted by various platforms. It was created by the guys behind Symfony. Take this code as an example {% for user in users %} <h1>* {{ user }}</h1> {% endfor %} This will simply be the equivalent in PHP World <?php $userArray = ["Nigeria","Russia"]; foreach($userArray as $user): ?> <h1><?= $user ?></h1> <?php endforeach; The PHP code though looks simple enough however, you start to notice that you have to be concerned about the PHP tags by ensuring they are closed properly , most times projects gets bigger and comes complex and harder to read/grasp, and also in PHP you can explicitly create variables in the template making it very hard to read as it grows and prone to getting messy WordPress is a major culprit when it comes to that regard. Have you ever wanted to created separate layouts for different pages and break your sites into different parts e.g Sidebar, Comment Section, Header Section ? the regular approach would be to create individual pages for each section and simply add them as templates for the pages and with time, you can end up having tons of templates, however Twig allows you to easily inherit templates and also override the templates where you can inject content into the block easily. Don't worry if you don't understand the concept, the following parts will explain with an example of how to easily inherit layouts and templates. Layout <!DOCTYPE html> <html lang="en"> <head> {{include("layout/elements/header.twig")}} </head> <body> <div class="container-fluid" id="minimal"> <header id="pageIntro"> <div class="bio_panel"> <div class="bio_section col-md-6"> <h1>Okeowo Aderemi</h1> <h2>{{ page.body }}</h2> </div> </div> <div class="clearfix"></div> </header> <section id="page-body"> <div class="container"> <div id="intro" class="col-md-7 col-lg-7"> <h1>About me</h1> <h2> {{ page.summary }} </h2> </div> {block name="content"}{/block} <a style="font-size:1.799783em; font-style:italic;color:#d29c23" href="{{pages.get('/notes').url }}">Read more articles</a> </div> <div class="clearfix"></div> </div> </section> </div> <footer> <div class="header-container headroom headroom--not-top headroom--pinned" id="header-container"> {{include("layout/elements/footer.twig")}} </div> </footer> </body> </html> This is basically a layout where we specify blocks and include other templates for the page, don't panic if you don't understand what is going on, I will simply break down the weird part as follows: Include This basically is similar to native PHP 'include', as it's name suggests it simply includes the templates and injects the content into the layout , nothing out of the ordinary here if you are already familiar with php's include function. {{ output }} This simply evaluates the expression and prints the value, this evaluate expressions, functions that return contents , in my own short words it's basically the same as <?= output ?> except for the fact that it's cleaner to read. {% expression %} unlike the previous this executes statements such as for loops and other Twig statements. {% for characters in attack_on_titans %} <h1> {{characters}} </h1> {% endfor %} This executes a for loop and within the for loop, it creates a context to which variables in that context can be referenced and evaluated, unlike dealing with the opening and closing PHP tags, Twig simply blends in with markup and makes it really quick to read. I will simply post the contents of both the header and footer so you can see the content of what is included in the layout header.php <meta charset="utf-8"/> <meta content="IE=edge" http-equiv="X-UA-Compatible"/> <meta content="width=device-width, initial-scale=1" name="viewport"/> <title> {{ page.title }} </title> <link href=" {{config.urls.templates }}assets/css/bootstrap.min.css" rel="stylesheet"/> <link href="{{config.urls.templates }}assets/css/main.min.css" rel="stylesheet"/> <link rel='stylesheet' type='text/css' href='{{config.urls.FieldtypeComments}}comments.css' /> <link rel="stylesheet" href="{{config.urls.siteModules}}InputfieldCKEditor/plugins/codesnippet/lib/highlight/styles/vs.css"> <script type="text/javascript" src="{{config.urls.siteModules}}InputfieldCKEditor/plugins/codesnippet/lib/highlight/highlight.pack.js"></script> <script src="{{config.urls.templates }}assets/js/vendors/jquery-1.11.3.min.js"> </script> <script src="{{config.urls.templates }}assets/js/vendors/bootstrap.min.js"> </script> <script src="{{config.urls.FieldtypeComments}}comments.js"></script> <link rel="stylesheet" type='text/css' href="{{config.urls.templates}}js/jquery.fancybox.min.css"> <script src="{{config.urls.templates}}js/jquery.fancybox.min.js"></script> {block name="javascriptcodes"}{/block} footer.php <nav class="site-nav pull-right"> <div class="trigger"> <a class="page-link" href="{{pages.get('/about').url}}"> <span>{</span> About <span>}</span> </a> <a class="page-link" href="{{pages.get('/notes').url}}"> <span>{</span> Journals <span>}</span> </a> <a class="page-link" target="_blank" href="https://ng.linkedin.com/in/okeowo-aderemi-82b75730"> <span>{</span> Linkedin <span>}</span> </a> <a class="twitter page-link" target="_blank" href="https://twitter.com/qtguru"> <span>{</span> Twitter <span>}</span> </a> </div> </nav> There's nothing special here, other than twig simply injecting these fragments into the main layout , the next part is the most interesting and important concept and benefit that Twig has to offer {% block content %}{% endblock %} This tag simply creates a placeholder in which the content would be provided by the template inheriting this layout, in lay terms it simply means child templates will provide content for that block, the 'content' simply uses the name 'content' to refer to that specific block, so assuming we were to inherit this template it would simply look like this. Inheriting Template Layout {% extends 'layout/blog.twig' %} {% block content %} <div class="container blog-container"> <section class="blog"> <header class="blog-header"> <h1> {{page.title}} </h1> <h5 class="blog_date"> {{page.published|date("F d, Y")}} </h5> <br> </br> </header> <div class="blog_content"> <hr class="small" /> {{page.body}} <hr class="small" /> </div> </section> </div> {% endblock %} {% block nav %} <div class="col-md-4 col-xs-4 col-sm-4 prev-nav"> <a href="{{page.prev.url}}"> ← Prev </a> </div> <div class="col-md-4 col-xs-4 col-sm-4 home-nav"> <a href="{{homepage.url}}"> Home </a> </div> <div class="col-md-4 col-xs-4 col-sm-4 next-nav"> <a href="{{page.next.url}}"> Next → </a> </div> {% endblock %} In this snippet you can easily notice how each blocks previously created in the header and layout are simply referenced by their names, by now you will notice that twig doesn't care how you arrange the order of each block, all Twig does is to get the contents for each blocks in the child templates and inject them in the layout theme, this allows flexible templating and also extending other layouts with ease. Twig in Processwire Thanks to @Wanze we have a Twig Module for Processwire and it's currently what i use to build PW solutions to clients https://modules.processwire.com/modules/template-engine-twig/ The Modules makes it easy to not only use Twig in PW but also specify folders to which it reads the twig templates, and also injects Processwire objects into it, which is why i can easily make reference to the Pages object, another useful feature in this module is that you can use your existing template files to serve as the data provider which will supply the data to be used for twig template. take for example, assuming I wanted the homepage to display the top six blog posts on it, TemplateEngineTwig will simply load the home.php ( Depending on what you set as the template), it is also important that your twig file bears the same name as your template name e.g home.php will render into home.twig here is an example to further explain my point. home.php <?php //Get the Top 6 Blog Posts $found=$pages->find("limit=6,include=hidden,template=blog-post,sort=-blog_date"); $view->set("posts",$found); The $view variable is the TemplateEngine which in this case would be Twig, the set method simply creates a variables posts which holds the data of the blog posts, the method allows our template 'blog.twig' to simply reference the 'posts' variable in Twig Context. Here is the content of the 'blog.twig' template blog.tpl {% extends 'layout/blog.twig' %} {% block content %} <div class="block_articles col-md-5 col-lg-5"> {% for post in posts %} <div class="article_listing"> <span class="article_date"> {{post.published}}</span> <h2 class="article_title"> <a href="{{post.url}}">{{post.title}}</a> </h2> </div> {% endfor %} {% endblock %} So home.php sets the data to be used in home.tpl once Twig processes the templates and generates the output, twig takes the output from the block and injects it in the appriopriate block in the layout, this makes Processwire templating more flexible and fun to work with. The major advantage this has; is that you can easily inherit layouts and provide contents for them with ease, without the need of running into confusions when handling complex layout issues,an example could be providing an administrator dashboard for users on the template side without allowing users into the Processwire back-end. You can also come up with several layouts and reusable templates. Feel free to ask questions and any concerns in this approach or any errors I might have made or overlooked. Thanks
    6 points
  2. As @horst said, ImageExtra is the way for now, but Ryan has stated it's in the plans for the core: It's one of the key things I'd like to see implemented ASAP actually.
    2 points
  3. So I stumbled over the request to allow limiting templates to be used only once under every parent page in this thread and found that this would actually come in handy (also in a site I've built). The code can be found on github and soon also in the module repo. After installation, you'll find a new checkbox "Only once per parent" in the family tab when editing a template.
    1 point
  4. Only reason I didn't do this for Tracy is that I doubt many people use this option - just figured it was one extra permission to clog up the list that perhaps only a very small percentage of users will actually apply to a role in their system. Does that seem fair enough? Or are lots of you actually using to to give Tracy access to non-superusers? If so, I can add to the install routine.
    1 point
  5. I really need something like that many times. Perhaps a partial solution is a module based on Textarea that the Inputfield Type is an HTML editor with design blocks. I tried to develop one but since I'm not a "real" programmer, I could not make much progress. Maybe someone who knows how to develop modules in Processwire can do it, it does not seem very complicated. It was based on GrapesJS http://grapesjs.com/ an open source Javascript library and it is very easy to include it in a project https://github.com/artf/grapesjs#usage. Here you can see a working example http://grapesjs.com/demo.html, the library exports the HTML code that can be stored in the Textarea. You just have to include the CSS that the frontend uses to make it work.
    1 point
  6. Yeah, similar experiences here. We have used a theme called The-7 (iirc) for 4 WP-sites. The theme had half a dozen variations of everything: 5 different tab elements, 7 accordion types, x sliders/carousels etc. etc. To ensure style consistency, we had to write a manual so authors could remember which items to choose and how. But tbh, I can fully understand why some ppl like such stuff. To most people, more is more.
    1 point
  7. I am trying to get the strftime format from the function convertDateFormat(). However, the output has $ signs in it which i cannot use in strftime.(example of conversion : $d/$m/%Y what strftime actually uses: %d/%m/%Y). Am I in the wrong here or is there some way to format strftime? Thank you in advance :).
    1 point
  8. This has been my experience with a site I inherited (wordpress) that was using the Oxygen plugin. There are template partials stored under the plugin menu that you "call" from the visual builder (ie once you go to edit a page). It really became a nightmare on finding what parts were being used, and where all of this data was being housed in the backend. Giving the client/user a way to drag/drop content seems nice, especially when you know the project is over. However, when you come back to it for any updates, the project has turned into a tangled mess with no map. I do apologize if this has became a rant on Wordpress, as I still enjoy wordpress for blogs. It really comes down to having a nicely structured backend where it all "just makes since".
    1 point
  9. I personally use a more verbose method when developing a side, as a max files may be changed during developement. // get the field unformatted, always returns an array, (not bound to context) $images = $page->getUnformatted('images'); // check if an image is available then always is via count if(count($images)) ... // get the image of a single image field always is via the first method $image = $images->first(); When switching from single image to multiple images or vice versa, always has potential to break some code. That's why I use this verbose method during development and only rework the code as a last step before deploying to public.
    1 point
  10. Alternatively, you already get the root page as an object in the first line of your code, so you can compare $child to $root and exclude it that way. $root = $pages->get("/"); $children = $root->children(); $children->prepend($root); foreach($children as $child) { // DoSomething1 ... if ($child->numChildren(true) && $child !== $root) { // DoSomething2 ... } }
    1 point
  11. The correct word is all times, I haven't come across any site builder that allows 100% control over content without creating a mess or even without the need for code, the closest so far I can think of is October CMS which generates a templates from the frontend and allows you to tweak them, but it requires knowledge of Twig or blade (not sure), really this is an important topic you raised, I have a client i tried convincing to try Processwire, but the client wasn't interested in coding and wanted to builder to achieve all their needs. The only thing i can think of is a Builder 100% built around Processwire API(s) meaning, when they create a Page and add content and use modules from builder, at the backend it should replicate the same process like creating a template, downloading the modules but that will be a very difficult task to achieve. However I would be happy to hear what ideas you have and we can research further upon that. so far Processwire is the best CMS platform that's minimal and straight forward but for developers more. ImpressPages comes close to what you have in mind, I will keep an eye on this thread and contribute should i have more information. If this can be achieved I think we can further push PW to clients and have them replace WordPress, at the same time we should be careful we don't create another WordPress in this ecosystem. what do you think ?
    1 point
  12. You can achieve something (that I think is better) using a the Repeater Matrix (profield). I was actually playing with this the other day. I created several "blocks" ie image/text, quotes, slideshow etc etc and with very little effort (output styling not included) had a homepage built. I like this approach better than Visual Builder as it allows the developer to have a bit more control of the output (styles/layout). In my experience, vb can lead to bloat, and a very fractured site structure. It also, at times, makes it difficult to find where pieces of the site are located.
    1 point
  13. Hi, <a href='index.htm#' data-filter='<?= $item->title ?>'> Why single quotes here? <div class="portfolio-item <?= $item->title ?> "> Space not needed before the closing double quote? <img src="<?=$single->images->first->url ?>" alt="<?= $single->images->description->first ?>"> Order of the "description" code? (Sometimes you may need the use of curly braces in order to make the code work when there are quite a few "->".) Do you have code highlighting (for html, css, php...) available/activated in your code editor? It helps. Often, a little detail can make it not work.
    1 point
  14. I dunno why yet, but can confirm the weird behavior. The function works when fed with strings but gives weird results when input is taken from the array. // output looping through getDateFormats() l, j F Y : %A, $-d %B %Y j F Y : $-d %B %Y d-M-Y : $d-%b-%Y dMy : $d%b%y d/m/Y : $d/$m/%Y d.m.Y : $d.$m.%Y d/m/y : $d/$m/%y d.m.y : $d.$m.%y j/n/Y : $-d/$-m/%Y j.n.Y : $-d.$-m.%Y j/n/y : $-d/$-m/%y j.n.y : $-d.$-m.%y Y-m-d : %Y-$m-$d Y/m/d : %Y/$m/$d Y.n.j : %Y.$-m.$-d Y/n/j : %Y/$-m/$-d Y F j : %Y %B $-d Y-M-j, l : %Y-%b-$-d, %A Y-M-j : %Y-%b-$-d YMj : %Y%b$-d l, F j, Y : %A, %B $-d, %Y F j, Y : %B $-d, %Y M j, Y : %b $-d, %Y m/d/Y : $m/$d/%Y m.d.Y : $m.$d.%Y m/d/y : $m/$d/%y m.d.y : $m.$d.%y n/j/Y : $-m/$-d/%Y n.j.Y : $-m.$-d.%Y n/j/y : $-m/$-d/%y n.j.y : $-m.$-d.%y Will investigate more. Edit : https://github.com/processwire/processwire/blob/master/wire/core/WireDateTime.php#L86 The code actually has those $ symbols. Submitted as an issue.
    1 point
  15. I think the simplest way would be to use Beas module ImageExtra , or you look into it and "borough" some code. Info: https://www.kf-interactive.com/blog/adding-custom-fields-for-images-in-processwire/
    1 point
  16. I've quickly had a look at the Hanna Code page and couldn't find any API to achieve what you are after. The only things I can think of are cookies or session. You can save the ID of Page B in a session or the values of Page B that you are after (modified, created, etc) $return = $attr["url"]; $id = (int) $return; $embeddedPage = $pages->get($id); echo $embeddedPage->body; $modified = $embeddedPage->modified; //echo date("l jS \of F Y h:i:s A", $modified); $session->set('my_page_id', $id); // $session->set('my_page_modified', $modified); You can then grab the value using $modifiedDate = $session->get('my_page_modified');
    1 point
  17. Not currently. You will be able to do this in the next version of Media Manager (v12) which will be out in the next couple of weeks. Media editing has been changed. Editors can now edit the media page directly, meaning, you can edit the image and other fields directly in the page where it lives using native ProcessWire features. ProcessWire now allows the editing of a file name by clicking on it. By editing the media page directly, you will be able to do this and lots of other stuff. See the gif below for an illustration.
    1 point
  18. I must be missing something then. Do you: Want to grab $embeddedPage and use it later on in your page or Want to echo $embeddedPage properies (body, created, etc) right within your Hanna Code? If #2, you are already doing it by echo $embeddedPage->body. E.g., the following works correctly for me (code in the Hanna Code itself) $return = $attr["url"]; $id = (int) $return; $embeddedPage = $pages->get($id); echo $embeddedPage->body; $modified = $embeddedPage->modified; echo date("l jS \of F Y h:i:s A", $modified); I have this Hanna Code Tag in Page A, where 1049 is the ID of Page B. [[test url=1049]] That outputs Page B's modified date. But this seems too easy. I have a feeling I'm still not getting you.
    1 point
  19. I found that the real cause is the php method realpath() used inside Duplicator.module. Reference: http://php.net/manual/en/function.realpath.php#example-2719 The method is generating the backslashes in Windows, which makes the zip extraction look like this. Here is my fix. $source = realpath($source); //search this line $source = str_replace('\\', '/', $source); //add this line $file = realpath($pathname); //search this line $file = str_replace('\\', '/', $file); //add this line The installer runs nicely now.
    1 point
  20. Yeah, I think that would be great - I think we need one feature-rich version going forward that has all these improvements. I don't expect Ryan will accept a PR, but I do remember sometime ago he suggested I could take over this module if I wanted - personally I don't really want to because I already have too many to support, but perhaps if we both had commit rights on the main repo we could share the load?
    1 point
  21. @Roych I pasted your code of the first post and it has syntax errors, so you should sort those out first. I recommend you follow guidelines such us: https://www.ntchosting.com/encyclopedia/scripting-and-programming/php/php-in/ In the article there is the "Possible yet not recommended usage:" section. Do not do that For example, do not do this: <?php foreach($pages->find("parent=1034") as $item) { echo "<li><a href='index.htm#' data-filter='.{$item->select->category}'>$item->title</a></li>"; } ?> instead: <?php foreach ($pages->find("parent=1034") as $item) : ?> <li> <a href='index.htm#' data-filter='<?= $item->select->category ?>'> <?= $item->title ?> </a> </li> <?php endforeach; ?> Much cleaner and a harder to make mistakes. But most importantly, do not mix coding styles
    1 point
  22. Lots of us too! See a few posts starting from this one: https://processwire.com/talk/topic/12208-tracy-debugger/?do=findComment&comment=126847 Let's start a donate to Adrian movement! And AOS fans should ask @tpr to implement a donation button as well!
    1 point
  23. OK, we are close to the solution. If it can help, I am using: ProcessWire v3.0.62 Login/Register v0.0.2 with option "Features to use" set to all (including "Use email address for login rather that user name") and option "Profile form fields" set to "E-mail Address (required)" and "Set Password (required)" more "First name" and "Last name". Furthermore, after the login form submission (failed) in the Session logs I get these 2 rows: 2 seconds ago --- Error: Failed login for '' - Unknown user: 2 seconds ago --- Error: Failed login for 'my_email-email.com' - Unknown user: my_email-email.com
    1 point
  24. Thank you - I will check, as now, I don't know why isn't working
    1 point
  25. @ryan thanks a lot! I had a little use case where I couldn't resist to use the new feature. It was only for pages with text fieldtypes. I used two little bootstrap scripts to export & import some pages. export.php import.php
    1 point
  26. I have made a pull request. You may take the changes for use before the module update. Code changes to support ListerPro
    1 point
  27. Hi arjen, I'll dig those up again. Not sure why I would have put an expiration on the pastes.
    1 point
  28. @teppo You´re right. Thanks for pointing this out. So I added $config->sessionFingerprint = 2; to site/config.php. Option 2 Fingerprints only the remote IP and not the user-agent
    1 point
  29. @jmartsch, two notes about that: Session fingerprint is a security feature, and should only be disabled when absolutely necessary. In your case this might actually be the situation, pointing this out in case someone else reads this It's never a good idea to modify anything in the wire directory – instead of that, copy the directive to /site/config.php and change it's value there.
    1 point
  30. To prevent being logged out when dev-tools is open add: $config->sessionFingerprint = 2; in your site/config.php (my PW version = 2.7) No more logouts. Problem solved. EDIT: Modified the instructions as teppo pointed out.
    1 point
  31. FYI, I routinely use Piwik for my web analytics on all my websites and any client's properties that I maintain. I can usually correlate the errors you are referring about to guest activity (i.e. unknown individuals or bots). If it's related to my own activity, I can look at the page affected (Piwik gives me this information) and then troubleshoot what PW template is associated with the problem (page). I'm quite sure there are other ways of tracking down this information, however that has worked for me. What I'm glad about is that PW gives up the error for me to troubleshoot.
    1 point
  32. Just reading your additional responses. Interesting that it doesn't affect all users. Try setting $config->sessionFingerprint to false, as perhaps the logout is the result of a dynamic IP address change or something in the user agent string changing. Though if the logout only occurs on one page, that still points to something up with that particular page. I'm actually wondering about something on the server outside of PW, like mod_security or similar messing with the session due to a combination of factors present on that page.
    1 point
  33. I think this forum software may have been made before the newer longer email addresses, but I'm sure they'll account for that in a future update. A lot of email address validators currently validate the last portion as being between 2 and 4 characters, and I'm guessing that's the case with IP.Board. That reminds me, I need to go double check that PW's $sanitizer->email() isn't one of them. The first thing I'd suggest doing is enabling debug mode by editing /site/config.php and setting the $config->debug=true; That should enable any obvious errors to appear on the screen. Getting logged out from a particular page suggests that some kind of output is occurring before the http headers, causing the session cookies to be lost. Remember to turn debug mode back off once you've tested things out to see if there are any errors being suppressed. Your admin theme appears to be heavily modified (found the admin login page), so that's one thing to look at. Restore the non-modified admin theme, by replacing the /wire/modules/AdminTheme/AdminThemeDefault/ with the original. Move your modified version to /site/modules/AdminThemeCaelan/ (or something like that) so that you can install core upgrades without losing your modified admin theme. The same goes for any core module–if you need to modify it, copy it to /site/modules/ModuleName/ and work with that instead. Otherwise you'll lose all your changes every time you upgrade PW. Speaking of upgrades, you might also want to upgrade to the latest PW dev version 2.5.20, as it contains a lot of fixes and enhancements that your site may benefit from. Though the issue you mention with logouts is not one I've heard of before. But it's worth a try. If none of this helps feel free to PM me a login so that I can take a look at the page in question and I can examine the http headers to see what is causing the session to break.
    1 point
  34. Edit well if there not too many projects you could try and evaluate the dates and store them on runtime to sort by that value afterwards when you loop them out. $pa = $pages->find("template=project"); if(count($pa)){ foreach($pa as $p){ if(!$p->numChildren) continue; // no assignments // store a temporary value to the project page $p->tmp_date = $p->children("sort=-due_date")->first()->due_date; } // output the projects foreach($pa->sort("-tmp_date") as $p){ echo "<li>$p->title</li>"; if(!$p->numChildren) continue; foreach($p->children("sort=-due_date") as $a){ // output assignments } } } This will work well if you don't have thousands of pages. The module approach would be the more efficient and scaleable.
    1 point
×
×
  • Create New...