Leaderboard
Popular Content
Showing content with the highest reputation on 06/07/2013 in all areas
-
I think this two new upcoming features could make me switch over from Sublime to Brackets in a while : http://dev.brackets.io/preso/cssconf/#/5 Psdlens and Response What do you guys think about?5 points
-
Hey, The Form API has CSRF protection build in, but if you for some reason don't want to use the API you can however use the CSRF protection. Its very simple but it took some time for me to find out, so i figured i share my findings with the rest. What is CSRF? First you need to create a token and a token name you do that as following: $tokenName = $this->session->CSRF->getTokenName(); $tokenValue = $this->session->CSRF->getTokenValue(); Very simple. Now what you want to do is create a hidden input field like this: $html .= '<input type="hidden" id="_post_token" name="' . $tokenName . '" value="' . $tokenValue . '"/>'; Now this will generate something that will look like this: You are done on the form side. You can now go to the part where you are receiving the post. Then use: $session->CSRF->validate(); This will return true (1) on a valid request and an exception on a bad request. You can test this out to open up your Firebug/Chrome debug console and change the value of the textbox to something else. Basicly what this does is set a session variable with a name (getTokenName) and gives it a hashed value. If a request has a token in it it has to have the same value or it is not send from the correct form. Well I hope I helped someone.4 points
-
No need to format the date with PHP. You can set the output format in ProcessWire - check out the field settings of your date fields.3 points
-
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.2 points
-
Hi, There's not much to this one. It doesn't stretch the limits of PW at all but the client is very happy with how easy it is to edit content. http://walkerabercrombie.com.au/ This is my first PW site (I usually use either textpattern & ExpressionEngine) and it was an absolute joy to learn something new. Illustrations by the great: http://lewkeilar.com/ Regards Martin2 points
-
@pwired, there isn't any software that keeps compatibility with old versions of modules forever. That's why I think it's great that you don't have to use lots of third party modules with ProcessWire, and everyone should be aware of the risk of making a website completely dependent of them. Anyway, no one is forced to upgrade immediately to a latest version (or even at all), and Ryan shows he is always very concerned about breaking things for people, so I'm pretty sure that when it comes the time when that step really has to be taken, all modules (or a good alternative to them) will be already compatible. One thing I think it's important to keep in mind here is that software has to evolve. And this evolution will always be a balance between backwards compatibility and new and better features. You really don't want to use a software that stopped in time...2 points
-
Kongondo, you should get some kind of forum member award. What you are posting lately is really amazing and helpful for pw users with little coding expierence. Thanks !2 points
-
Hi everyone, This site got a responsive refresh today - it's practically a brand new site. http://walkerabercrombie.com.au/ Cheers Marty2 points
-
@kongondo Wow that is indeed a confusing statement, thanks Post edited2 points
-
Another minor addition to happy family of ProcessWire modules: Markup Load Atom. Markup Load Atom was forked from Ryan's Markup Load RSS to provide similar functionality for Atom feeds: given Atom feed URL it loads it and allows you to foreach through it and/or render it with built-in render() method. Get it from GitHub: https://github.com/teppokoivula/MarkupLoadAtom Note: I've been using this on a production site for a while now, but haven't really worked much with Atom feeds specifically. If you're a guru in that matter and feel that something is odd or plain wrong here, please let me know and I'll see what I can do. How to use With your own markup: $atom = $modules->get("MarkupLoadAtom"); $atom->load("https://github.com/ryancramerdesign/ProcessWire/commits/master.atom"); foreach($atom as $item) { echo "<p>"; echo "<a href='{$item->url}'>{$item->title}</a> "; echo $item->date . "<br /> "; echo $item->body; echo "</p>"; } Or with built-in rendering: $atom = $modules->get("MarkupLoadAtom"); echo $atom->render("https://github.com/ryancramerdesign/ProcessWire/commits/master.atom"); Installation Installing is identical to most other modules; just copy MarkupLoadAtom.module or whole MarkupLoadAtom directory to your /site/modules/, hit "Check for new modules" at Admin modules area and install "Markup Load Atom". More examples, detailed instructions etc. can be found from README.2 points
-
Hi 3fingers, Here is the code that processes the form: https://gist.github.com/outflux3/5690429 here is the form itself: https://gist.github.com/outflux3/5690423 i didn't post the form results, but that's just a table that shows the results; hopefully the processor and form code will help! -marc2 points
-
nice tip: if you remove "&showoptions=1" from the url, you will have only the list without the search options. So, a very simple example of a link would be: echo "<a href='{$config->urls->admin}page/search/?submit=Search&template=article&category=general_news&sort=created&display=title,path,category,author'>all general news articles</a>"; edit: here is a quite silly example of how this could be used dynamically in a custom page (code and screenshot): echo '<dl>'; foreach($pages->get(1)->children("include=all") as $p){ echo '<dt>'; echo $p->title; echo '</dt>'; echo '<dd>'; echo "<a href='{$config->urls->admin}page/search/?submit=Search&template={$p->template}&sort=created&display=title,path'>search all pages with same template as {$p->title}</a>"; echo '</dd>'; } echo '</dl>';2 points
-
What kills me about this module is that Ryan literally decided to make it because I had something similar in WordPress and needed a substitute solution in ProcessWire (as part of the move of CMS Critic to PW) otherwise I'd have a ton of weird shortcodes in my posts for no reason. He made this module, imported my code from the other module (called Hana Code in WP) and named his Hanna Code (after his daughter) and Poof! a module is born. I am insanely jealous of his mad php skills (but glad they can be bought!)2 points
-
Anyone checked the new version TinyMCE 4 already? It's currently in late beta. The new version looks soooo cool and clean! I cannot wait to see this in PW!2 points
-
This basic tutorial is primarily aimed at those new to PW. It could also serve as a reference to others more seasoned PW users. The question about how to categorise content comes up in the forums now and again. Hopefully with this post we’ll have a reference to guide us right here in the tutorials board. Many times we need to organise our site content into various categories in order to make better sense of the data or to logically and easily access it. So, how do you organise your data when you need to use categories? Here are a few tips gathered from the PW forums on how to go about this. Using these tips will, hopefully, help you avoid repeating yourself in your code and site content and keep things simple. See the links at the end of this post to some useful discussion around the topic of categorisation. Before making decisions about how to organise your site, you need to consider at least three questions: What items on my site are the main items of interest? These could be people or things (cars, plants, etc.). In most cases, these are the most important content on which all the other stuff point to. Where do items need to be grouped into categories? This is about where items need to “live”. It is about the attributes of the items of interest (e.g. responsibilities, job types, colour, etc.). Attributes can have sub-attributes (e.g. a category job type = driver could be further sub-classified as job type role = train driver). Can they live in more than one place? - This is about having multiple attributes. There could be other issues such as the type of content your main items of interest are but that’s for another post. We’ll keep these examples simple. The main principles explained below still apply. There are at least three possible ways in which you can organise your content depending on your answers to the above three questions. These are: Single category Simple multiple categories Complex multiple categories These are illustrated below. Note that this is what I call them; these are not PW terms. 1. Single Category Suppose you need to do a site for a company that’s made up of several Departments each with employees performing unique functions. These could include “Finance”; “Media Communications”; “Administration”; “Technicians”; “Human Resources”; “Logistics”. We ask ourselves the following questions based on our 3 questions above: 1. Q: What items on my site are the main items of interest? A: Employees. 2. Q: What attributes of our items of interests are we interested in? A: Departments. (Single main category) 3. Do the Departments have sub-categories? A: Yes. (Multiple sub-categories) 4.Can Employees belong to multiple sub-categories? A: No. (Single sub-category) We conclude that what we need is a Single Category model. Why? This is because, in Single Categories model, items of interest can only belong to 1 and only 1 main/parent category and within that only 1 sub-category Employees in this company can only belong to one and only one department. Finance guys do their finance and Logistics guys do their stuff. Letting Techies do press conferences is probably not going to work; that we leave to the Media guys . Assuming the company has the following employees - James, John, Mary, Ahmed, Peter, Jason, Barbara etc., arranging our site content to fit this model could look like the following: Items of interest = Employees Categories = Departments Adopting out strategy to keep it simple and logical, let us write down, hierarchically, our employee names against their departments to mimic the PW tree like this: James Finance John Finance Mary Technician Ahmed Logistics Barbara Media Etc. We notice, of course, that departments start repeating. It doesn't look like we are doing this very logically. If we think about this carefully, we will conclude that, naturally, the thing (attribute in this case) that keeps repeating should be the main criteria for our categorisation. This may seem obvious, but it is worth pointing out. Also, remember, that as per the responses to our questions, the categories (Finance, Logistics, etc.) do not have sub-categories. In this aspect, we are OK. Using this principle about repeating attributes, we find that Departments, rather than Employees, need to be the main categories. Hence, we categorise our PW site content by doing the following. Create a template for each Department. Hence, we have a template called Finance, Logistics, etc. Add the fields needed to those templates. This could be a text field for holding Employee phone numbers, email field for email, title field for their names, etc. Create top level pages for each Department and assign to them their respective templates. Give them appropriate titles, e.g., Finance, Media, etc. Create a page for each employee as a child page of the Department which they belong to. Give them appropriate titles, e.g. James, John, etc. We end up with a tree that looks like this: 1. Finance (ex. main category) a. James (ex. item of interest) b. John c. Shah d. Anne 2. Logistics (ex. main category) a. Ahmed b. Matthew c. Robert d. Cynthia 3. Media a. Barbara b. Jason c. Danita 4. Human Resources a. Michael b. Pedro c. Sally 5. Technician a. Mary b. Oswald c. Dmitri d. Osiris Since an employee can only belong to one Department, our work here is done. We can then use PW variables, e.g. $page->find, $pages->find with the appropriate selectors to find employees within a Department. This is a very basic example, of course, but you get the idea. You have the choice of creating one template file for each category template as well. I prefer the method of using one main template file (see this thread). You could do that and have all Departments use different templates but a single template file. In the template file you can include code to pull in, for example, the file “technician.inc” to display the relevant content when pages using the template “Technician” are viewed. Example code to access and show content in Single Categories model $hr = $pages->find("template=human-resources, limit 50"); foreach ($hr as $h) { echo "{$h->title}"; } But sites do not always lend themselves to this model. Many times, items of interest will need to belong to multiple categories. 2. Simple Multiple Categories Let’s say you were building a site for cars - red cars, blue cars, 2-seaters, 5-seaters, etc. Again, we ask ourselves our questions based on our initial three questions: 1. Q: What items on my site are the main items of interest? A: Cars. 2. Q: What attributes of our items of interests are we interested in? A: Colour, Number of seats, Models, Year of manufacture, Types. (Multiple categories) 3. Do these multiple attributes have sub-attributes? A: Yes. e.g., the attribute Colour has several sub-categories - red, white, green, etc. (Multiple sub-categories) 4. Can Cars have multiple sub-attributes? A: No. e.g., a yellow car cannot be a green car. (Single sub-categories) We therefore conclude that what we need is a Simple Multiple Category model. Why? This is because, in Simple Multiple Categories, items of interest can belong to multiple parent categories. However, within those parent categories, they can only belong to one sub-category. Assuming we have the following cars, manufactured between 2005 and 2008, as items of interest: Mercedes, Volvo, Ford, Subaru, Toyota, Nissan, Peugeot, Renault, Mazda, arranging our site content to fit this model could look like the following: Items of interest = Cars Categories = Model, Year, Colour, Number of seats, Type Sub Categories = Model [Prius, etc.]; Year [2005, 2006, 2007, 2008]; Colour [Red, Silver, Black, White, Green]; Number of seats [2, 5, 7]; Types [sports, SUV, MPV]. Adopting out strategy to keep it simple and logical, if we wrote down our cars names against their attributes like this: Mercedes Model-Name: Year: 2005 Colour: Silver Seats: 2-seater Type: Sports Volvo Model-Name: Year: 2007 Colour: Green Seats: 5-seater Type: SUV Ford Model-Name: Year: 2007 Colour: Red Seats: 7-seater Type: MPV Etc We notice, again, that car attributes start repeating. In order not to repeat ourselves, we want to avoid the situation where our child pages “names” keep repeating. For instance, in the above example tree, we want to avoid repeating year, colour, etc. within the tree. Of course in the frontend our output needs to look like the above where we can list our cars and their respective attributes. We just don’t need a tree that looks like this in the backend. Since we have multiple categories and sub-categories, we need to rethink our strategy for categorising our content as illustrated below. The strategy we used in the first category model will not work well here. Hence, these repeating attributes (year, colour, etc.) need to be the main criteria for our categorisation. We need to end up with a tree that looks like this: 1. Cars a. Mercedes (ex. item of interest) b. Volvo c. Ford d. Subaru e. Toyota f. Range Rover g. Peugeot h. Renault i. Mazda 2. Model (ex. main category) a. Fiesta (ex. sub-category) b. Miata c. Impreza d. Matrix e. Prius f. E-Class g. XC-90 h. Scenic i. L322 j. 505 3. Year a. 2005 b. 2006 c. 2007 (ex. sub-category) d. 2008 4. Colour a. Red b. Silver c. Black d. White e. Green 5. Number of Seats a. 2 b. 5 c. 7 6. Type a. MPV b. Sports c. SUV d. Other At the top of the tree, we have our main items of interest, Cars. They do not have to come first on top of the tree like that but it just makes sense to have them like this. Next, we have the Cars’ categories (attributes). The main categories are parent pages. Each main category has children which act as its sub-categories (cars’ sub-attributes). For instance, the main category colour has sub-categories “red”, “green”, etc. Grouping them under their main category like this makes better sense than having them dangling all over the tree as parent pages themselves. Now that we know what we want to achieve, the next question is how do we go about relating our categories and sub-categories to our main items of interest, i.e., cars? Fields come to mind. OK, yes, but what about the sub-categories (2006, red, 5-seater, etc.)? Surely, we can’t keep typing those in text fields! Of course not; this is PW. We like to simplify tasks as much as we can. What we need is a special type of field. Page Reference Fields or Page Fieldtypes add the ability to reference other pages, either single or multiple pages, within a page. For instance, we could have a Page Reference Field in the template that our Car pages use. Let’s call this “car-template”. When viewing Car pages, we would have the ability to select other pages on our site that we wish to reference, for instance, because they are related to the page we are viewing. In other cases, we could also wish to reference other pages that contain attributes/values of the page we are viewing. This is the situation with our Cars example above. Hence, the sub-categories/sub-attributes for our Cars will be pulled into our car pages using Page Reference Fields. There are two types of Page Reference Fields; single page and multiple pages. What each do is obvious from their names. Single Page Reference Fields will only reference one page at a time. Multiple Page Reference Fields will reference multiple pages. OK, let’s go back to the issue at hand. We need to categorise Cars by various attributes. Do we need to reference the main categories (Year, Type, etc.) in our Car pages? In fact, we don’t. What we need to reference are the sub-categories, i.e. 2005, red, SUV, etc. These will provide the actual attributes regarding the parent attribute of the Cars. We have said we do not wish to type these sub-categories/attributes all the time hence we use Page Reference Fields. Which type of Page Reference Field should we use? Remember that our Cars can have only one sub-category/sub-attribute. That’s our cue right there. In order to select one and only one sub-attribute per Car, we need to use the single Page Reference Field. Hence, we categorise our Cars PW site by doing the following (you may follow a different order of tasks if you wish). Create a template to be used by the Car pages. Give it a name such as car-template Create a page for each of your cars and make them use the car-template Create one template to be used by all the main attribute/categories and their children (the sub-categories). We do not need a template for each of the categories/sub-categories. I name my template “car-attributes” Of course you can name yours differently if you wish. Add the fields needed to this template. You don’t need anything other than a title field for each actually. Create top level pages for each main category and assign to them the template car-attributes. As before, give your pages meaningful titles. Do the same respectively for their child pages. E.g., you should have the following child pages under the parent “Year” - 2005, 2006, 2007 and 2008. Create the Page Reference Fields for each of your main categories/parent attributes. Using our example, you should end up with 5 Page Reference Fields (model, year, colour, seats and type). Each of these should be single Page Reference Fields. It’s a good idea, under the BASICS settings while editing the fields, to include some Description text to, include additional info about the field, e.g. instructions. In addition, you don’t want any page that doesn't belong to a particular attribute to be selectable using any of the Page Reference Fields. For instance, when referencing the year a car was manufactured, we want to be able to only select children of the page Year since that is where the year sub-categories are. We do not want to be able to select children of Colour (red, green, etc.) as the year a car was manufactured! How do we go about this? PW makes this very easy. Once you have created your Page Reference Fields, while still in the editing field mode, look under the settings INPUT. The fourth option down that page is “Selectable Pages”. Its first child option is “Parent of selectable page(s)”. Where it says “Select the parent of the pages that are selectable” click on change to change the parent. By now you know where I am going with this. For the Page Reference Field named Year, choose the page “Year” as the parent whose children will be selectable when using that Page Reference Field to select pages. Similarly, do this for the remaining 4 Page Reference Fields. Note that under this field settings INPUT you can change how you want your pages to be selectable. Be careful that you only select the types that match single Page Reference Fields, i.e. the ones WITHOUT *. For single Page Reference Fields, you have the choices:Select - a drop down select Radio buttons PageListSelect Now edit the car-template to add all 5 of your Car Page Reference Fields. We are now ready to roll. Go ahead and edit your Car pages. In each of them you will see your 5 Page Reference Fields. If you followed the instructions correctly, each of them should only have the relevant child pages/sub-attributes as selectable. Do your edits - select year when car was manufactured, its colour, type, number of seats, etc. and hit Save. By the way, note that Page Reference Fields give you access to all the fields and properties of the page being referenced! You have access to the referenced page’s title, name, path, children, template name, page reference fields, etc. This is really useful when creating complex sites. I call it going down the rabbit hole! These properties of the referenced page are available to you on request. It does mean that you will have to specifically echo out the property you want from that page. Page Reference Fields are echoed out like any other field. Example code to access and show content in Simple Multiple Categories model $cars = $pages->find("template=car-template, limit=10, colour=red, year=2006, seats=5"); foreach ($cars as $car) { echo $car->title; echo $car->year; echo $car->colour; } I have made the above verbose so you can easily follow what I'm trying to achieve. The above code will find 10 red 5-seater cars manufactured in 2006. Remember, colour, year and seats are the names of your custom Page Reference Fields that you created earlier. Some sites will have content that belong to multiple categories and multiple sub-categories. We address this below. 3. Complex Multiple Categories Suppose you are developing a site for a school. The school has teachers (duh!) some of whom teach more than one subject. Besides their classroom duties, some teachers are active in various clubs. On the administration side, some teachers are involved in various committees. You know the drill by now. Let’s deal with our basic questions. 1. Q: What items on my site are the main items of interest? A: Teachers. 2. Q: What attributes of our items of interest are we interested in? A: Subjects, Administration, Clubs (Multiple categories) 3. Do these multiple attributes have sub-attributes? A: Yes. e.g., the attribute Subjects has several sub-categories - History, Maths, Chemistry, Physics, Geography, English, etc. (Multiple sub-categories) 4. Can Teachers have multiple sub-attributes? A: Yes. e.g., a Teacher who teaches both maths and chemistry (Multiple sub-categories) Apart from the response to the final question, the other responses are identical to our previous model, i.e. the Simple Multiple Categories. We already know how to deal with multiple categories so we’ll skip some of the steps we followed in the previous example. Since our items of interest (Teachers) can belong to more than one sub-category, we conclude that what we need is a Complex Multiple Category model. In Complex Multiple Categories, items of interest can belong to multiple parent categories and multiple sub-categories both within and without main/parent categories. By now we should know what will be the main criteria for our categorisation. We need to end up with a tree that looks like this: 1. Teachers a. Mr Smith (ex. item of interest) b. Mrs Wesley c. Ms Rodriguez d. Mr Peres e. Mr Jane f. Mrs Potter g. Ms Graham h. Mrs Basket i. Dr Cooper 2. Subjects (ex. main category) a. History (ex. sub-category) b. Maths c. English d. Physics e. Chemistry f. Geography g. Religion h. Biology i. French j. Music 3. Clubs a. Basketball b. Debate c. Football d. Scouts e. Sailing f. Writing 4. Administration a. Discipline b. Counselling c. Exams board d. Public relations e. Education We are ready to build our site. Which type of Page Reference Field should we use? Remember that our Teachers can teach more than one subject and can be involved in various sub-category activities. That’s our cue right there. In order to select multiple attributes/categories, we of course go for the multiple Page Reference Field. Similar to the previous example, create necessary templates and fields for the site. For our multiple Page Reference Fields, remember to select the correct input field types. These should match multiple Page Reference Fields and are marked with *. For multiple Page Reference Fields, the available choices are: Select Multiple* AsmSelect* Checkboxes* PageListSelectMultiple* PageAutoComplete* Remember to add the multiple Page Reference Fields to the Teachers template. Go ahead and test different selectors, e.g. find Teachers that teach Maths and Chemistry and are involved in the Writing club. Whether you get results or not depends on whether there is actually that combination. An important point to remember is that your multiple Page Reference Fields will return an array of pages. You will need to traverse them using foreach (or similar). Example code Complex Multiple Categories model Find the subjects taught by the Teacher whose page we are currently viewing. You can use if statements to only show results if a result is found. In this case, of course we expect a result to be found; if a Teacher doesn't teach any subject, he/she has no business teaching! subjects is the name of one of your custom Multiple Page Reference Fields. echo "<ul>"; foreach ($page->subjects as $x) { echo "<li>{$x->title}</li>"; } echo "</ul>"; There will be situations where you will need to use both Single and Multiple Page Reference Fields (independently, of course). For instance, in our Teachers example, we might be interested in the Gender of the Teacher. That would require a Single Page Reference Field. Summary What we have learnt: Categorising our site content need not be a nightmare if we carefully think it through. Of course not all sites will fit neatly into the 3 models discussed. By providing answers to a few simple key questions, we will be able to quickly arrive at a decision on how to categorise our content. There are at least 3 models we can adopt to categorise our content - single category; simple multiple category; and complex multiple category. In the latter two models, we make full use of PW’s powerful Page Reference Fields to mimic a relational database enabling us to roll out complex sites fast and easy. Useful links: http://processwire.com/talk/topic/3553-handling-categories-on-a-product-catalogue/ http://processwire.com/videos/create-new-page-references/ http://processwire.com/videos/page-fieldtype/ http://processwire.com/talk/topic/1041-raydale-multimedia-a-case-study/ http://processwire.com/talk/topic/683-page-content-within-another-page/ http://processwire.com/talk/topic/2780-displaying-products-category-wise/ http://processwire.com/talk/topic/1916-another-categories-question/ http://processwire.com/talk/topic/2802-how-would-you-build-a-daily-newspaper/ http://processwire.com/talk/topic/2519-nested-categories/ http://processwire.com/talk/topic/71-categorizingtagging-content/ http://processwire.com/talk/topic/2309-best-way-to-organize-categories-in-this-case/ http://processwire.com/talk/topic/2200-related-pages/ http://processwire.com/talk/topic/64-how-do-you-call-data-from-a-page-or-pages-into-another-page/1 point
-
Hey folks, Thanks to your help: my first project is done - and running fine on an Apache (local) server. I've already checked the requirements and set up a new database on the webserver. Now, how do I transfer my project to a webspace? Reinstall Processwire there and then add templates or ... ? I'm a bit stuck, I have to admit ... So what would be the easiest way to get my site online for testing? thanks in advance ashrai1 point
-
I found (after 2-3 Projects using PW) that it's a good technique to use templates in a way I think hasn't been thought of yet really by some. (Although the CMS we use at work for year, works this way.) I'm sure I'm maybe wrong and someone else is already doing something similar. But I wanted to share this for everybody, just to show alternative way of using the brillant system that PW is. Delegate Template approach I tend to do a setup like this: - I create a main.php with the main html markup, no includes. So the whole html structure is there. - I then create page templates in PW without a file associated. I just name them let's say: basic-page, blog-entry, news-entry... but there's no basic-page.php actually. - Then after creating the template I make it use the "main" as alternative under "Advanced" settings tab. So it's using the main.php as the template file. - This allows to use all templates having the same php master template "main.php" - Then I create a folder and call it something like "/site/templates/view/", in which I create the inc files for the different template types. So there would be a basic-page.inc, blog-entry.inc ... - Then in the main.php template file I use following code to delegate what .inc should be included depending on the name of the template the page requested has. Using the TemplateFile functions you can use the render method, and assign variables to give to the inc explicitly, or you could also use just regular php include() technic. <?php /* * template views depending on template name * using TemplateFile method of PW */ // delegate render view template file // all page templates use "main.php" as alternative template file if( $page->template ) { $t = new TemplateFile($config->paths->templates . "view/{$page->template}.inc"); //$t->set("arr1", $somevar); echo $t->render(); } <?php /* * template views depending on template name * using regular php include */ if( $page->template ) { include($config->paths->templates . "view/{$page->template}.inc"); } I chosen this approach mainly because I hate splitting up the "main" template with head.inc and foot.inc etc. although I was also using this quite a lot, I like the delegate approach better. Having only one main.php which contains the complete html structure makes it easier for me to see/control whats going on. Hope this will be useful to someone. Cheers1 point
-
This is a quick tip for my front-end developer peeps and other wanna-be-coders like me....I have been seeing this "strange" <?php endif; ?> code in some code here in the forums and in some template files. <?php if ($a == 5): ?> <p>A is equal to 5</p> <?php endif; ?> //In the above example, the HTML block "A is equal to 5" is nested within an if statement written in the alternative syntax. The HTML block would be displayed only if $a is equal to 5. //The alternative syntax applies to else and elseif as well. I have largely ignored it until I read about it more and wow! I wish I knew about it earlier. Anyway, you might want to read up about it. In a nutshell, it makes mixing PHP and HTML much more friendly in some cases. It helps avoid echoing out a lot of HTML but instead gives you flexibility to just type them out normally... A few resources to help you understand the alternative syntax. http://php.net/manual/en/control-structures.alternative-syntax.php http://www.brian2000.com/php/understanding-alternative-syntax-for-control-structures-in-php/ http://www.stoimen.com/blog/2010/03/10/php-if-else-endif-statements/ http://www.trans4mind.com/personal_development/phpTutorial/controlStructures.htm http://stackoverflow.com/questions/564130/difference-between-if-and-if-endif http://stackoverflow.com/questions/6023418/when-do-i-use-if-endif-versus-if http://www.trans4mind.com/personal_development/phpTutorial/controlStructures.htm1 point
-
Yes, explanation accepted . OK, see above how the selector is formed to find the search results; there you're searching for employees under the chosen location. And now you need to search for department/year under current location (and list those as options in the dropdown). Anything familiar there? The biggest difference - the only one you haven't solved already - is chosen vs. current location. You do know about $page variable holding the page object of the current page, right? So what you need is a way to use the $page variable in your selector somehow. Currently you're probably using something like $pages->find('template=department') to list options, correct? Now you need to add another part to that selector constraining the departments to your current location. As the same search form is used elsewhere as well (front page at least) you'd want that location constraint only when you actually are on a location page. Something like this should do the trick: $departmentSelector = 'template=department'; # variable holding a page object in a string enclosed in double quotes is interpolated to id of the page if($page->template == 'location') $departmentSelector .= ", parent=$page"; $departments = $pages->find($departmentSelector); And same for years. Got it?1 point
-
I was made aware of this by a friend in security, just in case your hosting fits this description https://isc.sans.edu/diary/Plesk+0-day+Real+or+not+/159501 point
-
Here, as a starting point: https://github.com/christophlieck/PW-Snippets.git I have Intype on my watchlist as I feel unhappy with the development speed of ST 2/3 and pricing policy. Does anyone has given it a try already and can share his/her experience?1 point
-
I didn't realize before that PageLinkAbstractor doesn't work in this scenario, so will have to put some time into correcting that. But for now I think the best bet is to just search/replace your DB dump when migrating it from dev to live (and this is what I do regardless).1 point
-
And worth mentioning <?php foreach($pages as $page): ?> // do something here in mixed html & php // probably a few lines of it <?php endforeach; ?> It makes code much more readable, but you would expect to use this most in output templates, where there is a lot of html going on.1 point
-
OK. Your statement confused me...Of course you meant in this particular case Gnuey needed to do the renaming1 point
-
There was a typo here (you don't need that second dollar sign), so it should be: echo "Amount {$grant->amount_approved}"; As for the date, I think you need this instead: $myDate = $grant->getUnformatted("cheque_date"); $myDate = date("Y-m-d H:i", $myDate); echo "<p>Date granted: $myDate</p>";1 point
-
In order to start building websites with ProcessWire you don't have to know much PHP, just basics. Check out this thread. To format your date do this (you may want to change format - check out the link posted above): $myDate = date("Y-m-d H:i", $grant->cheque_date); echo "<p>Date granted: $myDate</p>"; You can check the field name in setup/fields to make sure they are ok - field names are not visible on your screenshot, only their titles. Also you can install this module to help you navigate from inputfields to corresponding fields quickly http://modules.processwire.com/modules/helper-field-links/1 point
-
Comment from Ryan that I think is appropriate: http://processwire.com/talk/topic/1176-hiding-uneditable-pages-from-users/?p=104361 point
-
Scale isn't a problem. Pw does paginate the pages after an amount of pages (50 by default). You can change this in the Page List module. No nightmare at all...1 point
-
Yes exactly. The pseudo user page can be anywhere and you could add children pages for all kind of things. And only a couple field like prename lastname to the real user template. You dont have to it's up to you. I also think you're creative enough to already see what is possible it's more the code you worry. Let me say in PW it'S REALLY no rocket sience compared to other systems and just keep trying to go with the simple API. Once you learned that you'll do anything with ease. Maybe I'm too modest but I'm also not a hardcore programmer but a design guy, and learned a whole lot with PW. Of course I got some background and did a lot with php. You're in good hands with the guys here.1 point
-
Ahhhh...I think this is because I'm using a folder (leftover from MODx) called "/assets" and PW has its own folder called "/site/assets". I renamed it to "/test" and it seemed to work OK. But because other images in the site are already using "/assets/images/blah.jpg" I guess I need to either symlink "/assets" to "/test" or search & replace in the database. OK, I think this might be fixed now. Thanks!1 point
-
I'll hazard a guess. I know PW prevents u from running php scripts directly in site/templates/ and some other places. Maybe also in assets folder? http://processwire.com/talk/topic/407-processing-contact-forms/?p=31061 point
-
Try removing baseUrl from your renderPager options array. If you set it to an empty string, as you currently have, it will ignore your URL segments. The documentation says the default is "blank", which could lead one to believe it means an empty string or null. I quickly peeked under the hood of the module, and it seems the default is not really empty/blank; rather, it seems to detect the full current page URL including segements, so you can just omit it completely in the options.1 point
-
It is as simple as this: <?php /** * Page template * */ include("./head.inc"); echo $page->body; echo $page->your_field_name; echo $page->another_field_name; echo $page->third_field_name; include("./foot.inc"); But do finish to those tutorials. You easily get a grasp of all the possibilities.1 point
-
You may want to set $select->required = true; so that there isn't an unselected option. Rather than removing all the options and adding them back, you should just be able to set the 'value' attribute to whatever you want the selected option to be: $select->attr('value', $selected_page_id); Note that "kg" is your label rather than the value, so you are going to know the ID of the page with name/title "kg" in order to make it selected.1 point
-
$item = $this->pages->get("statistics.stat_type=ITEM" . $i . ", game_id=$match->game_id, limit=1"); if($item->id){ $item1 = $item->first(); $this->html .= $item1->value; } $item is the page containing the repeater, not the repeater itself. Your value field is in the pages inside the repeater: $statistics = $item->statistics; foreach ($statistics as $stat) { // Value is a field in the repeater echo $stat->value; }1 point
-
But it (actually) do not work with PW 2.3 when you have MultiLanguage enabled. If you do not use MultiLanguage, it still works pretty fine!1 point
-
Which you can download here http://mods.pw/Z1 point
-
You create a site profile with profile exporter.1 point
-
book ams.i writng also good for all in.deph and comperhansive.topkicks for.it begnner and.master pw user1 point
-
Nice! It didn't know you can do that. We definately need a markup guide and templating guide with different approaches. There are some great posts hidden in this forum. If only I could find some time to collect all that information.1 point
-
Notepad++ anyone? I don't know if it counts as an IDE but it works fine for me with its plethora of plugins although I may need something more IDE-ish in the future...1 point
-
Hi onjegolders, You know I thought I'd tried that, but I did it again and it worked. I just uncommented that first line: RewriteBase / So I'm very pleased, thank you so much!1 point
-
In most cases there's not a lot to it: 1- Transfer all files from your local environment to the desired location on the 'webspace'. You would probably do this via ftp. I assume you know how to do this. Make sure to also transfer the .htaccess file, because sometimes this is hidden. 2- Make a database export of your local database (via PHPMyadmin or other tools). Do a database import of this stuff in your new database on the webserver. 3- Check the database credentials in the file site/config.php on the server (near the bottom of the files). If needed adjust to the online environment and databasename, login etc. That's it. All things should work now. There are a couple of things to keep in mind though: - if locally you developed the site in a subdirectory (e.g. localhost/test/) and you move the site to the webroot or subdomain, most probably links to files and images inside richtextarea's stop working. You would have to adjust the paths in your database export before importing it. - sometimes you need to change directory permissions for certain dirs to write permissions.1 point
-
Two possible ways to do this: 1) Use the Site Profile Exporter Just install it on the local installation, run it and then install Processwire with the new created setup on the new host. Don't forget to copy your templates. 2) Manual by exporting the database and import it again. Copy your local files to the Webserver. Don't forget, that some folders need write permissions for Processwire to work(/site/assets /site/config.php, ...). You should have a phpmyadmin installed on your local machine. Use it, to export the database. Then import the database on your server(again, you could use phpmyAdmin). Check, if the database connection is possible with the login details saved in the /site/config.php. You might also have to delete the session and cache files in the /site/assets directory. By reinstalling a fresh PW, you would have to create the fields and template and page stuff again. You can copy the templates but the content will be missing .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
-
Just wanted to add a couple more notes to this: Using a 'summary' field I think it's more common on this type of page that you would display a summary of the news story rather than the whole news story... and then link to the full news story. To display a summary, you could have a separate 'summary' field in your template, which would be just a regular textarea field where you would have a 1-2 sentence summary of the article. And you would display this 'summary' field rather than 'body' field in the news_index template. If you wanted to autogenerate a summary from the 'body' field (rather than creating a new 'summary' field), you could just grab the first sentence or paragraph of the body and use that as your summary. This is how I usually do something like that: <?php // make our own summary from the beginning of the body copy // grab the first 255 characters $summary = substr($story->body, 0, 255); // truncate it to the last period if possible if(($pos = strrpos($summary, ".")) !== false) { $summary = substr($summary, 0, $pos); } That's a really simple example, and you may want to go further to make sure you are really at the end of a sentence and not at an abbreviation like "Mr." In the example that Moondawgy posted, it makes sense to autogenerate a summary (if he needed it). But in other cases, the 'body' can be quite long (and take up a lot of memory), and it makes more sense to maintain a separate summary field if you have to keep a lot of pages loaded at once. This is really only an issue once you get into hundreds of pages loaded at a time. It's not an issue in these examples, but I just wanted to point it out. Autojoin Using the 'autojoin' optimization can increase performance on fields that get used a lot. Not using it can reduce the page's memory footprint. What is more desirable in each instance depends on your situation. In this news section example, the date and body fields would benefit from having 'autojoin' turned ON. See this page for an explanation: http://processwire.com/talk/index.php/topic,32.0.html1 point
-
It looks like your news page example displays 4 stories per page, and it displays the full bodycopy for each of them. Assuming the same structure and fields of the pages you posted, you'll need to create two templates: 1. news_index.php 2. news_story.php You can call those templates above whatever you want, but just note that we'll use a separate template for news stories and news items. Your news_story template will contain the following fields: 1. title 2. date (date) 3. body (textarea) And it's markup might look like this (excluding headers, footers, etc.): /site/templates/news_story.php: <h1><?=$page->title?></h1> <div id='bodycopy'> <?=$page->body?> </div> Not much action there. Your news_index is where most of the action will happen. In Admin > Setup > Templates > news_index > Advanced, check the box for "Page Numbers" to turn them on. Save. Here is what the code in your news_index.php might look like: /site/templates/news_index.php: <h1><?=$page->title?></h1> <?php // start the news stories list echo "<ul>"; // get the stories for this page $stories = $page->children("limit=4, sort=-date"); // note if you set the stories to sort by date descending on the /news/ page // in the admin, then you can omit the "sort=-date" above. // cycle through each story and print it in a <li> foreach($stories as $story) { echo " <li><a href='{$story->url}'>{$story->title}</a> <p><strong>Date:</strong> {$story->date}</p> {$story->body} </li> "; } echo "</ul>"; // get values for our placemarker headline $start = $stories->getStart(); $end = $start + count($stories); $total = $stories->getTotal(); $num = $input->pageNum; $lastNum = ceil($total / $stories->getLimit()); // output the placemarker headline echo "<h4>Showing $start - $end of $total Article/s | Page $num of $lastNum</h4>"; // output pagination links echo $stories->renderPager(); Lets say that you don't want the pagination links that renderPager produces, you can always modify it's output by passing params to it. See the page about pagination here: http://processwire.com/api/modules/markup-pager-nav/ But if you want your literal "previous" and "next" buttons like on your site, then you'll want to insert your own logic to do that. Something like the next example. Note I'm reusing the vars I set in the previous example here for brevity. This snippet would replace the renderPager() method in the previous example. <?php // make the previous link if($num > 2) $prevLink = "./page" . ($num-1); else if($num == 2) $hrefLink = "./"; // page 1 else $prevLink = "./page" . $lastNum; // last page // make the next link if($num >= $lastNum) $nextLink = "./"; // page 1 else $nextLink = "./page" . ($num+1); // output the prev/next links: echo "<p><a href='$prevLink'>Previous</a> <a href='{$nextLink}'>Next</a></p>"; Disclaimer: the examples on this page are just written off the top of my head and are not actually tested examples. You'll likely have to tweak them. In particular, you may have to add or subtract 1 in a few places to get the right numbers. If you end up adapting this, please let me know of any errors I have here so that I can correct them.1 point