Leaderboard
Popular Content
Showing content with the highest reputation on 04/19/2014 in all areas
-
So I spent the last couple of hours in creating a module which imitates the Textpattern thing. Take a look. LanguageInstantInstall.module (You have to go to Setup -> Languages and add a new language). It's not completely finished (a function to update, etc. will follow) but it does what it has to do What do you think? / nico7 points
-
Hi there, today I was playing around with Bootstrap 3.1 and PW. I downloaded a boilerplate template for Bootstrap3.1 from initializr. Then I took a virgin PW 2.4 install and merged the initializr files with the files in PW template folder. I didn't touch any of the content and tried to replicate the site-default template as close as possible with Bootstrap markup. Only thing I added is a carousel instead of the random image. The result is a default PW install with Bootstrap look. For the main menu, I took Damienov's rendering function from here. To install my template: 1. Do a default PW 2.4 install 2. Unzip the attached file 3. Replace the original site/templates folder with the one from the attached zip file. The result can be seen here: http://pwboot.webseiten-fuer-alle.de/ templates.zip5 points
-
What Teppo said - since the admin user is a known ID it would be trivial to log in as the admin this way and wreck your site (or actually install a module like Hanna Code, throw in some PHP and do significantly more damage to your server). It's an extremely bad idea.4 points
-
There's an issue with your proposed approach; namely the way isLoggedin() works. As you can see, it only checks if this user is guest, i.e. if it's ID matches that of the guest user. It's going to return true for any user you've fetched with $users->get(). Putting that aside for a moment, there's an even bigger issue here. If I'm getting this right, you're logging user in, and later trying to check if that specific user (123) is logged in when anyone opens URL like http://example.com/user/123. Isn't that a huge security issue right there? How would you validate that the user opening this URL is the same one that earlier authenticated using correct credentials? I really wouldn't recommend pursuing this. There are going to be severe security implications no matter how you approach it. .. but if you really have to, I'd consider some sort of token-based authentication method. When the user logs in, provide an URL she can visit to log in. Typically that URL would be invalidated after single login (and after certain period of time) to make it slightly more secure. Automatically generating something like this would still be very risky (please don't do it). It's more often used in combination with, say, valid email: user types in her email and receives an URL that's valid for certain period of time and allows her to login (preferably once) before it's invalidated.4 points
-
Hi everyone, I've made a snippet update for the bootstrap 2.2.2 navbar by Soma to Bootstrap 3.x. I also added a multi level menu fix since it was removed from Bootstrap 3. Screenshot: topnav_inc <?php /* Navigation for ProcessWire using the Bootstrap 2.2.2 markup This menu was written by Soma based on work by NetCarver and a bit thrown in by Joss Navigation Bootstrap 3 update by Damienov, with multi level dropdown support fix */ function renderChildrenOf($pa, $output = '', $level = 0) { $output = ''; $level++; foreach ($pa as $child) { $atoggle = ''; $class = ''; if ($child->numChildren && count($child->parents) == 1) { $class .= 'dropdown'; $atoggle .= ' class="dropdown-toggle" data-toggle="dropdown"'; } else if ($child->numChildren && count($child->parents) > 1 ) { $class .= 'dropdown-submenu'; $atoggle .= ' class="dropdown-toggle"'; } else if ($child->numChildren && $child->id != 1) { $class .= 'dropdown-menu'; } // Makes the current page and it's top level parent add an active class $class .= ($child === wire("page") || $child === wire("page")->rootParent) ? " active" : ''; $class = strlen($class) ? " class='" . trim($class) . "'" : ''; if ($child->numChildren && count($child->parents) == 1) { // Add Caret if have children $output .= "<li$class><a href='$child->url'$atoggle>$child->title <b class='caret'></b></a>"; } else if ($child->numChildren && count($child->parents) > 1) { $output .= "<li$class><a tabindex='-1' href='$child->url'$atoggle>$child->title</a>"; } else { $output .= "<li$class><a href='$child->url'$atoggle>$child->title</a>"; } // If this child is itself a parent and not the root page, then render it's children in their own menu too... if ($child->numChildren && $child->id != 1) { $output .= renderChildrenOf($child->children, $output, $level); } $output .= '</li>'; } $outerclass = ($level == 1) ? "nav navbar-nav" : 'dropdown-menu'; return "<ul class='$outerclass'>$output</ul>"; } // bundle up the first level pages and prepend the root home page $homepage = $pages->get(1); $pa = $homepage->children; $pa = $pa->prepend($homepage); // Set the ball rolling... echo renderChildrenOf($pa); Add to your CSS /* Bootstrap 3 navbar with multi level fix */ .dropdown-submenu{ position:relative; } .dropdown-submenu > .dropdown-menu { top:0; left:100%; margin-top:-6px; ; -webkit-border-radius:0 6px 6px 6px; -moz-border-radius:0 6px 6px 6px; border-radius:0 6px 6px 6px; } .dropdown-submenu:hover > .dropdown-menu{ display:block; } .dropdown-submenu > a:after{ display:block; content:" "; float:right; width:0; height:0; border-color:transparent; border-style:solid; border-width:5px 0 5px 5px; border-left-color:#cccccc; margin-top:5px; margin-right:-10px; } .dropdown-submenu:hover > a:after{ border-left-color:#ffffff; } .dropdown-submenu .pull-left{ float:none; } .dropdown-submenu.pull-left > .dropdown-menu{ left:-100%; margin-left:10px; -webkit-border-radius:6px 0 6px 6px; -moz-border-radius:6px 0 6px 6px; border-radius:6px 0 6px 6px; } note: My php skills are crappy , so feel free to add some fixes on the code2 points
-
2 points
-
You were unable to understand post #7 because my thinking was wrong at this time ;-) If you just need the page as an object that keeps data (as I did), than this is the best way to go.2 points
-
That unused space isn't there for me most of time cause I use image field often on a column 30-40%.2 points
-
Pages within the CMS don't have a .html extension, so you are right to remove that. Your next try with just the name ("portfolio") is close - but when you are on another page, it is treated as another directory - so it assumes you want that file (portfolio) in the current directory (site-map). Change the topNav links to look like this: <ul id="topnav"> <li><a href="<?php echo $config->urls->root ?>" class="home"><span></span></a></li> <li><a href="<?php echo $config->urls->root ?>portfolio/" class="portfolio"><span></span></a></li> <li><a href="<?php echo $config->urls->root ?>services/" class="services"><span></span></a></li> <li><a href="<?php echo $config->urls->root ?>about/" class="about"><span></span></a></li> <li><a href="<?php echo $config->urls->root ?>contact/" class="contact"><span></span></a></li> </ul> <!-- topnav ends --> It uses the $config API variable to retrieve the site's root URL, and then just appends the name of the page you want to link to.2 points
-
As promised here is a quick overview of our current setup and what we are looking for: We're a communications agency with about 40 employees (and growing) doing all sorts of design, strategy and marketing related stuff. Some do have a good IT background but most aren't familiar with the command line (and most likely never will be). Our portfolio includes print materials as well as strategic papers, movies and digital products like apps and websites. So we have a quite large amount of files with different requirements regarding workflow, versioning and size. At the moment all the files are stored on a network storage and mounted as a drive on every computer. We also have a mixed setup of Operating Systems: Mac and Windows as workstations plus Linux as a server OS. Currently we do version our (programming) code using an internal gitlab server as well as using github for sharing code with freelancers - so no need for a code versioning tool as we are happy with the current one. All of our communication and calendar stuff is done with exchange (saas/office365). As for project planning software we are currently searching for a new tool ( https://www.openproject.org/ is my current favorite one). But that isn't related to file management at all: I'm quite a big fan of "one software per requirement" as most "one software to rule them all" tools are bloated and buggy (at least from my experience). To sum up all this background information we need a tool that: can handle 40-60 users can handle very large (binary) files can handle a enormous amount of files (Currently: 966GB of Data in 149350 Files) is easy to understand and use - even for people who don't have an IT background (Dropbox Style...) integrates with our workflows makes versioning and sharing files easy (this might include sharing them with customers but isn't the main point) is self hosted in our internal lan for speed reasons2 points
-
2 points
-
Except for name, all fields in ProcessWire Pages are custom fields . Yes, even Title is a custom field....Unless you specify a field as hidden, all fields added to a template will be available to all pages using that template....in the order you sorted them in the template. And here's a bonus for you....that doesn't mean that when you view that page (am talking frontend)...that you will see any fields automatically. Nada...you will have to output them yourself in your template file in the order you want, if you want! Sorry, I got carried away there for a minute...Enjoy!2 points
-
ProCache version 2.0.0 (now in beta, available in the ProCache board) adds these new features: Multi-host support If you've got multiple hostnames pointing at your ProcessWire site, now you can cache them separately. SSL / HTTPS support ProCache will now optionally cache HTTPS requests separately from HTTP requests. Switch to PDO queries Previous versions of ProCache used MySQLi. This new version now uses PDO, consistent with ProcessWire 2.4.0. This improves performance when generating and maintaining caches. Requirements ProCache 2.0 requires ProcessWire 2.4.0 or newer. ProCache 1.x still supports previous versions of ProcessWire.2 points
-
Hey, I think if you're a new user to Processwire it's really complicated to understand how to install a new language. I've got a couple of ideas how to optimize this: Better Description: It would be important to show at least a link to a documentary (for example: https://processwire.com/api/multi-language-support/) and add some screenshots to it. So that you get a step by step tutorial. Additonally I would add a description to the "Language Translation field" on how to get the language files (link to: http://modules.processwire.com/categories/language-pack/) Instant install: Another way to add languages could be the Textpattern way. But I'm not sure where to place this. In the module->new section? After clicking "add new" in the setup->languages page (that's the way I would prefer. And at the bottom a link "You're language doesn't exists? Create it now!")? Or somewhere else? What do you think about this? And what are your experiences with adding a new language for the first time? / Nico1 point
-
Requirements: * jQuery script like Fancybox or Colorbox, etc PW won't get into your way. It doesn't care what you use. You could use Mootools if you wanted to (does that still exist, btw?). So, yes, use whatever you wish. But you need to include the script yourself * Image file uploader from the PW admin system (front end) would be nice, but I am ok with uploading the images to the server via FTP if I have to. You can do both. For frontend, there's loads of examples on the forums about (see also Soma's Gists..). With frontend, the more important thing is security.... * Must have control over the generated thumbnail size. This is critical. The full-size images will be set by hand using Photoshop. Yep. Right out of the box. See here... * Access to the grid presentation of gallery thumbnails via a CSS file. Like I have said (previously)...PW doesn't generate or output markup. So, you can output whatever you wish with whatever CSS you wish. PW doesn't know or care so to speak * Pagination: ability to have a single page presenting one gallery of images or a page presenting multiple galleries on a single page. Pagination...right out the box...See here * Fast load times. Excuse me? Oh, OK, what Macrura said * Ability to include image title names and captions OR the ability to not include titles and captions. See here. echo $page->image->url; echo $page->image->description, etc, etc for what you want to show. If you don't what to show them, don't echo them. There are other techniques (yes, the dreaded PHP!) - e.g. echo description only when it is not empty...We can show you how to do that....Also, there's no image title per se, but there's ways round that... Note: In the above "image" examples, this is just echoing out a custom image field called image. There is no field in ProcessWire called image (or anything else actually). Like any other field, you give your image field whatever name you wish (within acceptable fields' naming conventions, of course). So, this could well have been echo $page->photo, etc. Final thing, you can have either a single image field or a multiple images field. For the latter, that's an array...As with all arrays..you need to foreach (loop) through it to output stuff. There are other stuff like echo $page->photo->first() but that's for later1 point
-
I found some helpful information on utf8mb4 http://mathiasbynens.be/notes/mysql-utf8mb41 point
-
Once this idea is proven and stabilised, would it be a core module candidate?1 point
-
I'm using vagrant on a Linux box and am quite happy. Only drawback is that file operations on the nfs mounted directory where my sites live are slow sometimes, especially when extracting archives through php. Other than that I love it because I work on different boxes and have the same dev environment. I stumbled across a vagrant box that has PW preinstalled but haven't used it yet.1 point
-
Sorry, in a bit of a rush, so not reading through your post well enough, but is it as simple as: $ordered = $pa->find("template=news, limit=25"); Sounds like you don't want to be finding from the full $pages array anymore, but rather your ordered $pa array?1 point
-
@GuruMeditationi it's worth looking at the source code for the module. it is 410 lines of code. It's pretty bare bare bones as far as forum's go, but the essentials are there to build something awesome if you have the time. "Private messages" You would have to create this feature but, this page makes it seem pretty simple. "Profiles, with the ability to add extra fields etc." Supported through processwire. All users are pages and can have custom fields. "An option that allows comments on articles/pages etc to create a new forum topic, so that users can reply in the forum, or on the actual article/page etc." I'm wondering about the "best" way to do it myself. But certainly looks possible. "Moderation tools, copy, move, close, pin, ban etc." copy, move, delete are all possible through the admin interface. Ban, close and pin would require some simple changes to module i think. Does not look like there is email notification/topic watching support yet either. Forgiveness in for any wrong information, I hope some one can correct me as I am still pretty new to all this as well.1 point
-
I also agree with onjegolders and ryan. As I started with the examples out of this topic as well as skyscrapers, at one point I could not see what certain things were doing. It took me some time to get used to the API and template system. I then started from the DEV branch, and I now build day by day to make it more dynamic. Later on I will manage smoothness.1 point
-
Max, you raise some good points (that have actually been raised before ). I'll comment on two of them: And you are absolutely right. Remember though that PW is still a young system (in relation to it being released open source, i.e.). As with most systems, you first start with making sure that the API documentation is rock solid. This is what PW has done (in my observation). You pick up the API and run with it. Ryan (or somebody else, can't remember) summed it up beautifully in some post. Rather than teach someone how to put together a photo gallery, or a school website, you'd rather give and teach them the tools (API) to put together ANY type of site . You master the API, you master everything (even beyond PW since there is no templating language - it's just OOP PHP). But, there is a place for 'how to' tutorials. However, with the relatively few hands on deck compared to other similar projects, it is impossible to equally concentrate on the API documentation, 'how to' tutorials and answering questions in the forums, not to mention continuing the development of the core PW code. Hence, so far, 'how to' tutorials have largely remained a community effort. Joss has really tried to write a few. He has also posted in the forums an excerpt from his upcoming 'how to' PDF. I also have a few 'how to' tutorials in the pipeline (as you can see from my rather dormant website www.kongondo.com ). We can do with a few more Marys to tell the truth... Good point. And this is one big difference between systems that employ templating systems (like MODx) and pure OOP based systems like PW. When MODx started to crumble under my feet, I was asking myself this same question. I took the plunge into PW and here we are having this conversation. What I have learned using PW is a transferable life skill (seriously). It is like HTML or CSS. It is universally applicable. I never have to learn another templating system. This means I can (relatively easily) pick up any OOP PHP system with good API documentation were PW to disappear tomorrow.... I am happy you are not giving up yet on PW. Yes, to use it, you will have to invest time in it....but we'll be here to guide you every step of the way1 point
-
Hi Can, Yep, that's the correct behavior. Hidden will exclude a page from $pages->find() calls, unless you specify "include=hidden" in your selector. Think of a hidden page like a page that should not be visible in your navigation or lists, but still accessible when one knows the direct URL. Unpublished really means that the page is not yet ready/published for the public, here a 404 is displayed. The same goes for pages with templates that do not have a physical template file associated. Be careful when logged in as superuser, if I remember correctly you'll see unpublished pages. In order to simulate the website for a guest visitor, you could use another browser or the private/incognito mode $pages->get() is an explicit call to retrieve a page. ProcessWire assumes that you want to get it, no matter if it's hidden or not Pw is throwing an 404 if you enter a path that does not exist. Or if a page you want to visit is unpublished or does not have a template file (because Pw does not know what markup to render). You can also throw a 404 anytime yourself, though that is already more advanced stuff. Could you maybe describe us what you want to do? Why would you want to prevent showing a 404? Cheers1 point
-
I think I agree with Ryan on this thread. My approach would be to encourage newcomers (to PHP) to start by breaking thing up in the simpler default way. The need for more complex approaches comes later and is more interesting for those with a more solid foundation in programming.1 point
-
Definitely start modularising things by "including" various files/snippets. It's a good and pretty easy habit to get into. From then on can start looking at modules/delegate approaches.1 point
-
I still use the default install setup of including head.inc and foot.inc the most to be honest (mostly through habit), but there are many ways to approach it as kongondo says.1 point
-
Vagrant is a great piece of software and for sure makes many things easier (as mentioned in the tao). It's just not fitting into OUR process and workflows. If you're (for example) working on one or two bigger projects at the same time with a large team of people vagrant would be a perfect fit for that.1 point
-
1 point
-
Sure! If you switch a lot between different projects you'll have to either start several instances or you'll "up" and "halt" them all the time: The first way is very "costly" in terms of performance, the second one in terms of time The generated isos tend to be quite big (around 2.5 GB): If you're working on 20+ Projects you'll either have to constantly delete (and therefore re-init) them or waste a lot of disk-space We've experienced some rather standard setups to be very slow even if we gave them a crazy amount of Memory (4GB+). This was especially the case with larger ModX sites (which has a slow backend anyway) On Windows (which we use for development) sometimes vagrant just keeped crashing on startup for unknown reasons Crazy caching behaviour: We mounted our local work folders into the VM but sometimes they just won't get updated for minutes Plus some very "Workflow related" issues that are not that important1 point
-
An option to select a site profile in the installation process would be nice. At least there should be an additional blank or the H5BP profile. Example from the other thread:1 point
-
1 point
-
$value = $pages->find( "title%=$q" ); $pa->import( $pages->find( "template=abc, …|…|…=$value" ) ); $pa->import( $pages->find( "template=abc, title%=$q" ) ); What should that do? You have to explaing a little more for a short minded like me. I have an example that shows how to construct your own pager using the PW PagerNav class: // include paginator class require_once($config->paths->MarkupPagerNav . "PagerNav.php"); $pa = new PageArray(); $res1 = $pages->find("template=xyz, title%=space"); $res2 = $pages->find("template=abc, title%=hal"); $pa->import($res1); $pa->import($res2); // config paginator $baseUrl = $page->url; $limit = 4; $start = ($input->pageNum - 1) * $limit; $total = $pa->getTotal(); $pagerNav = new PagerNav($total, $limit, $input->pageNum); $pager = $pagerNav->getPager(); // construct paginator markup foreach($pager as $link) { if($link->pageNum == $input->pageNum) $class .= "on"; if($link->type == "separator") $item = '…'; else $item = "<a class='$class' href='{$baseUrl}page{$link->pageNum}/'>$link->label</a>"; $pagerMarkup .= "<li>$item</li>"; } // output paginator markup echo "<ul class='pager'>" . $pagerMarkup . "</ul>"; echo "total: $total<br/>"; echo "start: $start<br/>"; echo "limit: $limit<br/>"; echo "<ul>"; foreach($pa->find("start=$start, limit=$limit") as $p){ echo "<li>$p->title</li>"; } echo "</ul>"; Also in my ever growing gist examples https://gist.github.com/somatonic/54205361 point
-
Thanks guys. I've just changed the release status to "stable", as there haven't been any real bugs to turn up, and I'm now using it on several production sites. If anyone is interested in seeing more details about the Apache bench test results I did, here they are. This is to the skyscrapers homepage, testing on localhost using the command "ab -n500 -c10 localhost:8888/skyscrapers/demo/". For all tests, I performed a manual pageview to the homepage before running, just to ensure the relevant cache was already active. The server is MAMP running Apache with PHP 5.4.4 on a Mac Pro (early 2008). ProcessWire is version 2.2.13. No caching enabled: Concurrency Level: 10 Time taken for tests: 29.066 seconds Complete requests: 500 Failed requests: 0 Write errors: 0 Total transferred: 11567500 bytes HTML transferred: 11361500 bytes Requests per second: 17.20 [#/sec] (mean) Time per request: 581.310 [ms] (mean) Time per request: 58.131 [ms] (mean, across all concurrent requests) Transfer rate: 388.65 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.7 0 8 Processing: 463 578 59.5 567 810 Waiting: 455 569 59.4 558 792 Total: 463 579 59.5 568 810 Percentage of the requests served within a certain time (ms) 50% 568 66% 591 75% 610 80% 622 90% 661 95% 693 98% 738 99% 760 100% 810 (longest request) Built-in Page cache enabled: Concurrency Level: 10 Time taken for tests: 6.809 seconds Complete requests: 500 Failed requests: 0 Write errors: 0 Total transferred: 11576500 bytes HTML transferred: 11370500 bytes Requests per second: 73.43 [#/sec] (mean) Time per request: 136.178 [ms] (mean) Time per request: 13.618 [ms] (mean, across all concurrent requests) Transfer rate: 1660.35 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.3 0 4 Processing: 93 135 17.6 132 249 Waiting: 90 130 15.7 127 236 Total: 93 135 17.7 132 249 Percentage of the requests served within a certain time (ms) 50% 132 66% 137 75% 141 80% 144 90% 154 95% 166 98% 187 99% 211 100% 249 (longest request) ProCache Enabled: Concurrency Level: 10 Time taken for tests: 0.117 seconds Complete requests: 500 Failed requests: 0 Write errors: 0 Total transferred: 11529500 bytes HTML transferred: 11370500 bytes Requests per second: 4260.61 [#/sec] (mean) Time per request: 2.347 [ms] (mean) Time per request: 0.235 [ms] (mean, across all concurrent requests) Transfer rate: 95942.85 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.2 0 1 Processing: 1 2 0.5 2 4 Waiting: 1 2 0.5 2 4 Total: 1 2 0.6 2 5 Percentage of the requests served within a certain time (ms) 50% 2 66% 2 75% 3 80% 3 90% 3 95% 3 98% 4 99% 4 100% 5 (longest request) ProCache Documentation and Order/Download page.1 point
-
1 point
-
$pageArray->has($page); http://processwire.c...er=has Also it doesn't matter if you add duplicates as PW will remove them anyway.1 point
-
Sorry my fault. I haven't read careful and assumed from your selector that you have authors with a page field "posts" to reference all posts. It's the other way round then. But having it the way I assumed would result in having a simpler code Don't worry, you could either change it if you like it or use this with current (like nico but little improved I think) $authors = $pages->find("template=authors"); // create empty page array $res = new PageArray(); foreach($authors as $a) { // temporarely add postscount property to author $a->postcount = $pages->count("template=posts, author=$a"); $res->add($a); } foreach($res->filter("sort=-postcount, limit=5") as $a){ echo "<p>$a->title ($a->postcount)</p>"; } Let me know if it works for you.1 point
-
// output the 5 authors with the most posts sorted by post count $authors = $pages->find("template=author,posts.count>0")->sort("-posts.count")->find("limit=5"); foreach($authors as $a){ echo "<p>$a->url $a->title ({$a->selec_page_image->count()})</p>"; } This seems to work, but only if you make the sort and limit after the find. I'm not sure what exactly happens, but if you put sort inside first find it will throw error, now it works if added afterwards. The last find is to limit it to 5. Note limiting has to be after the sort, otherwise you get a mixed result obviously.1 point
-
After Ryan's latest incarnation of Blog Profile I wanted to see if I could learn something from there to my own workflow. It turned out very nicely and this seems like a perfect template approach for me. Wanted to share it with you guys. Folder structure under templates folder: templates/markup/ templates/markup/helpers/ templates/markup/layouts/ templates/scripts/ templates/styles/ And it all begins from here: templates/markup/index.php -this is the "complete" html file, it has doctype, head and starting and ending body. There is very little "logic" here, it's more like a container. There is one very important code snippet there though: <?php if ($page->layout) { include("./markup/layouts/{$page->layout}.php"); } else { include("./markup/layouts/default.php"); } ?> Code above goes between header and footer of your site, that will be the main content. I call it layout, but the better name would be "content layout" or "inner layout" or something like that. Then the templates/markup/layouts/ folder will keep at least default.php file, but probably few others, like "threeColumns.php", "frontpage.php", "gallery.php" etc.. you got the idea. Each of the actual pw template files are then purely "controllers" - no actual markup generated there. All markup are done in files inside templates/markup/ folder and it's subfolders. This is how template file templates/home.php on one site I am building right now looks like: <?php // Carousel items $t = new TemplateFile(wire('config')->paths->templates . 'markup/helpers/carousel.php'); $t->set('carouselPages', $page->carousel); $page->masthead = $t->render(); // Tour themes $t = new TemplateFile(wire('config')->paths->templates . 'markup/helpers/items.php'); $t->set('title', "Get inspired from our <strong>tour themes</strong>"); $t->set('items', $page->featured_themes); $t->set('description', $page->themes_description); $t->set('url', $config->urls->root . "themes/"); $t->set('linkTitle', "All themes"); $page->main .= $t->render(); // National parks $t = new TemplateFile(wire('config')->paths->templates . 'markup/helpers/items.php'); $t->set('title', "Seven beautiful <strong>national parks</strong>"); $t->set('items', $page->featured_parks); $t->set('description', $page->parks_description); $t->set('url', $config->urls->root . "national-parks/"); $t->set('linkTitle', "All national parks"); $page->main .= $t->render(); $page->layout = "frontpage"; include("./markup/index.php"); This uses few "helper" markup files from templates/markup/helpers/ folder (namely carousel.php and items.php). Here is the carousel.php for your reference: <?php /* Generates the markup for the frontpage carousel */ if (count($carouselPages) < 1) return; $styles = ''; echo "<div id='carousel'><ul class='rslides'>"; foreach($carouselPages as $key => $p) { echo "<li class='c-item c-item-$key'>"; echo "<img src='".$p->image->getThumb('carousel') ."' alt='' />"; echo "<p>$p->summary</p>"; echo "<a class='button' href='{$p->link->url}'>$p->headline</a>"; echo "</li>"; } echo "</ul></div>"; Then populates the $page->masthead and $page->main properties and then set's the inner layout to "frontpage". That templates/markup/layouts/frontpage.php file is very simple on this site, but could be much more complicated if needed: <div id="masthead"> <?= $page->masthead; ?> </div> <div id="main" class="wrap"> <?= $page->main; ?> </div> Frontpage is rather unique and I could have done all the markup on the frontpage.php file also. But I do want to re-use those "carousel" and "items" components on other places as well. But if I do have totally unique stuff, where I do want to get "quick and dirty" this approach allows it. Then my template file would be something like this: $page->layout = "campaign2012"; include("./markup/index.php"); And then all the markup would be in that templates/markup/layouts/campaign2012.php Blog profile really gave me a good ideas (cleaner folder structure etc) and using TemplateFile class adds nice possibilities. This is of course just a one way to manage your templates, but hopefully someone of you finds this helpful when thinking about how you like to structure this stuff. PS: If you are just getting started with PW, then I recommend using the head and foot includes method from demo install. There is nothing wrong with that method and only more complicated sites starts to benefit from these methods introduces in this topic.1 point
-
I agree, though also think it's a delicate balance. I'd like people to perceive ProcessWire as something like Google (in terms of simplicity) – they can start using it without really having to know anything. They can discover the depth of it as their needs grow. If the depth is out in front, they might be overwhelmed. So I like to keep lots of surface simplicity and let PW grow with them. Most of what you use PHP for in a template is no different than how you would use some other template engine, except in syntax. So something like: <p>$somevar</p> is probably not a good example because there is no logic there, it's just pure output generation. You have to connect your variable to the markup at some point, and it doesn't get any simpler than that. I do think the point is valid when you get down into code that isn't geared towards output generation. But the nature of PW's API is that all the data is put at your fingertips so your template code can focus on output generation. The logic (or lack of it) that one uses in PHP at this level isn't really any different than that provided by something like Smarty, EE or another template engine. What is different is that PW puts all of the site's data at close reach, rather than some specific subset of it. That kind of resembles working in Javascript with the DOM. As you start working at a larger scale, using some discipline in how you do things can help you in the long run. I'm not talking so much about whether something is in a <p> or <li> tag, but about stuff that would create major obstacles if you wanted to completely re-skin your site tomorrow. An example of something that does go beyond output generation a little is a search engine. This is one of the reasons why I like the approaches we've been talking about, because it does a good job of isolating logic where it matters. This particular example doesn't have any real logic of the sort I'm thinking of, but pretend for a moment that it was a much bigger/more complex search engine: search.php <?php $q = $sanitizer->selectorValue($input->post->q); $results = $pages->find("title|body|sidebar*=$q"); $page->body .= $results->render(); include("./main.php"); Or using something like your guys method, where a separate view file generates output separately: search.php <?php $q = $sanitizer->selectorValue($input->post->q); $page->searchResults = $pages->find("title|body|sidebar*=$q"); /* then the output file /includes/search.inc knows to output results from $page->searchResults */ include("./main.php"); Another thing I've found that works well is to put reusable output generation in modules and attach it to PageArrays, not unlike $results->render(). For example, on those villa sites, I have a PageArray::renderVillas hook that can be called anywhere in the site and it always renders villas the same way, provides a sort select at the top, and knows to handle pagination. Likewise, I added a Pages::villas() hook that only finds villas and automatically plugins in the proper limit=n for consistent site-wide pagination. So if I needed to re-skin the site tomorrow, I could update all the site's villa lists in one shot (and there are hundreds of them). As time goes on, I think it'll be good for us to document best practices for large scale use. Like with PHP itself, I'm glad PW doesn't force us into one way of doing things. But I think it'll be helpful to lots of others if we outline what we've found helps to maximize scalability, longevity, etc. At the same time, when it comes to teaching people how to use ProcessWire with examples, I think some of these best practices can confuse the message. I wouldn't suggest that someone learning ProcessWire should use it in pure MVC mode until that is appropriate for their scale and need. It's only when you go bigger and more complex that your template code starts to become something more than a 'view', and it's at that point where you need to start considering the long term. I think this is only worthwhile if you are dealing with some other group of people that is creating the output files, and that group refuses to get anywhere near PHP. Ultimately they just need to be told that there's no difference between <?=$somevar?> and {{$somevar}} except for a lot of unnecessary overhead for the {{$somevar}} version. More here: http://processwire.com/api/why-php-syntax/ It would be easy possible within PW to create markup-generating type of modules. Not sure If that should be done, as It's a little against PW's philosophie. Markup generating modules are fine I think, but I just don't want PW's core to generate any markup. I figure it should always be markup neutral. But markup generating modules can be very handy. Though my preference is for markup generating modules to provide options for customizing that markup. Me too. Though when providing these as something beyond a basic example, I think it invites an audience that is interested in turn-key solutions and not web development. Such things require good support. Thats why I think there is a good fit for paid solutions in this (like Maruchan brought up).1 point
-
Nice discussion, thanks for posting guys. Our setup is pretty much similar to Ryan's (got good tips there when build it), although I have little bit of Soma's method baked in too If I take Ryan's example, then our main.php would be something like this: <html> <head> <title><?php echo $page->title; ?></title> </head> <body> <h1><?php echo $page->title; ?></h1> <?php if ($page->tpl) include("./tpl/{$page->tpl}.inc"); else include("./tpl/default.inc"); ?> </body> </html> Then I would set that $page->tpl in actual template file (like product.php, event.php etc), if I need to. Of course it would also be possible to allow client to choose tpl on certain page templates (using basic page field selection), but I haven't had the need yet. This way I can use same basic wrapper even though markup inside body tags would be totally different. This also allows to have $page->tpl as selection for page editors if that is needed (not yet the case). Most of the time it seems that default.inc works 90% of the time.1 point
-
Thanks for posting Soma, this is an interesting approach and not one I've seen before, but it looks great. The underlying concept and result is similar to the approach I usually use. Since you posted a good description, I'll try to do the same for mine. The only reason you see head/foot files in the default PW profile is because it seems to be simpler for new users to grasp. But I almost never use that approach in my own sites. Like your system, I have a main.php file which is my main markup file. But unlike your system, main.php is included from all the other template files (rather than main.php including them). The other template files focus on populating the key content areas of the site, specific to the needs of the template. Examples of key content areas might include "main" (for center column/bodycopy) and "side" (for sidebar/related info), though often includes several other identified areas. But I'll keep it simple in this case. Here's how it works: basic-page.php <?php $outMain = "<h2>{$page->subtitle}</h2>" . $page->body; if($page->numChildren) $outMain .= $page->children->render(); // list the children $outSide = $page->sidebar; include("./main.php"); main.php <html> <head> <title><?php echo $page->title; ?></title> </head> <body> <h1><?php echo $page->title; ?></h1> <div id='main'><?php echo $outMain; ?></div> <div id='side'><?php echo $outSide; ?></div> </body> </html> The benefit of this approach is that basic-page.php can setup whatever it wants in the key content areas ($main or $side) whether simple like in this example, or something much more complex. I actually prefer for the variables representing the key content areas to be optional. In the scenario above, $outMain and $outSide would have to be defined by every template or they would end up as uninitialized variables in main.php. As a result, I actually use $page as an anonymous placeholder for these variables (making sure they don't conflict with any existing field names) and then let main.php assign defaults if the calling template didn't specify one of them. For example: basic-page.php <?php $page->outMain = "<h2>{$page->subtitle}</h2>" . $page->body; if($page->numChildren) $page->outMain .= $page->children->render(); // list the children // note: no $outSide specified include("./main.php"); main.php <?php // setup defaults when none specified if(empty($page->outMain)) $page->outMain = $page->body; if(empty($page->outSide)) $page->outSide = $page->sidebar; ?> <html> <head> <title><?php echo $page->title; ?></title> </head> <body> <h1><?php echo $page->title; ?></h1> <div id='main'><?php echo $page->outMain; ?></div> <div id='side'><?php echo $page->outSide; ?></div> </body> </html> Final thing to point out here is that main.php is the only template actually outputting anything. Because basic-page.php (or any other template) is determining what's going to go in that output before it is actually sent, your template has the opportunity to modify stuff that you might not be able to with other methods. For instance, the <title> tag, what scripts and stylesheets are loaded, etc. Here's the example above carried further to demonstrate it: basic-page.php <?php // make a custom <title> tag $page->browserTitle = $page->rootParent->title . ": " . $page->title; $page->outMain = "<h2>{$page->subtitle}</h2>" . $page->body; if(count($page->images)) { // display a clickable lightbox gallery if this page has images on it $config->scripts->add($config->urls->templates . "scripts/lightbox.js"); $config->styles->add($config->urls->templates . "styles/gallery.css"); $page->outMain .= "<ul id='gallery'>"; foreach($page->images as $i) { $t = $i->size(100,100); $page->outMain .= "<li><a href='{$i->url}'><img src='{$t->url}' alt='{$t->description}' /></a></li>"; } $page->outMain .= "</ul>"; // add a note to $page->title to say how many photos are in the gallery $page->title .= " (with " . count($page->images) . " photos!)"; } if($page->numChildren) $page->outMain .= $page->children->render(); // list the children include("./main.php"); main.php <?php // if current template has it's own custom CSS file, then include it $file = "styles/{$page->template}.css"; if(is_file($config->paths->templates . $file)) $config->styles->add($config->urls->templates . $file); // if current template has it's own custom JS file, then include it $file = "scripts/{$page->template}.js"; if(is_file($config->paths->templates . $file)) $config->scripts->add($config->urls->templates . $file); ?> <html> <head> <title><?php echo $page->get('browserTitle|title'); // use browserTitle if there, otherwise title ?></title> <?php foreach($config->styles as $url) echo "<link rel='stylesheet' type='text/css' href='$url' />"; foreach($config->scripts as $url) echo "<script type='text/javascript' src='$url'></script>"; ?> </head> <body> <h1><?php echo $page->title; ?></h1> <div id='main'><?php echo $page->get('outMain|body'); // use outMain if there, or body otherwise ?></div> <div id='side'><?php echo $page->get('outSide|sidebar'); // use outSide if there, or sidebar otherwise ?></div> </body> </html> More than half the time, I'll actually just re-use page variables like $page->body and $page->sidebar rather than $page->outMain and $page->outSide. That way there's no need to consider defaults, since $page->body and $page->sidebar untouched technically are defaults. <?php $page->body = "<h2>{$page->subtitle}</h2>" . $page->body . $page->children->render(); But technically you've got a little more flexibility using your own self-assign anonymous variables like outMain and outSide, so figured I'd use that in the examples above. outMain and outSide are just example names I came up with for this example and you could of course name them whatever you want.1 point