Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 03/06/2012 in all areas

  1. I've been working on making repeatable fields for ProcessWire for a couple weeks and actually now have it nearly ready. I need to do some more testing before releasing it, but wanted to give you a little preview. (please view the full screen version for better quality) Accessing repeatable fields from the API is super-easy. They are fully searchable/selectable with the API just as easily as with any other fields. So if you wanted to find all pages that had a buildings field with a floors value of 50 or more, you'd do this, for example: $pages->find("buildings.floors>=50"); Lets say you are on the page with a 'buildings' field and you want to print out all the building names and number of floors. It works exactly the same as page references: foreach($page->buildings as $p) { echo "<p>{$p->building_name}</p> has {$p->floors} floors.</p>"; }
    1 point
  2. Just pushed changes made by Ryan. They went in almost exactly like that.
    1 point
  3. I love it when that happens - happened to me with something the other day (although it was half an hour to do something I thought would take half a day - same principle). It certainly starts to make up for those frustrating times when running into coding roadblocks - those tedious ones where the answer turns out to be easy, but only after lots of time and swearing - I much prefer it when it happens this way around
    1 point
  4. Thanks for all suggestions. Following the Soma's template, I have created a master layout template (functionally identical with the default PW template), that looks like this: <!DOCTYPE html> <html lang="en"> <head> <? include "blocks/htmlhead.inc" ?> </head> <body> <p id='bgtitle'><?= $page->rootParent->title ?></p> <div id="masthead" class="masthead"> <div class="container"> <? include "blocks/masthead.inc" ?> </div><!--/container--> </div> <div id="content" class="content"> <div class="container"> <div id="sidebar"> <? include "blocks/sidebar.inc" ?> </div><!--/sidebar--> <div id="bodycopy"> <? include "views/{$page->template}.inc" ?> </div><!--/bodycopy--> </div><!--/container--> </div><!--/content--> <div id="footer" class="footer"> <div class="container"> <? include "blocks/footer.inc" ?> </div><!--/container--> </div><!--/footer--> <? include "blocks/editpage.inc" ?> </body> </html>
    1 point
  5. This came up once again when I was teaching PW for our clients. It was 5 min for the first "Oops, I deleted something and I want it back". What I started thinking that how about having this with additional page status, something like "removed", which hides it all together from admin site tree and makes it unaccessible from selectors. But it still keeps it original position and it isn't moved anywhere. It could also have some "safe time", like 30 days before it is removed finally (or maybe moved to the current trash). Then we would have "personalized trash", which would show all the removed pages that user did have editing access and they could edit and recover those pages. Just an idea - I do find current trash somehow limited.
    1 point
  6. Thanks Ryan. The only downside to this is it counts all comments including the ones waiting for moderation. The number may not always agree with what's being displayed. Not an issue though. Didn't even see that until you pointed it out. My search skills are wanting it seems. Thanks. I did the pre-populate and hide option.
    1 point
  7. Antti, this looks fantastic. I'm pointing another domain to processwire.com so that I can test this out. One suggestion that I have is to change this: // If subdomain is visible on url, we might wanna redirect if(strpos($_GET['it'],$subdomain) !== false) { // We don't redirect if one has editing access (because user would need to login on new domain also) $page = $this->pages->get("/". $_GET['it']); if(!$page->editable()) { $_GET['it'] = str_replace("{$subdomain}/", "", $_GET['it']); $this->session->redirect('http://' . $subdomain . '/' . $_GET['it']); } } …to this (or something like it, as I've not actually tested, but hopefully the intention is there): // saved $_GET['it'], since PW unsets it after using it // note this is an unsanitized variable, so only look at it, don't use it protected $it; public function init() { $this->subdomainsArray = explode("\n", strtolower($this->subdomains)); $this->it = ltrim($_GET['it'], '/'); // ...then your existing code… } public function ready() { // if $this->subdomain was valid in init(), then no need to do more here. if($this->subdomain) return; $subdomain = $this->page->rootParent->name; // if rootParent->name isn't a subdomain, then no need to continue if(!in_array($subdomain, $this->subdomainsArray)) return; // if subdomain is visible on url, we might wanna redirect if(strpos($this->it, $subdomain) === 0 && !$this->page->editable()) { $url = str_replace("/{$subdomain}/", '/', $this->page->httpUrl); $this->session->redirect(url); } } The point of this is to avoid sending an unfiltered $_GET['it'] into $pages->get(), which may contain anything in it at that point. There's always a security concern with sending unfiltered input to any function that isn't expecting it. You can avoid the issue completely by moving the check into the ready() function, which gets called after PW has already validated the URL and determined the page. If you wanted to get more fancy with it, you could also foreach($input->urlSegments) and append $input->pageNum (if > 1), but I really don't think that's necessary.
    1 point
  8. Added multilang support for AdminBar. Also uses template labels if available "New Blog post" instead of just "New" or old db-style "New blog-post".
    1 point
  9. Has anybody had the chance to try out these repeatable fields yet? I have one more update that I put in the latest commit, though this applies to all fields, not just repeatable fields. This update enables you to create columns rather than just rows. You can now specify a width percent in any field's input settings (or use the columnWidth property on an inputfield from the API). Here's a screenshot of the new setting: And here's the result (combined with a repeatable field). In the screenshot below, I've set building_name to have a width of 50%, floors to have a width of 25% and height to have a width of 25%: Note that because this involves some stylesheet tweaks, this may not work on 3rd party admin themes right away. But I'll be happy to assist admin theme developers to add the necessary stylesheet tweaks to make this work.
    1 point
  10. I'm not sure this describes what it actually does, or I might be misunderstanding what you are trying to say. But what actually happens is that the template file assigned to the accessed page gets executed, as a PHP file. ProcessWire spits out no content. You have to specifically code your template file to spit out content. ProcessWire does not participate in this aspect, only you do. That template file is handed a copy of $page, which represents the page that was requested by the URL. Whether you choose to do anything with that $page is up to you. This is an architecture that ProcessWire supports. Make all your pages use the same template file and this is what you will have. This doesn't mean all your pages have to use the same 'template' (unless you want them to), just the same 'template file'. To achieve that, edit each template, click 'Advanced' and set the 'Alternate Template Filename' to be the one you want to be the top of your pyramid (i.e. main.php). Another way to do it is to maintain separate template files that just include("./main.php")). Either way, all valid requests will be routed to your main.php with $page being the only thing different from request to request. If you want requests routed to your template file that don't map to URLs in ProcessWire, then edit the template settings and turn on URL Segments in the URLs tab. Now all URLs below the page(s) using that template file will be handed any URLs below them that don't map to pages. You can then map $input->urlSegment(n) to whatever you want, whether a function/class method, another $pages->get/find call, another application, etc. This is a lot of fun to be had in here. You can do this, but your htaccess will have to identify the paths that get directed to your own script by looking for them with a rewrite rule. Or you'd have to put your script in a directory that is the same as the URL used to access it (or at least, the first url segment is the same). The htaccess file does not know which requests will result in 404s. It doesn't know what is "non-cms" unless the request is to a real file or directory. ProcessWire is what ultimately generates the 404.
    1 point
  11. Antti's right about what that example is doing, but let me do another. Lets say we've hooked in before $session->redirect(). If I go look at the function definition in /wire/core/Session.php, I can see it has two arguments: $url and $http301. So lets say I want to hook into that and automatically add a GET variable to every redirected URL, like "url.com/path/?is_redirected=1", I'd do this: public function hookSessionRedirect(HookEvent $event) { $url = $event->arguments('url'); // get the existing value $url .= (strpos($url, '?') === false ? "?" : "&") . "is_redirected=1"; $event->arguments('url', $url); // set the new value that gets sent to $session->redirect } Btw, there is some overhead in using the argument names as opposed to the position (it involves using Reflection), so in most cases I think it's still better to use the argument position rather than the name. But have wanted to make both options available because I think some people will find it more straightforward to to use the argument names... and it no doubt makes code examples more readable.
    1 point
  12. Adam, that's a pretty slow page render time you had there. I don't think that can be attributed to taking an MVC approach, so am guessing there is some other factor at play that caused the bottleneck. If you want your current template file to be a controller pulling in other views, then use the TemplateFile class and place your view files in a directory like /site/templates/views/ $t = new TemplateFile('./views/list-news.php'); $t->set('items', $pages->find('template=news, featured=1, sort=-date, limit=3')); $out = $t->render(); Your /views/list-news.php file would look like this: <ul> <?php foreach($items as $item): ?> <li><a href="<?=$item->url?>"><?=$item->title?></a> <?=$item->date?></li> <?php endforeach; ?> </ul> If you are using this approach, it is far more efficient and straightforward to keep the foreach() in the view rather than calling up a new view for each item. It also gives the view the opportunity to handle an empty condition (if you want it to), and it prevents separation of the container from the items (i.e. you aren't splitting a <ul> and an <li> in separate files). Another approach (and the one I like to use) is to use a module as a view. I'll create an autoload module that adds a 'renderNews' (or another name as applicable) to the PageArray class. Then I get the output of that view like this: $out = $pages->find('...')->renderNews(); It's almost as easy with a normal, non-autoload module: $m = $modules->get("MarkupRenderNews"); $out = $m->render($pages->find("...")); Of course, the same approach can be done just by including a set of functions as views and calling upon them as needed, though I prefer the structure of modules for this stuff. But the end result is just as simple: $out = render_news($pages->find('...'));
    1 point
  13. 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
  14. OT for giggles: Call Japan and tell them to change their alphabet, because current is hardly translated into ASCII
    1 point
×
×
  • Create New...