Leaderboard
Popular Content
Showing content with the highest reputation on 05/30/2014 in all areas
-
Continuing from my previous post in this thread about some selector enhancements available on the dev branch, we've got a couple more advanced options for use in selectors in case anyone is interested: OR-groups These let you specify multiple expressions and only one of them has to match in order for the selector to match. It's a way of saying "either this has to match OR that has to match". This is useful because selectors always assumed AND – meaning everything has to match. While you have always been able to use the pipe "|" to specify ORs for fields or values or both, the scope of it was just that field=value statement only. Now we have something new called OR-groups. These let you create multiple selector groups and only one of them has to match. You can specify OR-groups by surrounding selectors in parenthesis. An example demonstrates it best. Lets say that we wanted to find all "product" pages that were in stock, and either in a featured date range, or had a highlighted checkbox checked. Previously we would do like this with two separate find operations: $items = $pages->find("template=product, stock>0, featured_from<=today, featured_to>=today"); $items->add($pages->find("template=product, stock>0, highlighted=1")); Now we can do it in one find operation: $items = $pages->find("template=product, stock>0, (featured_from<=today, featured_to>=today), (highlighted=1)"); Above are two selectors surrounded in parenthesis. Only one of them has to match. You can specify as many of them as you want. This type of OR expression is something you couldn't previously do with selectors. Think of the parenthesis as a way of saying "this is optional". But of course, at least one of your parenthesized selectors has to match in order for the full selector to match. I'm guessing the above usage probably covers 99% of the situations where you might need it. But lets say that you want to have different combinations of OR expressions. You can create named groups that OR with each-other by specifying: foo=(selector1), bar=(selector2), foo=(selector3), bar=(selector4) In the above you'd replace "foo" and "bar" with names of your choice. And you'd replace the "selector" with any selector strings. Those foo/bar names aren't referring to fields, instead they are just named groups that you can name however you want. In that selector, at least one of the "foo" named selectors would have to match, and at least one of the "bar" named selectors would have to match. If you didn't use the foo/bar named groups here (but still used the parenthesis), then only one of the 4 selectors would be required to match. Sub-selectors Some of you are already familiar with these because it was committed to the dev branch a couple weeks ago (and I think may have been outlined elsewhere in the forums). Sub-selectors let you put a selector within a selector, enabling you to perform more complex matches that used to require you to use separate API calls. These can be used on the 'id' property of any field that maps to a page. The 'id' property is assumed when referring to a page reference or a parent, so it's not necessary to specify it unless you want to, i.e. "field" and "field.id" mean the same thing in this case. Sub-selectors are specified between [square brackets]. For example, lets say we are matching products and our product template has a "company" page field. Each company also has it's own page field where all the company locations are identified. Lets say we want to find all products that are made by a company that has more than 5 locations and at least one of those locations has "Finland" in the title. Previously we would have had to do it like this: $companies = $pages->find("template=company, locations>5, locations.title%=Finland"); $items = $pages->find("template=product, company=$companies"); That's easy enough. But now it's even simpler, as you can do it in one operation: $items = $pages->find("template=product, company=[locations>5, locations.title%=Finland]"); When you've got a "field=[value]" selector, any properties you refer to in "[value]" assume the "field", so "locations" above is referring to a property of the "company" field.9 points
-
This Module didn't have it's real own thread, now it has Markup RSS Enhanced This Module is the enhanced version of Ryan's Markup RSS Module and is completely compatible with it. In addition, this enhanced module supports the usage of enclosures a way of attaching multimedia content to RSS feeds. Give the RSS enhanced module a PageArray of pages and it will render a RSS feed from it. The Module should be used directly from your template file. In the examples the $rss variable is used for as instance of the module. $rss = $modules->get("MarkupRSSEnhanced"); Basic usage In case you only need 1 feed for your site, you need to setup the defaults in the Modules config. then you can use the code below. $items = $pages->find("limit=10, sort=-modified"); // $items, PageArray of Pages $rss = $modules->get("MarkupRSSEnhanced"); // load the module $rss->render($items); // render the feed Setup channel elements The channel element describes the RSS feed. There are 3 required channel elements: title $rss->title link $rss->url description $rss->description $rss->title = ''; // (string) Title of the feed. $rss->url = ''; // (string) URL of the website this feed lives. Example: http://www.your-domain.com/ $rss->description = ''; // (string) Phrase or sentence describing the channel. $rss->copyright = ''; // (string) Copyright notice for content in the channel. $rss->ttl = ''; // (string/integer) Number of minutes that how long it can be cached. Setup item elements Every page from the PageArray use the item element. $rss->itemTitleField = ''; // Fieldname to get value from $rss->itemDescriptionField = ''; // Fieldname to get value from $rss->itemDescriptionLength = ''; // Default 1024 $rss->itemEnclosureField = ''; // Fieldname to get file/image from $rss->itemDateField = ''; // Fieldname to get data from $rss->itemLinkField = ''; // Fieldname to get URL from or don't set to use $page->httpUrl $rss->itemAuthorField = ''; // If email address is used, itemAuthorElement should be set to author $rss->itemAuthorElement = 'dc:creator' // may be 'dc:creator' or 'author' Item element enclosure RSS enclosures are a way of attaching multimedia content to RSS feeds. All files with proper mime types are supported. If you asign an image field to the itemEnclosureField there are 3 extra options you could set. width The width of the image. height The height of the image. boundingbox Checking boundingbox will scale the image so that the whole image will fit in the specified width & height. This prevents cropping the image $rss->boundingbox = 1 // (integer) 1 or 0, on or off $rss->width = 400; // (integer) Max width of the image, 0 for proportional $rss->height = 300; // (integer) Max height of the image, 0 for proportional Prettify the feed Prettifying the feed is not supported by all clients. $rss->xsl = ''; // path to xls file $rss->css = ''; // path to css Download on GitHub View on the modules directory4 points
-
Update: Blog version 1.1 Read below before updating. For new installs, proceed as normal ------------------------------------- Changelog 1. Added new widget 'Post Author' - @adrian idea, thanks. - This widget allows you to add a post's author's biography with each post. You can add it before or after a post (or wherever you wish). See example in updated 'blog-post.php' example: $blog = $modules->get('MarkupBlog'); echo $blog->postAuthor(); - The widget can be enabled/disabled in the 'Settings' Tab of ProcessBlog. It is enabled by default - Updated the CSS to style the widget Screen 2. Made 'posts truncate length' configurable - @looeee idea, thanks. - This is for when you want to render a 'post summary' - e.g. as seen on /blog/posts/. - Was previously hard-coded to 450. Default is now 450 where no value is specified - Can be configured either on the page /blog/posts/ or 'Settings' Tab of ProcessBlog UPGRADING As I have said previously and repeat here (just in case you missed it ) a module of this kind must not be altering your data if you've already installed it or where there are potential conflicts. Therefore, to upgrade, some manual input is required. Once we hit a lock-down on new features, then this requirement will fizzle out... A. Author Widget 1. This comes with a new template without a template file. - Create a template called 'blog-widget-basic' - Give it a tag 'blog' and a label 'Blog Widget: Basic' //just for consistency with other blog templates - Add the field 'blog_summary' save. Then within the template (i.e. click on the upward arrow of the field), change the description to 'Widget Description' in the modal that opens up. Save... - Still on the edit template view, under the 'Family' tab, specify 'No' under 'May pages using this template have children?' and 'Yes' to 'Can this template be used for new pages?' Save... - Copy over the updated blog.css to /site/templates/css/ (this assumes you haven't made any custom changes to this file!!!) 2. Post Author page - Under /blog/widgets/ create a new page called 'Post Author' and assign it the the above template ('blog-widget-basic') - In the 'Widget Description' field enter a description, e.g. 'Renders Post's author biography.' Save... The Author Widget should then automatically appear under 'Settings' in the Widgets section in ProcessBlog B. Post Excerpt Length 1. Add Field to Template - In the template 'blog-posts', add the field 'blog_quantity'. Save. Change the field's description to 'Posts truncate length'. Save C. Update Blog to version 1.1 In PW, now update the module to version 1.1. This will copy over the new module files (ProcessBlog.module and MarkupBlog.module) and their related files. Note: installer will not run again! So, don't worry All done; now go write a post about how cool ProcessWire is4 points
-
Feasibly it's possible, but it's not desirable. Making wire('var') aware of context involves pre-compiling the template files to replace wire('var') with wire($this, 'var'). So it's something we do to provide backwards compatibility in template files. We won't be providing that backwards compatibility outside of template files, so people will have to use $this->var, $this->wire('var') or wire($this, 'var'); elsewhere. Also, a Static::syntax implies dealing with a framework that only has one context, which gives the appearance of a weakness that isn't there, something I think we'd want to avoid. While I'd really like to limit statics in ProcessWire as much as possible (just because they are more than often a bad programming practice), that syntax is perfectly fine for static functions where context doesn't matter or where you are passing the context into it. For example, $sanitizer functions would not need to have different behavior in different contexts. I've even seen some frameworks that do use statics for sanitization functions as well. In the end though, IMO they would still be better served by providing the same functions non-statically. The plan is that when you do this... $main = new ProcessWire('/site/'); $intranet = new ProcessWire('/site-intranet/'); ...everything in each of those two instances will be unique to those instances and whatever site files are stored in /site/ or /site-intranet/. That means that when a module is initialized, it is only initialized for the instance (whether main or intranet). When a module refers to $this->var, it's referring to API variables that are part of its instance only. This enables you to have multiple sites talking to each other. Currently this isn't possible precisely because PW uses statics for API variables behind the scenes. But the fact that we've kept that behind the scenes is a good thing because that means it doesn't matter how our API variables are stored. We can switch them to a stronger storage mechanism that would be tied to an instance. This is one reason why I deprecated the Wire::getFuel() syntax (that appeared in early versions of PW2) early-on... though you might still see it appear in a few core spots, which will need to be changed. But we've really tried to keep the public API clear of static calls so that the API would not have to change as PW continues to grow as a framework.4 points
-
There are many ways you can achieve what you're looking for (that's a strength of ProcessWire). What I normally do is use templates and roles. Say we have Group A, Group B and Group C. I create the roles and give permissions, as required. I assign users to the Groups, as needed. I create individual templates for each group (i.e. "Access-For-Group-A", "Access-For-Group-B", "Access for Group C"). Each template has Access Security enabled where access is only given to a particular group. I will then create a page and in the Settings tab, assign the template for Group A. I then will create individual pages for the other 2 groups. When doing this I always disable Guest access on the templates. These pages serve as the access point for the private data for each group. I then create sub-pages below the main group page. Those pages take their access from the parent page above it. When someone logs in, they will only see the pages that they have access to. You can even have an individual assigned to two or more roles with this scheme. There are probably better ways of doing this, however this works for me.3 points
-
yeah, i always need to nag the clients during development to keep their page names consistent with the titles.. maybe a module where you could enable a "development mode" which would prompt the user when changing title if they also want to rename (update slug)... some might even want to leave it on and then rely on the page path history to redirect..2 points
-
$page->numChildren includar.hidden y unpublishado .. alos.s u have $page->numVisibleChildren2 points
-
Exactly. so, the logic, in order of ascending precedence (i.e. 3 > 1, etc.) could probably go like this: 1. Comments are ON by default. 2. On each 'post' page (/blog/posts/my-post/), will include a checkbox: 'check to disable comments for this post'. This will ignore #1 3. On some settings page or on the 'posts' page (/blog/posts/) have a checkbox or similar that says: 'disable comments on all posts'. Ignore #1 & #2 So, #3 is 'greatest' and will ignore #1 and #2 but will NOT change their settings; checked boxes (#2) will remain checked and unchecked ones (#1) will remain unchecked but their 'directions' will be superseded by #3 . ################################# EDIT ################################# Oops warped thinking on precedence above!!! It should be the other way round! See edits below.. So, the logic, in order of ascending precedence (i.e. 3 > 1, etc.) could probably go like this: 1. Comments are enabled by default everywhere. 2. On the 'comments' page (/blog/comments/) have a select that says: 'disable comments on all posts/disable new comments on all posts'. Ignore #1 but respect individual post's settings (#3) 3. On each 'post' page (/blog/posts/my-post/), will include a select: 'disable comments on this post/disable new comments on this post/always enable comments on this post'. This will ignore #1 & 2. This is like a get() in ProcessWire: it is explicit and ignores 'hidden' status. So, #3 (individual post setting) is 'greatest' and will ignore #1 and #2. ################################# END EDIT ################################# Btw, you will notice very soon that there's very little 'do this the PW way' - in many cases, there is no PW way The system is so versatile yet powerful you will be amazed...2 points
-
Comments on/off? Been thinking about a new feature. Ability to turn on/off comments both on a per post basis and on a Blog-wide basis. So, if a post has comments turned off, the user gets the usual 'Comments not allowed for this post' or something similar. Additionally, maybe also a feature to turn-off submitting of new comments on a post when approved comments hit a certain number, say '100'. What do you guys think? Need to think a bit more about how best to implement this...2 points
-
@ivan see Macrura's post that Module is doing exactly that. ( and you should remove the if template statement ) The URL get indexed with search engines and are shared on social media & mail etc. Then changing the title will result in a 301 or in worse case a 404. There are situations where it is desirable that the url changes and thus trowing a 404, but to make this the default behaviour is bad practise in my opinion.2 points
-
I very frequently see people not finding stuff in the forums, I don't blame them, the forum search sucks bit time Then many times forum members suggest to use {searchterm} site:processwire.com/talk on Google. It's true, nine times out of ten I DON'T find what I am looking for using the standard forum search. Nine times out of ten I DO find what I am looking for using Google search. So perhaps there should be a hint on the search dialog/page to use Google instead (if that's even possible). New users would find stuff quicker, saves time for all.1 point
-
This is pretty neat: https://pushover.net/ I had been thinking it would be great to have a notification app that you could somehow use with ProcessWire and this - with a little programming - would do nicely! Notifications of new comments on your site? No problem. Built your own client support system in PW? Get push notifications of new tickets. The possibilities are endless and the price tag isn't bad - free for up to 7,500 notifications per month and only $4.99 per user for the app In fact, I already know a few clients who would love notifications of certain things to arrive this way!1 point
-
I recently completed a website that had a very large gallery requiring multiple albums (categories) and 100+ images per album with pagination. The solution I developed accomplishes this with just 2 templates (gallery-index and gallery-album) and a single multi-image field for each album, allowing for quick, mass upload of images*. One of the great things about ProcessWire is that you can custom build something like an image gallery with just the tools that the template system and API provide out of the box, without going in search of modules. Once we have the basic templates set up, we will use the excellent FancyBox jQuery script/plugin to add some slick Javascript "lightbox" functionality on top of it. The gallery will still work without FancyBox or with Javascript disabled; it will simply fall back to opening each image in a blank page. So, without further ado... 1. Create a file named gallery-index.php in your site/templates/ folder with the following code. This will be the main page of your gallery. The code simply loops through all of the photo albums that are children of your main gallery page and uses the first image in the album as the cover photo: <? include("./head.inc") ?> <? // Configure thumbnail width/height $thumbWidth = 250; $thumbHeight = 250; // Create an array of the child pages that use the gallery-album template $albums = $page->children('template=gallery-album'); ?> <h2><?= $page->title ?></h2> <div class="gallery"> <ul class="gallery-row row"> <? if(count($albums) > 0) { foreach($albums as $album) { // Grab the first image from the album and create a thumbnail of it $thumb = $album->images->first()->size($thumbWidth, $thumbHeight); ?> <li class="col span4"> <div class="gallery-album photoShadow"> <a href="<?= $album->url ?>" class="gallery-albumThumb" title="<?= $album->title ?>"> <img src="<?= $thumb->url ?>" alt="<?= $thumb->description ?>" /> <h4 class="gallery-albumTitle"><?= $album->title ?></h4> </a> </div><!-- /gallery-album --> </li><!-- /col --> <? } } ?> </ul><!-- /gallery-row --> </div><!-- /gallery --> <? include("./foot.inc") ?> 2. Add the gallery-index template in the ProcessWire admin under Setup->Templates. This template does not require any fields, although you may want to add a body field for outputting additional content to the page. 3. Create a file named gallery-album.php in your site/templates/ folder. This template will be used to both hold and display the images in your albums. Here we will be loading the fancybox plugin as well, so make sure you've downloaded it here: http://fancyapps.com/fancybox/ and uploaded the /fancybox/ folder to your site/templates/scripts/ folder. We are appending the fancybox files to the $config->scripts and $config->styles array before outputting them in our head.inc file so that we're only loading that code on the album pages. So make sure you are outputting those arrays in the <head></head> section of your head.inc file along with your other scripts & styles, like so: <? foreach($config->scripts as $file) { ?><script type="text/javascript" src="<?= $file ?>"></script> <? } ?> <? foreach($config->styles as $file) { ?><link rel="stylesheet" type="text/css" href="<?= $file ?>" /> <? } ?> Please note that you will also have to include jQuery in your head.inc file before your other scripts, if you're not already including it. So here is our gallery-album.php. Notice also that we are calling the FancyBox script and customizing some of its options at the bottom of the file: <? $config->styles->append($config->urls->templates . "scripts/fancybox/jquery.fancybox.css"); $config->styles->append($config->urls->templates . "scripts/fancybox/helpers/jquery.fancybox-thumbs.css?v=1.0.7"); $config->scripts->append($config->urls->templates . "scripts/fancybox/jquery.fancybox.pack.js"); $config->scripts->append($config->urls->templates . "scripts/fancybox/helpers/jquery.fancybox-thumbs.js?v=1.0.7"); // Configure thumbnail width/height & number of photos to display per page $thumbWidth = 150; $thumbHeight = 150; $imagesPerPage = 32; // Make ProcessWire pagination work on the images field (see for full explanation of this) $start = ($input->pageNum - 1) * $imagesPerPage; $total = count($page->images); $images = $page->images->slice($start, $imagesPerPage); // Create a new pageArray to give MarkupPagerNav what it needs $a = new PageArray(); // Add in some generic placeholder pages foreach($images as $unused) $a->add(new Page()); // Tell the PageArray some details it needs for pagination $a->setTotal($total); $a->setLimit($imagesPerPage); $a->setStart($start); include("./head.inc") ?> <?= $a->renderPager() ?> <div class="upOneLevel"><a href="<?= $page->parent->url ?>">← Albums</a></div> <h2><?= $page->title ?></h2> <div class="album"> <ul class="album-row row"> <? if(count($images) > 0) { foreach($images as $image) { $thumb = $image->size($thumbWidth, $thumbHeight); ?> <li class="album-photo darkenOnHover col span3"> <a href="<?= $image->url ?>" rel="fancybox-gallery" class="fancybox" title="<?= $image->description ?>"> <img src="<?= $thumb->url ?>" alt="<?= $thumb->description ?>" /> <!-- Uncomment this line if you want descriptions under images <p class="album-photoDescription"><?= $image->description ?></p>--> </a> </li> <? } } ?> </ul><!-- /album-row --> </div><!-- /album --> <div class="group"><?= $a->renderPager() ?></div> <script type="text/javascript"> $(document).ready(function() { $(".fancybox").fancybox({ prevEffect : 'elastic', nextEffect : 'elastic', loop : false, mouseWheel: true, helpers : { title : { type: 'outside' }, thumbs : { width : 100, height : 60 } } }); }); </script> <? include("./foot.inc") ?> 4. As we did before, add the gallery-album template in the ProcessWire admin. Assign the images field to it, and go into the URLs tab and make sure Page Numbers are allowed. 5. Create a page in the ProcessWire admin for the gallery index using the gallery-index template. You'll probably want to give it a title like "Gallery". 6. Underneath your Gallery page, create child pages that use the gallery-album template, one page for each album you want to create. Name them however you'd like. 7. Go into each album page you created and populate the Images field with your images. Just drag-and-drop. It's as simple as that! If you want to add a description for each image, you can also add it here. If you have more than 32 images (or whatever value you set the $imagesPerPage variable to), the pagination will kick in and split the album into multiple pages. 8. Finally, add in the CSS. The CSS is really up to you, but I'm including a good starting point below. This includes a handy responsive grid system I built for my sites, as well as a .photoShadow class I developed which gives your album covers a cool 3D Polaroid look using pure CSS. /********* Helper Classes **********/ .row:after, .group:after { content: ""; display: block; height: 0; clear: both; visibility: hidden; } .row { ; /* Remove left gutter */ margin-top: 0; margin-right: 0; margin-bottom: 0; padding: 0; zoom: 1; /* IE7 */ position: relative; } .col { display: block; float:left; margin-left: 2%; /* Gutter size */ margin-top: 0; margin-right: 0; margin-bottom: 0; padding: 0; zoom: 1; width: 95.99999999996%; } .span1 {width: 6.33333333333%;} .span2 {width: 14.66666666666%;} .span3 {width: 22.99999999999%;} .span4 {width: 31.33333333332%;} .span5 {width: 39.66666666665%;} .span6 {width: 47.99999999998%;} .span7 {width: 56.33333333331%;} .span8 {width: 64.66666666664%;} .span9 {width: 72.99999999997%;} .span10 {width: 81.3333333333%;} .span11 {width: 89.66666666663%;} .span12 {width: 97.99999999996%;} .photoShadow { position: relative; border: 5px solid #fff; background: #fff; -moz-box-shadow: 0px 0px 2px #ccc; -o-box-shadow: 0px 0px 2px #ccc; -webkit-box-shadow: 0px 0px 2px #ccc; -ms-box-shadow: 0px 0px 2px #ccc; box-shadow: 0px 0px 2px #ccc; } .photoShadow:before { z-index: -1; content: ""; display: block; position: absolute; width: 104%; height: 16px; bottom: -5%; left: -2%; overflow: hidden; border-radius: 50% 50% 0 0; box-shadow: inset 0px 8px 5px #999; } .darkenOnHover { opacity: .8; -webkit-transition: opacity .2s; -moz-transition: opacity .2s; -ms-transition: opacity .2s; -o-transition: opacity .2s; transition: opacity .2s; } .darkenOnHover:hover { opacity: 1; -webkit-transition: opacity .1s; -moz-transition: opacity .1s; -ms-transition: opacity .1s; -o-transition: opacity .1s; transition: opacity .1s; } /********** Blocks **********/ .gallery { } .gallery-album a:hover { text-decoration: none; } .gallery-albumTitle { font-size: 1.1em; text-align: center; margin: .2em ; } .gallery-album { -webkit-transition: all .2s; -moz-transition: all .2s; -ms-transition: all .2s; -o-transition: all .2s; transition: all .2s; } .gallery-album:hover { -webkit-transition: all .2s; -moz-transition: all .2s; -ms-transition: all .2s; -o-transition: all .2s; transition: all .2s; -webkit-box-shadow: 0 0 3px #555; -moz-box-shadow: 0 0 3px #555; -ms-box-shadow: 0 0 3px #555; -o-box-shadow: 0 0 3px #555; box-shadow: 0 0 3px #555; } .album-photo img { margin-bottom: 6px; border: 1px solid #ddd; } .upOneLevel { font-size: 1.1em; margin-bottom: .4em; } .upOneLevel .icon-circle-arrow-left { font-size: 1.5em; margin-right: .4em; } .upOneLevel a:hover { text-decoration: none; } .MarkupPagerNav { margin: 1em 0; font-family: Arial, sans-serif; float: right; } .MarkupPagerNav li { float: left; list-style: none; margin: 0; } .MarkupPagerNav li a, .MarkupPagerNav li.MarkupPagerNavSeparator { display: block; float: left; padding: 2px 9px; color: #fff; background: #2f4248; margin-left: 3px; font-size: 10px; font-weight: bold; text-transform: uppercase; } .MarkupPagerNav li.MarkupPagerNavOn a, .MarkupPagerNav li a:hover { color: #fff; background: #db1174; text-decoration: none; } .MarkupPagerNav li.MarkupPagerNavSeparator { display: inline; color: #777; background: #d2e4ea; padding-left: 3px; padding-right: 3px; } I think that's it! Just make sure you're including the CSS file in your head.inc inside the <head></head> tags and you should be all set. If you come across any issues trying to implement the above (or find any of it confusing) please let me know below. Everyone is coming from different backgrounds and different experience levels. And if you find this tutorial useful, please feel free to let me know as well * I should mention that although it is possible to create galleries in Processwire where each image is represented by its own page (and, as Ryan has mentioned, is often preferable since it is ultimately more scalable), sometimes the ease of using a single image field (which can upload and decompress zip files of images in mass) simply outweighs any drawbacks. If I had to create this gallery with the 1-image-per-page method, it would have taken hours to upload all of the images one-by-one without some sort of additional programming to automate the process.1 point
-
Well, I read through your code a week back but couldn't spot any obvious flaw. Now I had another look and feel like I should've seen it in the first place... Actually you did yourself in the very beginning as you had the very same problem then. You're welcome. And you should still hang on to what you had working . Here's the problem line (core of it anyway): $matches = $all->find("rental_period.date_from>=$df, rental_period.date_to<=$dt, rental_period.booked=0"); As you figured out before (and Ryan confirmed you right), this kind of selector is something to beware of. While all of the three conditions must match, they only have to match the same page (property_availability), not the same repeater item. So this would match any page that has rental_period-repeater with one item matching the condition for date_from, another item matching the condition for date_to and third item matching the condition for booked. All the conditions could have a match in the very same repeater item, but that's not required (and you'd want it to be). That's just the nature of repeaters. Nasty, I know. Take the version with working date ranges and add the capacity and region handling like you have now in the beginning. Then, instead of first finding all cottages matching region and capacity, just go for the repeater items matching the date range like before and modify the match handling like this: foreach($matches as $item) { $property = $item->getForPage(); if(!$property->viewable()) continue; // skip if property is unpublished or something // add this: skip the match if it doesn't match your region or capacity if(!$property->matches($filterSelector)) continue; // now you have the $property and the matching repeater $item $termCottages[] = $item->id; } Here $filterSelector should be empty if no region or capacity has been chosen. Or it could be something like "location=xyz, sleeps>=10" to rule out any cottages not located in xyz or having capacity of less than 10. I haven't tested it but naturally it'll work like a charm . So you were on the right track all along. Just don't go for something that's been proven faulty for this scenario before.1 point
-
I always quite like solutions that also do bandwidth checking, and other stuff, like https://github.com/adamdbradley/foresight.js (standalone) or https://github.com/teleject/hisrc (this is a jQuery plugin) If that was to be paired by some PW magic to create image variations that would be cool.1 point
-
@Horst, I was just thinking loud there, so I don't have a real test case & no js. But I have a site where I swap a transparent pixel, with no fallback if js is disabled. Nothing special there. After you posted your code I instantly thought about a module as the extra markup I don't want to type manually every time. Adaptive images must be easy to use. And should work without Javascript. After That I searched the net for the <picture> element, ( not that I'm keen with those HTML5 tags for those things ) Then the baby starts crying. Will come back to this.1 point
-
The level isn't supported as a placeholder, but you could add a hook to it and since "interation" is a updated property in the module you can use that to get the level without adding more overhead. // module load $nav = $modules->MarkupSimpleNavigation; // add hook to before parsing of the "inner_tpl" opening tag $nav->addHookBefore("getInnerStringOpen", null, function($event){ // get the current level $level = $event->object->iteration; // "<ul class='level-{level}'>" $tpl = $event->arguments("tpl"); // replace {level} with number and send tpl back to the argument $event->setArgument("tpl", str_replace("{level}", $level, $tpl)); }); // render navigation output echo $nav->render(array( 'max_levels' => 3, 'outer_tpl' => "<ul class='level-1'>||</ul>", 'inner_tpl' => "<ul class='level-{level}'>||</ul>", )); Since the opening tpl doesn't support parsing but only is used once, you can add the level-1 fixed. Also wanted to point out that these classes are something not really needed as you can do it via CSS ul { (level1) } ul ul { (level2) } ul ul ul { (level3) }1 point
-
Pete, that's a good way to go. With the creation of different sized images, I would do it at uploading images through the images inputfield, the most common way I think. And if someone add images through the API somehow, he simply can add some more line of code to craete the images. It can go like this: public function init() { $this->addHookBefore('InputfieldFile::fileAdded', $this, 'createImages'); } public function createImages($event) { $inputfield = $event->object; if('images' != $inputfield->name) return; // we assume images field !! name of the field is: images !! otherwise change it $p = $inputfield->value['page']; // get the page if('gallery'!=$p->template) return; // don't do it on other pages than galleries $image = $event->argumentsByName("pagefile"); // get the image $image->width(400); $image->width(800); $image->width(1200); $image->size(200, 200); } This hooks into before the image gets added and creates the additional variations. It does not replace the event. After that the admin thumb is created. It is very basic and could be made a bit comfortable with checking the images filedname itself, etc. But it works fine for an individual single site.1 point
-
Cool. I'll give this a go and see how I get on. I'd like to move this into a module at some point to allow easybuse by others but I'll work on getting it working before I think about that part 9f my plan.1 point
-
This is very rough, but if it does what you want, I can clean things up. Basically it will regenerate the name of the page if the title changes. It works on page save, so you won't see the updated name until after you save. Of course you need to consider the impact this can have on broken links and search engine indexing of your site. You might want instead to change the module so it only updates the name if it is empty at page save. EDIT: I didn't see Macrura's code above. I also agree with Martijn that in general this isn't a good idea, but one great tool in PW that can help with this is a core module that is disabled by default, but if you turn on the "Page Path History" module, PW will take care of redirecting pages to their new name/url automatically. UPDATE: For those interested in this functionality, check out this module, instead of the attached one: https://processwire.com/talk/topic/7724-page-rename-options/ RegenerateName.zip1 point
-
One thing that should be possible is to create your 'own image' format. I do think there are opensource solutions for this. But to lazy to search one But basically it looks like this: <?php $phone = $image->size(40,15); $tablet = $image->size(200,75); $desktop = $image->size(400,150); $image = "<div class='my-adaptive-image'>" . " <img src='/path/to/transparent.png' " . " data-phone='$phone->url' " . " data-tablet='$tablet->url' " . " data-desktop='$desktop->url' " . " style='display: block; height: 0 width: 0;'" . " />" . " <noscript><img src='$desktop->url' /></noscript>" . "<div>"; echo $image; You can us Javascript to replace the transparent.png with the URL matches the data attribute. And use javascript to remove the style information. At that point there a fully adaptive image. If there's no Javascript active, then the inline style of the first image prevents displaying it. And the Image inside the <noscript> takes over. There are downsides to this methode. 1. It forces the browser to repaint your page every time it finds an image. 2 It needs 1 extra request for every page where the transparent pixel is used. (but this one get cached, so not really a big deal)1 point
-
Leaflet allows you host your own raster tiles, but I think you can also use svg if you want. Mapnik is a great python script for generating these raster tiles automatically from SHP files and adding colored data points from your database etc. I think so long as you restrict the area and zoom levels that leaflet is allowed to show, you can use this combination quite effectively, but it could definitely get out of hand if you're not careful. I would love to go vector with all my maps, but unfortunately I need to support governments and old versions of IE still, although I think we might be almost out of the woods on that one finally. The other advantage that map tiles has over vector is when you have hundreds/thousands of data points on a map - svg rendering speeds in browsers aren't there yet for these scenarios. Same goes for complex geographical shapes like rivers - raster tiles render so much quicker in this case as well. Back to topic - I think for a map that is appearing on every page of a site I'd still go with something completely static - you could even cache the google static map to avoid any limit restrictions.1 point
-
What I ended up doing is creating pages, and within those pages creating children as topics, replies to those topics then become children of the respective topic. thanks Apeisa for your useful code, which helped me steer my own code in the right direction.1 point
-
$pages->count() is used to count pages matching given selector string, such as $pages->count("template=basic-page"). $page->children returns children of given page (PageArray object), not a selector string. You can also use built-in property "numChildren": <?php echo $page->numChildren; ?>1 point
-
Yes, for instance, Foundation 5 includes jquery that can be used to interchange between content or images depending on media queries http://foundation.zurb.com/docs/components/interchange.html There is also responsejs.com that does a similar job in a similar way. You could probably do this with plain old javascript, to be honest (but I am not sure how!) But with any of these methods, you can use the PW API to create your images, or use Crop Image module to create specific images and then just call them depending on viewport size.1 point
-
Without testing it and just looking at the .htaccess file it looks like it should work since the .htaccess is routing images through the PHP script. ProCache bypasses PHP using .htaccess so it should be possible to make the two compatible. It will slow your page load times slightly as it has to fire up PHP to check some things, but would still be quicker than doing it without ProCache on at all. I can't help thinking though that there is a way to do this without PHP at all. Since you can write some code in ProcessWire to create any size versions of images you require (think about hooking page save and producing your variations then) all you need then surely is some JS to detect screen size and load the appropriate sized image, falling back to the largest. The advantage here is no PHP is used at all. I make it sound so easy, but it's probably not1 point
-
I could have sworn there was a discussion about this somewhere on the forums already, but I can't find it right now. Here are a couple of external discussions: http://stackoverflow.com/questions/3446216/what-is-the-difference-between-single-quoted-and-double-quoted-strings-in-php http://www.codeforest.net/php-myth-busters-using-single-quotes-on-string-is-faster-then-double-quotes A good collection of php comparisons: http://www.phpbench.com/ I seem to see conflicting results for all these optimizations1 point
-
The only difference I can think of is that an admin editor with the rights to edit the page with the checkbox could change this setting on you, whereas permission/role based restrictions require a higher level permission to change, but presumably this shouldn't really be an issue.1 point
-
If I understand correctly, it sounds fine. I am assuming you'll have a conditional in the template file for this page that will only render that portion of the page if the checkbox is checked?1 point
-
If it's not working from inside site/assets, then I think there must be something else at play. I actually just tested locally and I can even download: http://pwtest.dev/assets/form-downloads/11 AHAA SPRING DISTRICT MEETING FORM.doc just fine, and looking at PW's htaccess there is a rule for ignoring folders when rewriting so it should work and it does - my mistake above. EDIT: I see that: http://ahaa.inthooz.me/assets/img/ahaa-header-logo.png works fine on your site, so can you narrow it down to the form-downloads folder or the mixed case/space ridden filenames? Have you switched servers? Do the cases of the filenames in the links actually match the filenames? ie Windows vs Linux issue? ANOTHER EDIT: It is weird to me that the page you are getting when trying to download is actually the homepage with a 404 header and not the actual 404 page. AND ANOTHER: Notice that: http://ahaa.inthooz.me/site/assets/form-downloads/dd.doc'>http://ahaa.inthooz.me/site/assets/form-downloads/dd.doc gives you a proper 404 page, but: http://ahaa.inthooz.me/site/assets/form-downloads/d d.doc takes you to the homepage with a 404 header. Turns out I get the same thing locally!1 point
-
The first thing I notice is the spaces and mixed case in the filenames. This is a bad idea for lots of reasons which is why if you uploaded the docs via a Files field in PW it would have converted them for you. I think what is causing the 404 is PW's htaccess rewrites. It sees: http://ahaa.inthooz.me/assets/form-downloads/ and goes looking for a page with that path. You could potentially move form-downloads into: http://ahaa.inthooz.me/site/assets/form-downloads/ and things should work just fine. But, why not upload them to PW and have files that are part of the system? EDIT: having them all in a files field in PW means you could use this code to provide download links to all and it would automatically work for all new files as they are added: echo "<ul>"; foreach($page->files as $file){ echo "For the {$file->description} form, <a href='{$file->url}'>click here!</a>"; } echo "</ul>"; Although personally I hate "click here" links, but I went with how you had things structured already1 point
-
Hi Jonathan, I might be wrong but I'm thinking that the main reason this is so useful is that with Wordpress there are so many obstacles to overcome and this helps reset them in some way. With ProcessWire basically being a blank canvas I suppose the need is less great? That said, there are sure to be things here that can help speed up any site's development.1 point
-
Maybe you could add a "check for new translations" button to the language settings. This button would glob() recursively through the modules folder and collect all of the language files. These files should follow a conviction like "ProcessProgress.de.lang.json" or have some kind of install information in it to let the crawler recognize to which language the files belong.1 point
-
Around PW 3.0, I'm looking to make PW multi-instance compatible so that you could have multiple ProcessWire instances (each connected to separate databases) running from the same PHP code. This opens a huge amount of flexibility. But it also means that any kind of static references will have to disappear because function calls like Wire::getFuel('var') and wire('var') make the assumption that there is only one instance of ProcessWire running. This is one of the bad things about static calls in general. When that time comes, we will be able to apply contexts to our template files so that calls like wire('var') still work. But the reality is that non-static calls like $this->wire('var'), $this->var, and $var are technically superior when it comes to a multi-instance environment. So my opinion would be that it's not good to introduce new static calling methods when they may soon be obsolete.1 point
-
Hey, just wanted to tell you that my new tech blog (which gonna be a little bit like flamingruby.com in worse english) has finally a name and a new design. And this sweet animated icon It's not completely finished but the main design is ready. Responsive and some bugfixes will follow. What do you think: http://supercode.co/ -- nico1 point
-
1 point
-
I think most of us do that, at least sometimes. But there are no things in coding I hate more than trying to figure out where does lonely } starts. Well, there is. Add few more ifs inside each others, and the soup is ready. When you have huge chunks of html, maybe few intended ifs and then some lonely <?php } ?> or <?php endif; ?>, you are far from readable and easily manageable code. So my preference is actually: you should have so little logic on your markup generation, that echoing the parts that go inside ifs is good (usually oneliners). When having bigger chunks, then you should have other means: using functions, includes, some object or moving more logic to controller (or using actual controller). // with little html, echoing is much cleaner (imo) if($foo == $bar) { echo "<p>Show this text and this $variable</p>"; } else { echo "<p>Show this text instead with this $otherVariable</p>"; } // functions if($foo == $bar) { echo renderStuff1(); } else { echo renderStuff2(); } // includes if($foo == $bar) { include(./markup/stuff1.inc); } else { include(./markup/stuff2.inc); } // object $markup = new Markup(); if($foo == $bar) { echo $markup->stuff1(); } else { echo $markup->stuff2(); }1 point
-
@WillyC you are making great progress with your talking, I remember that back in 2011 it was much worse i donut live with mom.any more she always.fixings thing reword this,clean that.wipe those,flush that written for me.blah blah .enuff i get.my own place fine is my written i coder.not writter1 point
-
Can one do that? Build custom access controls? That'd be awesome. For now: Let's say you have the following structure: Pages - Page 1 - PageField 'userGroupSelect' - user-group-1 - Page 2 - PageField 'userGroupSelect' - user-group-2 - Page 3 - PageField 'userGroupSelect' - user-group-3 User Groups (which are pages) - User Group 1 - PageField 'userSelect' - user1 - user3 - User Group 2 - PageField 'userSelect' - user2 - user3 - User Group 3 - PageField 'userSelect' - user2 - user3 the following code would get you the references: // the user you want to get references for $targetUser = $users->get("user3"); // find pages that reference users $userReferences = $pages->find("userSelect.count>0"); // create empty page array, which will contain the matching groups $groupsWithUser = new PageArray(); foreach ($userReferences as $userReference) { if($userReference->userSelect->has("id={$targetUser->id}")) { $groupsWithUser->push($userReference); } } // find pages that reference user groups $groupReferences = $pages->find("userGroupSelect.count>0"); // create empty page array, which will contain the matching pages $pagesWithGroups = new PageArray(); foreach ($groupReferences as $groupReference) { if($groupReference->userGroupSelect->has("id={$groupReference->id}")) { $pagesWithGroups->push($groupReference); } } foreach ($groupsWithUser as $groupWithUser) { echo "{$targetUser->name} is referenced in User Group '{$groupWithUser->name}'<br>"; } foreach ($pagesWithGroups as $pageWithGroups) { echo "{$targetUser->name} is referenced in Page '{$pageWithGroups->name}'<br>"; } Perhaps there is a more elegant way, dunno. But as I said, you would want to wrap that logic into a custom hook for users, like $user->isInGroup('user-group-1') or $user->isReferencedOnPage('some-page-name') or something similar.1 point
-
Interesting question, wanna know the answer too =) Why do you need it? Breadcrumbs-like thing? I doubt it's possible with one selector only (perhaps with the new nested selectors). I would select all categories/groups with "your-user-page-field-name-here"-value of your desired user and then select all pages with "your-category/group-page-field-name-here"-value of each previously selected groups/categories. To wrap it all nicely I would first of all create a new hook called something like $page->references('page-field-name'); which spits out a WireArray of all the referencing pages. You can go from there then ...1 point
-
Hi, for all german users of this FormTemplateProcessor.modul who want to get german Umlaute like öäüß and not found the issues to similar slovak characters on github. After you have checked all the other solutions of that problem, like to save the page where the form is in "utf-8 without bom" and set the right header of the page, then only one is to do: search in /site/modules/FormTemplateProcessor.module for $value = htmlentities($this->contact->get($field->name)); and change to: $value = htmlentities($this->contact->get($field->name), ENT_QUOTES, 'UTF-8'); This was the solution for me. (Lösung für deutsche Umlaute im Kontaktformular) Thanks for this fine modul, ryan! _guenter_1 point
-
I found this very useful when dealing with my dev and live configurations. Paste and configure in /site/config.php $base_url = $_SERVER['SERVER_NAME']; switch ($base_url) { case "samplewebsite.dev": // LOCAL CONFIG $config->dbHost = 'localhostdb'; $config->dbName = 'processwire'; $config->dbUser = 'root'; $config->dbPass = 'PaSsWoRd'; $config->dbPort = '3306'; $config->httpHosts = array('samplewebsite.dev', 'www.samplewebsite.dev'); $config->debug = true; break; case "samplewebsite.com": // LIVE CONFIG $config->dbHost = 'livedb'; $config->dbName = 'processwire'; $config->dbUser = 'username'; $config->dbPass = 'pAsSwOrD'; $config->dbPort = '3306'; $config->httpHosts = array('samplewebsite.com', 'www.samplewebsite.com'); $config->debug = false; break; }1 point
-
Quick and dirty interface to batch delete unused templates by selecting them with checkboxes: if ($input->post->submit) { foreach ($input->post as $t) { // proceed only if the input is an integer different from 0 if (!(int)$t) return; $t = $templates->get($t); $templates->delete($t); $name = $t->name; // delete the fieldgroup associated with this template. more info in the next post $fg = $fieldgroups->get($name); $fieldgroups->delete($fg); // verify that the template is not there and print the confirmation if (!$templates->get($t)) echo "<p>template {$name} was deleted.</p>"; } } else { // print the form echo "<form method='post'>"; echo "<ul>"; foreach ($templates as $t) { // name of the temlate and number of pages it uses echo "<li>" . $t->name . " (" . $t->getNumPages() . " pages)"; // include checkbox if the template is not used by pages, and set it's id as value if (!$t->getNumPages()) echo " <input type='checkbox' value='{$t->id}' name='{$t->name}'>"; echo "</li>"; } echo "</ul>"; echo "<input type='submit' value='delete these' name='submit'>"; echo "</form>"; } Edit: Added "if ($templates->get($t)))" to the "template was deleted" line. Now we are sure that it was really deleted Edit2: Edited the code based on the problem explained in the next post1 point
-
To use a honeypot I use the following code. Works like a charm and never ever get spam send through the forms on several websites. 1. First I add the checkbox $field = $modules->get('InputfieldCheckbox'); $field->label = "Stuur een e-mail"; $field->attr('id+name','sendemail'); $form->append($field); 2. When processing the field find the spam field and set the error // Get the form field $spamField = $form->get("sendemail"); // Get the spam action $spamAction = $sanitizer->text($input->post->sendemail); // If the spamAction is true tell the bot to try again if ($spamAction == 1) { $spamField->error("Please try again!"); } 3. Hide the field with CSS .Inputfield_sendemail { display: none !important; }1 point
-
Here is a revised version of the module. It accepts all Page::status options, eg. $form->pagestatus = Page::statusUnpublished; $form->pagestatus = Page::statusHidden; If left blank, then the page will be published. Not sure if this is really an ideal default. Maybe it would be good to get some input from others on this. FormTemplateProcessor.module1 point
-
Hi I know this is an old thread now, but I'm hoping I can provide some useful information relating to this error, as I encountered it myself recently when installing ProcessWire on Vidahost Cloud hosting. I eventually managed to trace the problem back to a probable cause, and a solution - please bear with me The "non-object" mentioned in the error was the "type" property (which was NULL) of the fieldtypes list for a template (the loop in Pages.php). It was NULL because fieldtype information was not set when initialised. ... because the list of modules could not be loaded and returned to the functions relying on them. I think this was caused by the findModuleFiles() function. Although it seemed it could create the file on the first call of this function, the combination of network file system in the cloud hosting (which introduced a delay in file access) and the LOCK_EX parameter meant that the data couldn't actually be written to it. On next calls to the findModuleFiles() function, the cache file "exists", but its empty contents are returned - resulting in the error. From testing, my proposed change removes the LOCK_EX parameter, which allows the data to be written to the file correctly. In addition, if the cache exists - the content is only returned if the array isn't empty. My proposed code changes are a pull request on GitHub if anyone else encounters this problem.1 point
-
i tried Pete's code but it wasn't working; i made some changes based on seeing similar things on other modules, so here is a version that is working for me on latest stable: <?php class BuildUrl extends WireData implements Module { /** * Basic information about module */ public static function getModuleInfo() { return array( 'title' => 'Build URL', 'summary' => 'Builds a URL on page save based on specific fields.', 'href' => '', 'version' => 001, 'permanent' => false, 'autoload' => true, 'singular' => true, ); } /** * Initialize the module and setup hooks */ public function init() { $this->pages->addHookAfter('saveReady', $this, 'buildUrl'); } /** * * @param HookEvent $event */ public function buildUrl($event) { // Accesses the $page object $page = $event->arguments[0]; if($page->template != 'product-child') return; $page->name = $page->title; $this->message("slug changed to " . $page->name); } }1 point
-
No there's no way to avoid this, except if you would do $fields = $somepage->fieldgroup; foreach($fields as $f) { $inputfield = $fields->get("$f->name")->getInputfield($somepage); $form->add($inputfield); } This somehow gets around the error when rendering the form. But once submitted you'll end with the same error. Yes you are correct. In PW a page needs to be saved before files can be added or accessed, the PagefileManager requires a page id and since you build it using inputfields in context of a page object it fails. Pagefiles are saved in the filesystem using the page id, and since the page doesn't yet have an id it's simply not possible. For the same reason Ryan's FormTemplateProcessor also only works with text fields. Since you seem to have file fields in your template, you have to save the page to go this route, which I'm sure you don't want. Or remove file fields from the form, which is maybe also not what you want. So you have to build it different and build the form differently, maybe still using the fields from a template, but add your own file field upload. There various threads and example I did a while back. Some are on my gists. Example: https://gist.github.com/somatonic/41509741 point
-
WillyC is right! How could I missed it So you can do: $form->setMarkup(array( 'list' => "<div {attrs}>{out}</div>", 'item' => "<div {attrs}>{out}</div>" ));1 point
-
This is great! Thanks for posting this. This is a great guide for people that want to implement some of PW's form controls outside of the admin template. It looks like you've got all the parts covered. Since this thread is turning into a helpful tutorial and reference, I want to cover the different ways of getting and setting values to Inputfields: <?php // setting $inputfield->attr('name', 'value'); $inputfield->set('name', 'value'); $inputfield->name = value; // same as set() above, but shorter // getting $value = $input->attr('name'); $value = $inputfield->get('value'); $value = $inputfield->value; // same as get() above, but shorter The attr() method is designed for explicitly setting setting/getting attributes that should go with the form input. For instance, 'name', 'class', 'id', 'value', 'checked', 'type', or whatever other attributes you want to go with the input. It should not be used for anything else because whatever you pass to it will end up as an attribute on the markup that gets output. Whereas the set(), get() and direct reference methods are designed for setting/getting other properties, like field configuration options. They also work with attributes like the attr() method, but it's better to use the attr() method when setting an attribute just to be clear to PW that you intend that to be an actual attribute with the markup that gets output. If you look in PW's code, you might also see it using these two functions in some instances: <?php $inputfield->setAttribute('name', 'value'); // same as $inputfield->attr('name', 'value'); $value = $inputfield->getAttribute('name'); // same as $value = $inputfield->attr('name'); These are the same thing as the attr() method, and the preferred syntax is to use the attr() method in your code. Internally, PW translates the attr() method to either setAttribute() or getAttribute(), depending on the number of arguments. The reason setAttribute() and getAttribute() exist is for people creating new Inputfield classes... it's easier to override single purpose methods rather than multi-purpose methods (like attr). But on a public interface, it's easier to use a multi-purpose method like attr(), so I recommend ignoring setAttribute() and getAttribute() unless you are developing new Inputfields. Variations of attr() In addition to getting and setting single attributes, the attr() method can also set multiple attributes at the same time. Below are all the possible variations (some repeated from above): <?php // set single attribute (same as example above) $inputfield->attr('name', 'value'); // get single attribute (same as example above) $value = $inputfield->attr('name'); // set multiple attributes with same value $inputfield->attr('id+name', 'value'); // set multiple attributes with different values $attrs = array( 'name' => 'value', 'name' => 'value', // etc.. ); $inputfield->attr($attrs); Inputfields that contain other Inputfields One type of Inputfield is the InputfieldWrapper, and it's designed solely to contain other Inputfields. Examples of InputfieldWrappers also include InputfieldForm and InputfieldFieldset (all are derived from InputfieldWrapper). On these Inputfields, the get() and direct reference can be used to retrieve any one of the fields by name: <?php $inputfield = $form->get('your_field_name'); $inputfield = $form->your_field_name; // direct reference works as alternate syntax Internally, the two calls above translate to this: <?php $inputfield = $form->find("name=your_field_name")->first(); What that means is it'll find any field in the form, not just direct children of the field you are checking. So a call to $form->get('your_field_name') will return the associated field, regardless of of many fieldsets it's wrapped under. This is just to keep things simple. After all, the form fields are all living in the same namespace when the form gets output. By the way, that find() method mentioned above can be used with any selector, just like with pages. It will return all inputfields in the form that match the properties you give it. But in practice, I've not ever needed it in my forms, so not sure how useful it really is. You can add/remove Inputfields as children using these methods. In the example below, we'll assume that $form is an instance of InputfieldForm: <?php $form->append($inputfield); // append an inputfield to the form $form->prepend($inputfield); // prepend an inputfield to the form $form->add($inputfield); // same as append() $form->remove($inputfield); // remove inputfield from the form Built in properties All Inputfields have these built-in properties that you can set or get: <?php $inputfield->label = "The clickable label that appears above this field. Should only be a few words."; $inputfield->description = "A longer description that appears below the label. Can be any length."; $inputfield->notes = "A extra highlighted area that appears under the field. Can be any length."; $inputfield->head = "Headline that appears below label/above description. "; // PW 2.1 only! $inputfield->id; // HTML 'id' attribute. Auto-generated if you don't set it. $inputfield->name; // HTML 'name' attribute, required $inputfield->value; // HTML 'value' attribute, if applicable $inputfield->class; // HTML 'class' attribute, optional $inputfield->required = 0; // value not required for this inputfield $inputfield->required = 1; // value IS required for this inputfield $inputfield->collapsed = Inputfield::collapsedNo; // Field will display open (this is the default) $inputfield->collapsed = Inputfield::collapsedYes; // Field will display collapsed, requiring a click to open $inputfield->collapsed = Inputfield::collapsedBlank; // Field will display collapsed only if blank $inputfield->collapsed = Inputfield::collapsedHidden; // Field will not be rendered in the form1 point