-
Posts
17,240 -
Joined
-
Days Won
1,704
Everything posted by ryan
-
Are you sure that your hook isn't being triggered when moved in PageList? I can't think of any reason why it shouldn't. But if you can confirm that's the case let me know because it would be a bug. Both the trash() and restore() functions are triggered by $pages->save() when it detects that the parent has changed to or from the trash. There isn't currently a move() hook, though I'd be willing to add one to $pages if you all think it would be worthwhile. Currently, you can detect a move by hooking into $pages->save() and checking if the page's parent has changed: <?php if($page->parentPrevious) { // page has moved // $page->parentPrevious is the previous parent (Page object) // $page->parent is the new parent (Page object) } There also isn't currently a trash empty() hook either, though I'll also be happy to add one of these if you want it. However, when you empty the trash, $pages->delete() is called for every page in the trash, so an 'empty trash' hook might be irrelevant?
-
Hacking the core to have no trail slash as default
ryan replied to Adam Kiss's topic in General Support
This can be set on a per-template basis in your template settings. I don't think you can hack the core for this just because the PW admin requires trailing slashes, so hacking the core to make non-trailing the default setting may cause problems elsewhere in the system. But if you want to match the slash behavior WP URLs, I would set this for the page templates where applicable. -
Widgets like they are in WordPress are an entirely different animal than they would be in something like PW. In WordPress, you aren't dealing with anything-goes templates and instead you are dealing with themes. The theme follows an expected format an typically has an area built to contain widgets, among other things. Drupal is the same with blocks. These CMSs are markup generation engines because it's assumed that the various elements (widgets, blocks, modules, plugins) are generating their own markup. In these CMSs you are typically designing the theme for the CMS, rather than having the CMS accommodate your design. You are styling the output of someone else's markup. There are plusses and minuses to this approach. A plus is that you can make assumptions about things like widgets. Another plus is that you can make a lot happen without development (like dragging widgets in WordPress). A minus is that you are locked in by the needs of the theme and you have far less flexibility and control over your output. PW is a very different tool from WP and Drupal. PW's approach is to provide you with the tools to get to your data easily, and let you decide what you want to do with it in terms of output. With a few exceptions, it isn't going to generate the markup for you, and that's the point. I personally dislike working with tools that generate the markup for me because it always feels like I'm cleaning up someone else's mess rather than creating my own. Most Designer/Developers prefer to create their own markup because it lets you use your skills to accomplish whatever you want to. ExpressionEngine is another tool that operates on this same principle. Given the above, I prefer to keep PW out of markup generation at it's core. Meaning, I wouldn't ever want widgets/blocks to be a required element of the software, or part of it's base architecture (like WP or Drupal). But I also think that this is a great use of plugin modules, as well as your own development processes. The methods described by Antti and Adam are great ways to make widgets part of your site. And as was described earlier, you can turn any Page into a widget simply by rendering it (and you'd want to make sure your page's template is producing the appropriate output): echo $pages->get("/widgets/latest-news-list/")->render(); Still, something like the above really isn't much different than this (with less overhead too): include("./widgets/latest-news-list.php"); That's how you might make your own widgets. But I like the approach of using modules for widgets too, as they are easier to share for people that want them. I wouldn't ever want to start including built-in widgets in the core since that would be directing how you should build your site. But I'm not opposed to at least supporting it in the core for modules. So if you guys think it makes sense to add a base Widget module class in the core (like the Pyro example) that's good with me. In fact, we already do to an extent so I'm not sure it would be much more than this: abstract class Widget extends ModuleJS { abstract public function render(); } Still, good to have a known starting point even if not much to it. The way you would call it would be the same way you already do for other modules: echo $modules->get("MyWidget")->render(); or maybe something simpler: echo $widgets->MyWidget->render(); Almonk is this along the lines of what you were thinking, or something different?
-
You could do $page->save('fieldname'), replacing fieldname with the name of the field you want to save. But unless we're talking about something different than above, you don't need to do any save() calls. Also, saving a single field isn't going to trigger your hook.
-
Adam and Antti are right. When you are calling save() you are triggering an recursive/infinite loop. Why? Because every time you call $pages->save() your hook it getting executed once again. So in this case you'll want to do what Antti suggested which is to make your hook run before (rather than after). That way you can jump in before PW executes the save and set what you need, and let the original $pages->save() do it for you. Also, I'm assuming that "exit;" is there just for debugging... In terms of scalability, this is exactly what PW is built for. I've used it on sites up to ~10k pages. Most sites I develop in it are 2k-8k pages and up to 10x that number of URLs. This is either a lot of pages, or not a lot, depending on your point of view. I think for most open source CMSs, this is considered large scale. But if you are working at Google or Facebook, then you would say this is small scale. PW hasn't been tested in the hundreds of thousands of pages scale yet. I imagine it would work just fine at that scale, but don't have the sites to prove it yet. As you start to use PW at a larger scale, you do have to pay more attention to how you use it. For instance, calling $page->children() on a page that has 2,000 children is a bad idea because it'll be slow. Whereas calling $page->children('limit=25') is fast. Here's some more details on this: http://processwire.com/talk/index.php/topic,5.0.html Another factor to consider as you go larger scale is caching. PW 2.1 comes with some strong caching features that you can define on a per-template basis (edit a template, set a cache time, and more options open up). This is for caching entire page renders. For caching smaller pieces, it's really good to use the MarkupCache module. For instance, on that VillaRental site, the top left "villa search" box is expensive to create since it has to to iterate through all the regions, destinations and vacation types. As a result, it is cached using the MarkupCache module, which re-creates it once per day, while the rest of the page may be uncached. More info on how to use the MarkupCache module is here: http://processwire.com/talk/index.php/topic,8.0.html
-
To filter out the current rights holder, I think you'd just want to not link to it, or add some logic to the template to avoid listing those. To not link to it, you'd just do something like this: <?php if($page->atype->name == 'current_rights_holder') { echo $page->atype->title; } else { echo "<a href='{$page->atype->url}'>{$page->atype->title}</a>"; } Or if you want to link to, but not list all the current rights holders, your atype template could have the logic instead: <?php if($page->name == 'current_rights_holder') { echo "nothing to list"; } else { echo $pages->find("atype=$page, limit=25")->render(); }
-
I misunderstood before that you actually want to re-create this in PW. I think that makes sense to do it, and it should be straightforward to setup your event template and add fields to it. I would keep all your events under the same parent and just set the default sort to be reverse chronological on your date field, and select from them based on that same date field. For example, to retrieve all events in July, 2011 you'd so something like the following. We'll assume you have a field called 'date' that hold's the events date: <?php $start = strtotime("2011-07-01 00:00:00"); $end = strtotime("2011-08-01 00:00:00"); $events = $pages->find("template=event, date>=$start, date<$end, sort=date"); You'll also want to draw a calendar using an HTML table. To do this, you'll need to know the number of days in the month. There's a lot of examples out there on how you might do this with PHP. So I won't attempt to redo it here, but will say that as you are drawing those rows and columns for the days, you'll want to check if any of them matches the date you found in your $events for the month (like above), so that you can highlight the individual cell as having an event in it.
-
Definitely check out the example sinnut linked to, that's even better.
-
See the code example on this page, which demonstrates running a hook after a page is saved: http://processwire.com/api/modules/ You could also do the same thing when a page is deleted by hooking into the $pages->delete. method. There are a lot of these kind of hooks in PW. You can identify them by doing a grep for "___" (3 underscores) in PW's core or modules, as they all start with 3 underscores. Likewise, you can make any functions in your own modules hookable by preceding the function name with 3 underscores.
-
If I had to create a calendar like this, I would just create an 'event' template and give it title, detail and date fields. It would certainly be simpler to develop this in PW than without it. But if I'm understanding you correctly, you don't have that option and you need to work with this existing code and database. Given that, I think you should be able to just copy all this code and paste it into a PW template file (after fixing the security issues). Not sure if this is necessary, but since PW already has a DB connection going on, you probably want to keep a copy of your own DB connection and use it in your calls: <?php $mydb = mysql_connect(...); /// And then use that in your DB calls, i.e. mysql_select_db("database_event", $mydb); mysql_query("SELECT ...", $mydb); With regard to the security issues, there are both XSS and SQL injection problems here. XSS in a giant can of worms and you want to keep it closed. You need to make sure that any input from the user is routed through htmlentities() or htmlspecialchars() before it is output, unless you have specifically typecast it as something (like an integer). This includes $_SERVER['PHP_SELF'] which can be considered tainted, and needs to be sanitized in the same way. Though you probably don't need to use PHP_SELF at all, so I would just avoid using it. To protect against SQL query injection, you need to sanitize and validate your data. If you are expecting something to be an integer, then typecast it to an integer as soon as you get it from GPC (get, post or cookie), i.e. <?php if(isset($_GET['month'])){ $month = (int) $_GET['month']; if($month < 1 || $month > 12) die("Invalid month"); } else $month = 1; If you are getting a string from GPC (get, post, cookie) input, then there's more to consider. If there is a group of expected values, then make sure the string is literally one of those expected values before using it. i.e. <?php $valid = array('Jan', 'Feb', 'Mar', ...); $key = array_search($_POST['month'], $valid, true); if($key === false) die("Invalid month"); $month = $valid[$key]; If you are dealing with unknown text, then you'll want to sanitize it as much as possible: <?php $title = substr(strip_tags($_POST['txttitle']), 0, 128); // strip HTML and limit to 128 characters $title = htmlentities($title, ENT_QUOTES, "UTF-8"); // may or may not want this here, see below $title = mysql_real_escape_string($title); // sanitize for database query After the above, SQL injection is not going to be a problem with your $title. You may or may not want the htmlentities() there. If you don't have it there, then you'll need to know that you may be inserting XSS tainted data into your database, and any time you output that data, it will need to be entity encoded. I think in your situation it may be a good idea to run the htmlentities() before insertion, but I generally prefer not to keep entity encoded data in the database just because it takes up more space, prevents legitimate HTML, and interferes with fulltext indexing. ProcessWire does not store entity encoded data. But it does take a little more discipline to always treat data in your DB as possibly XSS tainted. To recap, look at all the $_GET, $_POST and $_SERVER vars in your code and consider them tainted. Do what's necessary to make them safe for database insertion and/or output, depending on the context. And once it's secure, try using this in a PW template.
-
Once you start really using them, you may not want to do it any other way. While the page reference type is infinitely scalable, I also think it's a good solution for small needs too. Lets say that you needed to associate some other data with your Publisher, Developer and Current Rights Holder. For instance, maybe a brief summary of what they are, an graphic/image/icon, or anything else. You just add that field to your template used by those entries. I'll reuse a previous example: <?php // for syntax highlighting foreach($page->atype as $a) { echo "<p><a href='{$a->url}'>{$a->title}</a><br />{$a->summary}</p>"; } Note how I liked to the atype page. This presents some cool new possibilities for usability (not to mention things for Google to index). We could make the atype page show all pages that have a given type selected. Depending on the context, this might be really useful to the users of your site (if not, yourself). Here's what we might do in the atype template: <?php echo "<h1>{$page->title}</h1>"; echo "<h2>{$page->summary}</h2>"; echo "<h3>Pages with {$page->title} selected:</h3>"; $entries = $pages->find("atype=$page, limit=25"); echo $entries->render(); When you start building sites in this manner, the page references start to become valuable parts of the site's structure. You start to build connections that weren't possible before, many of which are genuinely useful. But for everything else, or for pages that may not have a place in your site structure, put them in a /tools/ or /config/ section of your site, which you may or may not make accessible to guests. Here are a couple examples: http://www.tripsite.com/tools/difficulty/easy/ http://www.tripsite.com/tools/tour-types/self-guided/ From a technical standpoint, while you are right that there may be a max of 3 entries per page in the database instead of 1, each entry is an integer (a pointer to a page) rather than a string of text, like you see in many other CMS's select types. So perhaps there are 3 entries, but they are consuming far less storage resources than in the other CMS (or in fieldtype you linked to). Lets say you decide the word "Current" in "Current Rights Holder" is not necessary and you want to change the label to "Rights Holder". In PW you just go and change it. In other CMSs, you might not be able to, or if you did, it might corrupt the data leaving all existing "Current Rights Holder" entries there while new ones are labeled "Rights Holder". If you are certain you won't ever want/need to do anything more than just show a Developer, Publisher and Current Rights Holder checkbox, then there's still no harm in keeping it a Page reference. But if you don't want to take that route or one reason or another, you can make them single checkbox fields: $page->developer $page->publisher $page->rights_holder Wrap a fieldset around them to keep them together. I only use these checkboxes for single on/off toggles, but just wanted to mention this alternative if you don't want to use the Page fieldtype for one reason or another. But as a best practice, I nearly always recommend using Page references for this kind of stuff as I think it leads to good design structure, scalability and long term benefits. But I can say that only for my own sites, and I know everybody's needs are different.
-
Thanks for the link. Nice photos in that story too (love the cockpit photo). Amazing there's only 400 of them. But I guess given what it is, the cost is pretty prohibitive. But for someone with money that wants a supercar for the attention, perhaps this is a much better value (and grabs more attention) than any Ferrari or Lamborghini.
-
Great explanation Antti! Here's a couple more simple examples. I think that 'type' might be a reserved word (not positive), but I'll change it to 'atype' in the examples just in case. Iterate through the selected values on your $page: foreach($page->atype as $a) { echo "<p>" . $a->title . "</p>"; } The output might look like this: <p>Publisher</p><p>Developer</p> Or, if you made your page reference a single page reference, then it's just: echo "<p>" . $page->atype->title . "</p>"; Antti's second example can also be simplified a bit to this: if($page->atype->has("name=publisher")) { echo "Publisher"; }
-
You can define config settings for your own use. What I do on a lot of sites is define some of the common image dimensions in my /site/config.php file and just call upon these when I need them: $config->myThumbWidth = 150; $config->myThumbHeight = 100; Then when you need to create a thumbnail, just refer to those: $thumb = $page->image->size($config->myThumbWidth, $config->myThumbHeight); This may be a preferable way to do it if you have multiple templates that need to use the same dimensions. That way you aren't specifying the same dimensions in multiple places... though I'm not always that disciplined.
-
I think I'm going to manually change it to start at exactly 5k–that way it won't look like an accident.
-
Sounds good, I will plan to implement. Admittedly, this isn't a huge priority for the sites that I work on (though certainly a very useful feature), but perhaps it is more important for others. I'm leaving the country to travel in 2 weeks and want to come back and get 2.1 released asap and then start focusing on multi-language features. I'm thinking it would be good to focus on multi-language features first, as that is the priority. But perhaps the image fieldtype/inputfield improvements would be a good to accompany 2.2 as well.
-
Actually I think if you set $config->advanced = true in your /site/config.php file, then you'll get a 'required' option with most fieldtypes. The only problem is that this has to be implemented by the fieldtype module, and it's not implemented by all of them. But as we get closer to wrapping up 2.1, it seems like this is one thing that should probably be finalized in this version (and taken out of advanced mode).
-
I can't reproduce either. Can you provide additional details on your environment (esp. browser) and the exact steps to reproduce? Thanks, RYan
-
Did your own pages start with lower numbers and then jump into the 5k's, or did they just start in the 5k's? Hopefully they started in the 5k's, but want to double check. I'd hoped to keep a few thousand IDs reserved in the system for future needs. But you are right in your assumption in that I was testing with a lot of data at one point. I'd planned to reset it back, but ultimately didn't because I figured I'd like to keep a block of IDs as reserved for future system use. I didn't originally plan it that way, but did intentionally not reset it back when I realized there would be some benefit to keeping a block of lower IDs for system use.
-
Yeah, I think you are right. I'm going to change this.
-
Sent, let me know if you don't get it. Any more? PM me if you don't feel like pasting your email address here.
-
Wow, not cheap. Where did you find that? I'm guessing a child's car seat won't fit so well in their either.
-
I haven't yet really figured out Google+ yet, but I have figured out how to do invites so if anyone isn't already on it just let me know if I can send you an invite.
-
Lots of good ideas here and lots to consider. I'm trying to figure out if some of these things should be built-in, or if we should make the image capabilities as pluggable as possible, or if we should have a couple of different types of image fields (or more likely, a little of each). It's true that the in-CMS image scaling may not make much sense if you are already scaling the images in the API. But we also can't assume that's the way everyone is using it. I tend to think that interactive scaling will be applicable in some instances. Perhaps it's something a checkbox can enable/disable in the image's field settings. No doubt, cropping is definitely more useful to the way that most use PW. Antti's got a point in that there may be a need to have the smaller version visually cropped but not the larger version. For instance, this site: http://directory.pewscholars.org/years/2011/ On that site', we've got these little square thumbnails of people that all have to be the same dimensions vertically and horizontally. But if you click to their bio, the photo is only constrained horizontally. When I initially uploaded the photos, some people's heads were getting chopped, so I had to do some manual cropping and Photoshop and re-upload for a few of them. But of course, that meant that I had to take something away from their full-size photo for the needs of the thumbnail. It wasn't a problem in this case, but certainly it would have been preferable to be able to manipulate my thumbnail separately and not have to crop my full-size photos. Thinking of another instance, I was working on a site where we had the thumbnail, which led to a portfolio page of "medium" size photos, which in turn led to the full size photos. So if there is an $image->thumbnail, why not an $image->medium too? Once starting down this path of image variations, it's a little hard to predict what the exact need will be. So one option would be to take $image->thumbnail even a little further and provide for definable image variations in the image field's settings. Perhaps you call one of them "thumbnail" and another "toenail", but it's left up to the user what they want to define with a given image field. It would be a little tricky to implement, but it would be useful and only add the additional functionality for those that specifically wanted it. Another option related to the $image->thumbnail idea is to instead have an $image->original. This is Antti's idea, but maintains an original rather than a thumbnail. By default $image->original would just point to itself. But if you cropped or resized your image at any time, then $image->original would be populated with the original unmodified version. There's no assumption about what it would be used for. Though PW would use it internally so you could interactively scale up or re-crop an image from the original (rather than going through a new JPG compression for every modification). Basically it would just "be there" in the background if you wanted it. The $image you see in your field would be the one you cropped (so the person's head doesn't get cut off). Most folks might not even know PW is keeping a copy of the original (minus a little 'view original' link)... so there's no perception of additional complexity. From the API perspective, if I wanted to produce a list of 100x100 thumbnails and link to the original unmodified version you'd do it like this: <?php foreach($page->images as $image) { $thumb = $image->size(100, 100); echo "<a href='{$image->original->url}'><img src='{$thumb->url}' /></a>"; } If an image had never been cropped, then of course $image->original would be the same thing as $image... no need for performing additional checks in your API code. Both $image->thumbnail or $image->original could have solved the examples I mentioned above. The main difference is that $image->original would be used by PW internally (so it can scale/crop from the source rather than copies), and there would only be one image to interact with from the admin side (like currently).
-
PW updates the last modified date if you call $page->save() or $pages->save($page) -- those two commands functionally identical. But if you save just one field from a page, like $page->save('body'), it doesn't update the last modified date or anything in the pages table. It only updates the 'field_body' table. Perhaps it makes sense for it to update the pages table too (something to talk about more), but the thought behind the current behavior is this: if you are just saving an individual field, you are likely doing so to limit overhead as much as possible. As a result, PW doesn't considers that a field save, not a page save. I don't think that there would be much overhead in updating the modified date, so it's something to consider. I'm not sure how you are seeing it modify the modified_user because the query that updates that is the same one that updates the modified date, and it doesn't get executed when you just save a field (2.1). For a page save, the last modified user has to correspond go a user in the system. So if you want to show the user as "api" as the last modified user, then you'd have to add a user called "api" and set it to be the current user before you save your pages (full page saves), like this: Wire::setFuel('user', $users->get('api'));