Leaderboard
Popular Content
Showing content with the highest reputation on 05/24/2012 in all areas
-
@diogo Thanks for sharing your code. I just packed it in a Module adding some features. It extends the Page class, by hooking a new method mlUrl(bool includePageId). I wanted to generate the localized url too. So instead of using names I use title to generate url. That function accepts one boolean parameter to specify if to use the page id: www.mydomain.com/it/my-italian-title/ www.mydomain.com/it/[page id]_my-italian-title The id is useful if you can't be sure that the localized title is unique, or in case that there are a lot of contents and you don't want to search by title in a long loop, but seeking directly by id. The generated url always contains the language code, for the default one too, that in this case is manually mapped to 'en'; This is the code (that could be improved, especially in the generation of the url): <?php class MultiLanguageURL extends WireData implements Module { /** * getModuleInfo is a module required by all modules to tell ProcessWire about them * * @return array * */ public static function getModuleInfo() { return array( // The module'ss title, typically a little more descriptive than the class name 'title' => 'MultiLanguageURL', // version: major, minor, revision, i.e. 100 = 1.0.0 'version' => 100, // summary is brief description of what this module is 'summary' => 'Multi language url', // Optional URL to more information about the module 'href' => '', // singular=true: indicates that only one instance of the module is allowed. // This is usually what you want for modules that attach hooks. 'singular' => true, // autoload=true: indicates the module should be started with ProcessWire. // This is necessary for any modules that attach runtime hooks, otherwise those // hooks won't get attached unless some other code calls the module on it's own. // Note that autoload modules are almost always also 'singular' (seen above). 'autoload' => true, ); } /** * Initialize the module * * ProcessWire calls this when the module is loaded. For 'autoload' modules, this will be called * when ProcessWire's API is ready. As a result, this is a good place to attach hooks. * */ public function init() { $this->addHook('Page::mlUrl', $this, 'mlUrl'); } private function mlPath($page) { if($page->id === 1) return '/'; $path = ''; $parents = $page->parents(); foreach($parents as $parent) if($parent->id > 1) $path .= "/".$this->toSlug($parent->title); return $path . '/' . $this->toSlug($page->title) . '/'; } public function mlUrl($event) { $page = $event->object; $includeId = $event->arguments(0); $url = $this->mlPath($page); if ($includeId) { $segments = explode('/', $url); $index = count($segments)-2; $segments[$index] = $page->id.'_'.$segments[$index]; $url = implode('/', $segments); } $lang = $this->user->language->name; if (!$lang || $lang=='default') $lang = 'en'; $event->return = '/'.$lang.$url; } private function toSlug($str) { $str = strtolower(trim($str)); $str = preg_replace('/[^a-z0-9-]/', '-', $str); $str = preg_replace('/-+/', "-", $str); return $str; } public function parseUrl() { $page = $this->fuel('page'); $user = $this->fuel('user'); $languages = $this->fuel('languages'); $pages = $this->fuel('pages'); $input = $this->fuel('input'); $lang = $page->name; if ($lang == 'en') $lang='default'; $user->language = $languages->get($lang); $basePage = $pages->get("/"); $segments = $input->urlSegments; if($segments){ $page = $basePage; foreach($segments as $segment){ // search of page id inside the segment $parts = explode('_', $segment); $pageid=null; if (count($parts)>1 && is_numeric($parts[0])) { $pageid=$parts[0]; $page = $pages->get($pageid); } else { $children = $page->children; foreach ($children as $child){ $found = false; if($segment==$this->toSlug($child->title)){ $page = $child; $found = true; break; } } } if (!$found) throw new Wire404Exception(); } } else { $page = $basePage; } $this->fuel->set('page', $page); return $page; } } This is the code to use the module in your template: <?php $page = $modules->get('MultiLanguageURL')->parseUrl(); include("./{$page->template}.php"); ?> and to generate the localized url: // to include the page id $page->mlUrl(true); // to generate without id $page->mlUrl(false);5 points
-
If you helped me here in the forums, thank you. This site has undergone a total re-make from ANO CMS to PW. 'Soon' I'll convert it to HTML5 but for now this was enough work. http://33sigs.org/3 points
-
It's common sense to name things based on meaning -- search, sitemap, home, basic-page / default etc. -- but I'm interested in hearing if you folks are defining / following more specific rules than these. This is something I've been thinking quite a bit lately and unfortunately I feel a bit lost here. IMHO this becomes an important thing especially when you have multiple developers working on / providing support for same projects, which they aren't necessary familiar with. It's one of those things that make it easier for a new developer to jump on board of a project and instantly understand what's happening under the hood (or at least make educated guesses.) So after this (longish) explanation, I'd really love to hear what you think about this subject -- what kind of naming conventions do you apply for your templates and/or fields? (If any.)2 points
-
For all you star wars fanatics: http://www.amazon.com/Darth-Vader-Star-Wars-Chronicle/dp/145210655X Arrived today and me and my 5 year old son both love it Please feel free to share other great stuff here.1 point
-
Hello everyone! I'm just about finished developing my site on my localhost (MAMP) with ProcessWire and I'm wondering what I need to do to migrate my site off my local server onto my hosted server. I want to make sure I know all the steps before I start in case I miss something. The database name has changed as the server automatically names databases and users—they differ from ones I've originally created—so I'll need to change any instances of the database name and user. I'm guessing I modify the config.php file to match the new credentials. Is that it? Are there anymore instances I need to worry about, e.g. in the SQL file? Other than modifying the config.php file, re-uploading all the files and importing the SQL file, is there anything that I'm missing? From what I've read in other posts, it seems that migrating ProcessWire should be easier than WordPress. I just wanted to confirm steps before I start in case I run into problems later on for doing it wrong now. Thank you for your help! Tina1 point
-
The current core date/time fieldtype/inputfield in ProcessWire has been a little weak, and there are some issues when it comes to using the datepicker. I've been working on fixing all of these and really upgrading the datetime fieldtype and inputfield to be much stronger. However, I need some help testing before a commit to the core source. I would just put it on a dev branch, but know that not many people will try it that way, so figured this would be better to post here. The file is attached. To install, you'd replace the existing files... /wire/modules/Fieldtype/FieldtypeDatetime.module /wire/modules/Inputfield/InputfieldDatetime/ (entire directory) ...with the new ones here: <attachment removed> Here's what's new: You can now select from predefined date and/or time formats, rather than having to manually construct a PHP date format (though you can still do that too if you want to). It converts your date format to the equivalent javascript format, so when you select a date from the datepicker, it stays in the right format rather than changing to a YYYY-MM-DD format like that old one did. No more conversion issues with day-first vs. month-first dates and the datepicker. There is now a time picker too! If you opt to include a time component, the datepicker will include a timepicker. You can now choose when you want your datepicker to appear: on click (as before), on focus (new), or inline (new, always visible). You can now optionally specify alternate date/time output and input formats on a per-language basis. So if you need month-first dates for US users, and day-first dates for European users, no problem. For the date output format, you can use PHP strftime() codes if you want to. This enables you to have one date format that outputs language-specific/localized month or day names. Though if you don't need to output localized month/day names, then it's better to stick with the default date formats. If anyone gets a chance to try it, please let me know how it works for you and if you run into any issues. There is a lot of new and somewhat complex code in here, so it needs a good testing. I've been testing here, but know with something like this there are some scenarios I could miss. Once we've confirmed all is stable, I'll commit this to the core. Thanks, Ryan Date/time picker example: Fieldtype configuration example: Inputfield configuration example:1 point
-
Absolutely right. I tend to try for 'self-documenting' code, in other words (exactly as you say), naming things based on meaning. This is for templates, fields and other variables. What I also do is use includes in templates for common page elements (which I am sure everyone does) and name them meaningfully. Also grouping template files by purpose is useful. For example, I'll name my includes inc.head.php, inc.foot.php etc, so they are grouped together in any list or file selector. While I was writing, I see apeisa got in before me! I also try to keep things in English, but even for a native English speaker sometimes you wouldn't think I was!1 point
-
Well.. after template specific field settings the need for new fields have been much less and it is easier to use more generic field names. My usual projects do have all or some of these for example: image images file files summary body date datetime tags (page field) I also have some common template names used in many projects: basic-page home members-home events event news news-item I try to use what is given on demo template. Also, trying to keep all the field and template names In English. Nothing too special here, interested to hear about others.1 point
-
1. That behavior is only useful when doing something like $page->get("headline|title"); where you'd like the headline, but will settle for the title if there's no headline. Literally saying I'll take headline OR title, but I prefer headline." That behavior works there because we're not talking about retrieving pages, we're just talking about retrieving the first non-empty value. Order matters here because we're stating a preference for headline since it is mentioned first. 2. When you use OR "|" in a selector to retrieve pages, you can use "|" between field names, like "title|body*=something". That literally translates to "Find all pages that have the word 'something' in either the title OR body. Order here means nothing, because if it matches in either title or body, you are going to get the page in the results either way. It may match the word 'something' in the title field on some pages, and in the body field on others. 3. You can also use OR "|" in the selector value, like "body*=this|that". That translates to "Find all pages that have the word 'this' OR 'that' in the body field. Some of the returned pages will match 'this' and some will match 'that'. It doesn't matter what the order is either (I'm not sure how it could). Getting back to this: "modified_users_id|created_users_id=1020" -- that translates to exactly the same thing as #2 above, as it should. Literally meaning "Find all pages that were either modified OR created by user 123". When I execute that find on my machine, here is what I get: page: 1, created_user_id: 1020, modified_user_id: 2 page: 4459, created_user_id: 1020, modified_user_id: 41 page: 4601, created_user_id: 41, modified_user_id: 1020 page: 4602, created_user_id: 1020, modified_user_id: 1020 page: 4615, created_user_id: 1020, modified_user_id: 41 page: 6674, created_user_id: 1020, modified_user_id: 40 page: 6683, created_user_id: 1020, modified_user_id: 41 page: 6705, created_user_id: 1020, modified_user_id: 1020 This is the expected behavior. At least, it is on my machine. Before yesterday (or if you haven't replaced your PageFinder.php file with the one attached above), you would have gotten this: page: 1, created_user_id: 1020, modified_user_id: 2 page: 4459, created_user_id: 1020, modified_user_id: 41 page: 4602, created_user_id: 1020, modified_user_id: 1020 page: 4615, created_user_id: 1020, modified_user_id: 41 page: 6674, created_user_id: 1020, modified_user_id: 40 page: 6683, created_user_id: 1020, modified_user_id: 41 page: 6705, created_user_id: 1020, modified_user_id: 1020 Note it would only catch the 'created_user_id' part (or whichever you specified first), because fields native to the 'pages' table didn't consider ORs in the fields part of the selector. If you are getting something other than the expected result, can you post the selector you are using and the list of matching pages like above? Here's the code I used to do it if it helps: $results = $pages->find("created_users_id|modified_users_id=1020"); echo "<ul>"; foreach($results as $result) { echo "<li>page: {$result->id}, created_user_id: {$result->created_users_id}, modified_user_id: {$result->modified_users_id}</li>"; } echo "</ul>"; Also just to reiterate, this will only work if you are running the latest PW and have installed the PageFinder.php file attached above.1 point
-
I had need for multiple feeds and it seemed to be pretty straightforward implementation. Only few modifications to load method: public function load($url) { $this->items = new WireArray(); if (is_array($url)) { $items = array(); foreach ($url as $feed) { $xmlData = $this->loadXmlData($feed); $xml = simplexml_load_string($xmlData); $items = array_merge($items, $xml->xpath('/rss//item')); } $rss = simplexml_load_string($xmlData); } else { $xmlData = $this->loadXmlData($url); $rss = simplexml_load_string($xmlData); } if(!$rss) { $msg = "Unable to load RSS feed at " . htmlentities($url) . ": \n"; foreach(libxml_get_errors() as $error) $msg .= $error . " \n"; $this->error($msg); return $this; } $this->channel['title'] = $this->cleanText((string) $rss->channel->title); $this->channel['description'] = $this->cleanText((string) $rss->channel->description); $this->channel['link'] = $this->cleanText((string) $rss->channel->link); $this->channel['created'] = strtotime((string) $rss->channel->pubDate); $this->channel['pubDate'] = date($this->options['dateFormat'], $this->channel['created']); $n = 0; // If we already have $items set, it means we are dealing with multiple sources. Let's sort them if(isset($items)) { usort($items, function ($x, $y) { return strtotime($y->pubDate) - strtotime($x->pubDate); }); } else { $items = $rss->channel->item; } foreach($items as $item) { $a = new MarkupLoadRSSItem(); foreach($item as $key => $value) { $value = (string) $value; if($key == 'pubDate') { $value = strtotime($value); $a->set('created', $value); $value = date($this->options['dateFormat'], $value); } else { $value = $this->cleanText($value); } $a->set($key, $value); } $this->items->add($a); if(++$n >= $this->options['limit']) break; } return $this; } What it does it sniffs if $url is array, then loads/caches all those and merge their rss-items to $items array. Then later on that $items is sorted by pubDate. So this is fully backwards compatible => just give it an array instead of single url if you need to parse multiple feeds. If you guys can test it works for you too then maybe Ryan you can put this on your version. I can do pull request if you want to (although it seems that new and fancy GitHub for windows does mess up line endings..).1 point
-
Thanks for the question @tinacious and answers @all, it's a reminder for me to do this stuff before I go live, can't have too many of them... Thanks for the list @formmailer, the only two I would add are: don't forget that you can have two config files, one to control your local (MAMP XAMP etc) environment (inc' db credentials etc) and another for the live/remote, see Maintaining a separate development configuration file. This is not really a thing to do at go-live time, but seemed worth noting in this context once you have two config files, for your local file only, enable "$config->debug = true;" — I have just started doing this and already it uncovered something slightly dirty in my PHP for me to tidy up. This ability to maintain local and remote configs is for me yet another differentiator for PW where something small yet really useful in the real-world has made it into production, a testament to the thought and care gone in to this product. PS: UPDATE: I just remembered that assuming you use the above dual config files setup you need to configure your FTP client to not transfer a file called config-dev.php or the remote env' thinks it is a dev env I just tripped myself over with that.1 point
-
Sorry for spamming this thread, but I wanted to say that this problem sorted itself out in a strange way. I installed a custom admin theme, then it didn't work either. Then I deleted the site/templates-admin folder via ftp. while it was deleting I refreshed the page, got some errors.. kept refreshing it until the folder was gone. Then, magically, the default theme worked just fine. very strange, but I'm glad I got it out of that funky state. Thanks all for your help.1 point
-
Coincidentally, I started looking at this last night and found these... .htaccess to nginx rules convertor Wordpress performance server I hope to try PW out on nginx in a virtual machine tonight.1 point
-
Alan, none of the updates should require running an install script. PW keeps track of an internal system version for things like DB modifications. If it detects that the system version has changed, it runs it's own install script behind the scenes on the first request after it detects it. For instance, a DB modification took place about a month or so ago, but I don't think anyone knew it.1 point
-
Thanks Ryan, I will take a look at that module. I think your right about the name thing. I was thinking from a security perspective though. There is a limited amount of space, so I don't want to give the impression that anyone who finds it can enter their name, so It's going to be checking against a database of invited guests. If we do the names, then people will (with some trial/error) be able to see who is on the list. I guess this isn't some high security weapons locker though . Yeah I certainly want to capture guestbook entries when people RSVP, simply because it will make it easy and I think there will be more responses. Thanks for the advice!1 point
-
put a checkbox on the main page template (it's more efficient since you don't even have to call the other page) and do this: <?php if($page->checkbox){ $item = $pages->get("template=upcoming-exhibitions"); echo "<h2>{$item->title}</h2>"; echo "<img src='{$item->exhibition_image->size(100,100)->url}' alt='$item->description'/>"; echo "<p>{$item->exhibition_summary}</p>"; } ?> Or, if you still prefer to have the checkbox on the exhibitions page: <?php $item = $pages->get("template=upcoming-exhibitions"); if($item->checkbox){ echo "<h2>{$item->title}</h2>"; echo "<img src='{$item->exhibition_image->size(100,100)->url}' alt='$item->description'/>"; echo "<p>{$item->exhibition_summary}</p>"; } ?>1 point
-
@MadeMyDay are you still struggling with this? I came up with a solution that uses url segments... of course you would loose this functionality on your templates... You would only have to create an empty page for each language and assign it this template: <?php function toSlug($str) { $str = strtolower(trim($str)); $str = preg_replace('/[^a-z0-9-]/', '-', $str); $str = preg_replace('/-+/', "-", $str); return $str; } $lang = $page->name; $user->language = $languages->get($lang); $basePage = $pages->get("/"); $segments = $input->urlSegments; if($segments){ $page = $basePage; foreach($segments as $segment){ $children = $page->children; foreach ($children as $child){ $found = false; if($segment==toSlug($child->title)){ $page = $child; $found = true; break; } } if (!$found) throw new Wire404Exception(); } } else { $page = $basePage; } include("./{$page->template}.php"); This is all it does: Changes the language to the name of this page (page must have same name as correspondent language) For each url segment, it converts the title to a slug format, and looks for the children with the same name. On the last segment it changes the $page object to the correspondent page object. If no page is found, throws a 404. Includes the template from the original page. Maybe you could combine this with Ryan's solution for the URLs1 point
-
Hi Tina, Your steps cover most of the process. A few notes: Make sure that the files/folders in the assets folder are writable Make sure to copy the the .htaccess file to the root of your website (some FTP programs hide this file by default) I prefer to delete the contents of the /site/assets/cache folder, but I am not sure if it's needed. You can do this before uploading the site, just to safe time. If you are going to use a new sql database, you can change the name and the user in the site/config.php file. For future use, for example if you want to copy the production database to your local mamp installation, make sure you add "drop tables" to your export, otherwise you will get errors about existing tables. That's all I can think of now. And if something should go wrong.... these forums are here to help you. 8) /Jasper1 point