-
Posts
17,232 -
Joined
-
Days Won
1,700
Everything posted by ryan
-
I love the forum software here, but agree the code bug is making me a little insane. I'm tempted to try and go in the code and fix it myself, but wouldn't know where to start. If IP.Board would just replace their rich text editor, I think they'd have a near perfect product. The rich text editor bugs in IP.Board only seem to mutate and evolve… they fix one thing, and break another. But I kind of understand, as TinyMCE in ProcessWire is no different in this regard… fix one thing, break another, rinse, repeat. Rich text editors are just plain complicated and evil (though a necessary evil). But the world would be a better place if we all used lightweight markup languages instead. The only other consistent problem I run into is that I my "View New Content" button (that I depend on) often completely misses some things. And I've lately had an issue with PMs showing up in my list, days after they were sent. Though I'm also getting old and sometimes wonder if I'm the one missing things, as the IPB behavior seems very random. So if anyone has ever been expecting a reply from me and didn't get it (whether in the forum or PM), let me know (email ryan at this domain), as it's very possible I never saw it.
-
This all makes sense, I'll plan to get the closest() method added. The only thing I'm having a little trouble with is the term "closest", as to me it would imply not just parents, but siblings, children, etc. I realize it's just referring to parents in jQuery, but still question their choice of the term. Would it not be better for us just to support a $selector argument on the parent() method, like $page->parent($selector)? Seems like it would do exactly the same thing, but maybe be a little more readable? Maybe I just haven't used this particular method in jQuery enough. But I like the goal behind the closest() method, regardless of what we call it. I tend to use rootParent quite a lot in my own sites, for any number of things. But then again, all my sites are structured in a manner that the first level pages are always the main section pages… and that never changes. So rootParent and me are good friends.
-
Great ideas. Anyone want to do a pull request for this? Either way, I'll plan to get these updates in 2.3.
-
Is this what you think happened in the case you mentioned? I can't say as though I've witnessed that behavior, but let me know if you are able to reproduce the redirect loop--it seems plausible in the situation you mentioned. 301s are useful, but 302s can also be dangerous if used in the wrong place. I was stating this just to clarify why 301 is the default behavior of the $session->redirect function, and why you have to specify a second bool param if you specifically want a 302.
-
I'd be surprised if your IP is changing that often on a cable/DSL setup. If it were going through some wireless service, then it might be more likely to change often. Session fingerprinting also looks at the user agent string... is it possible anything is changing in that? (some browser plugin changing it or something)?
-
If you are in control of IT, why are you even using IE??
-
Repeaters are still pages, but I agree with what you say: content that needs to be moved around a lot will be more accessible outside of a repeater.
-
Can you clarify what you mean by add your extension to the "~"? I'm not sureI understand.
-
What might be good is if we just add $selector support to the $page->parents() function so that you could do: $page->parents($selector). Though, keeping consistency with jQuery, adding closest() might make sense as it returns a single item rather than an array (as parents() would). I'd never used closest() in jQuery before, but this page describes the difference (scroll down a bit): http://api.jquery.com/closest/ … I think it seems like a good idea.
-
This nearly always points to the server not reading the .htaccess file or not having Apache's RewriteEngine. Try adding some random characters to the very top of your .htaccess file, like "hello321" and save. Try to load your homepage. If you don't get a "500 server error", then the server is not reading your .htaccess file. I think this is the most likely scenario and if you find it to be the case, you'll need to ask the web host to enable .htaccess support. Let us know what you find?
-
We've never seen any negative side effects from using 301s, so that became the default behavior of $session->redirect(). Whereas, there can be negative side effects from using 302s on the front-end of your site (at least with regards to Google). Even if that reason doesn't matter on the admin side, most redirects that PW does internally I would still call fitting the permanent designation, semantically. Though if a browser/software did start applying that literally to just a URL in our context (as opposed to a URL+POST) then we'd be forced to use 302s in some instances. This one might make more sense as a 302, I agree. I've not seen this before, but please let us know what you find.
-
Progress on ProcessWire 2.2 and overview of multi-language support
ryan replied to ryan's topic in Multi-Language Support
Good ideas Ralf and Slkwrm. I've added a Language Packs link to the main ProcessWire download page. We may go further with some of these ideas when there is time too. -
If you stick with the way you are doing it now, giving each checkbox field it's own 'name' attribute, then you would just need to update your $form array to include them, like the additional 'Hats' entry below: $form = array( 'contact_name' => $sanitizer->text($input->post->contact_name), 'email' => $sanitizer->email($input->post->email), 'comments' => $sanitizer->textarea($input->post->comments), 'Hats' => ($input->post->Hats ? 'Yes' : 'No') ); Your <input> tag should also read like this: $checked = array(); $checked['Hats'] = ($form['hats'] === 'Yes' ? 'checked' : ''); … <input type='checkbox' id='Hats' name='Hats' value='1' $checked[Hats]> However, I think a better way to approach all of the above is to use just one $products variable to account for all of them. $products = array('Hats', 'Gloves', 'Mens', 'Bags', 'Scarves', 'Clothing'); $form = array( 'contact_name' => $sanitizer->text($input->post->contact_name), 'email' => $sanitizer->email($input->post->email), 'comments' => $sanitizer->textarea($input->post->comments), 'products' => array() // make it an empty array ); // sanitize like this if($input->post->products) foreach($input->post->products as $product) { // here we are just ensuring submitted products are in fact valid if(in_array($product, $products)) $form['products'][] = $product; // add product } // output the checkboxes like this: foreach($products as $product) { $checked = in_array($product, $form['products']) ? "checked" : ''; echo "<label><input type='checkbox' name='products[]' value='$product' $checked> $product</label><br>"; } The above may look longer, but keep in mind it's replacing the entire checkboxes input and output, while giving you more flexibility. Adding a new product is just a matter of adding it to the $products array at the top. When you generate your email, you'll want to detect the array so that the email has the product names rather than the text "array()": foreach($form as $key => $value) { if($key == 'products') $value = implode(', ', $value); // make it a CSV string $message .= "$key: $value\n"; }
-
If you clone the page with the repeater, it should make a copy of the repeater items as well. But if you need to take that repeater and literally put it on another page, then ask yourself: is this a one-time thing, or something I need to do multiple times? If multiple times, then consider adding a page reference field so that one page can opt to choose the repeaters from another, rather than maintaining duplicate entries across pages. But if it's just a one-time thing, then I don't have an easy answer outside of using the API to do it.
-
Using your existing example, you could do it this way, which is a little simpler and less code. Basically you'd just add your own 'totalPages' temporary variable to each category… and use that in your output. That way you don't have to retrieve those categories any more than once (it would execute faster too). $categories = $pages->find("template=kom_catlanding"); foreach($categories as $category) { // populate your own made-up variable: totalPages $category->totalPages = $pages->count("template=kom_project, project_services=$category"); } echo "<ul id='catcloud'>"; foreach($categories as $category) { echo "<li><a href='$category->url' class='$category->totalPages'>$category->title</a></li> } echo "</ul>"; If you wanted to sort them by count or something, you could do this: $categories->sort('-totalPages'); // sort most to least
-
That sounds like one possible way to do it. But maybe not ideal unless you are already having them fill these things out on the front-end. You could always create a custom Fieldtype/Inputfield designed for this specific purpose. But if you are okay with the information being automatically populated when the page is saved, you could pretty easily create a module to do it for you. Such a module hooks before Pages::save() and looks for pages having the expected fields, then automatically populates the required value, before the page is saved. Let me know if I can describe further or give an example?
-
I'm not familiar with this architecture, but wanted to mention that ProcessWire doesn't need DB create privileges. It just needs for you to specify what DB it should use (one that is already created by someone with privileges to do so). If that behavior is annoying, you can also disable it by uninstalling the SessionLoginThrottle module. 180 second timeout is really short: 3 minutes. This could be the entire problem right there. I would suggest bumping that up to 3600 (1 hour). But if that's not it, try these: Try editing your /site/config.php and changing the $config->sessionFingerprint line to false. If that doesn't fix it, try adding this line to your /site/config.php: $config->protectCSRF = false; The only other possibility I can think of is if there isn't enough disk space... or something weird with the cookie domain? (switching between hostnames on same site?). But I'm really thinking this has to do with the 180 second timeout. You may think that's enough, but there is some randomness in how PHP's garbage collection of sessions works, and what their GC settings on the server are and maybe (?) that's causing some unexpected/random behavior here.
-
It's not clear to me whether you are intending to get 'FieldtypeModules' (as your code says) or 'FieldtypeTemplates' (as your text says), but I don't think that either is what you want. Though you are probably getting the error because those are 3rd party modules that aren't installed by default. If your module needed a 3rd party module installed, then you'd want to have it called out in the 'requires' section of your getModuleInfo() function. But don't think about that much now, because FieldtypeModules/FieldtypeTemplates is not what you want here. Fieldtypes are only applicable to handling data with pages. In this case, you are building a collection of Inputfields to hold your module's configuration data. So what I think you are wanting is something like this: if(!isset($data['mailChimpTemplates'])) $data['mailChimpTemplates'] = array(); // default value $field = $modules->get('InputfieldAsmSelect'); // or InputfieldCheckboxes? $field->name = 'mailChimpTemplates'; $field->label = 'Select what templates to use'; foreach(wire('templates') as $template) { $field->addOption($template->id, $template->name); } $field->value = $data['mailChimpTemplates']; $inputfields->add($field); During your module's regular execution, when you want to access your mailChipTemplates variable, you'll just want to remember that it is holding template IDs rather than template objects. If you wanted to make it hold template objects instead, you could put this in your init() function: $templates = $this->mailChimpTemplates; foreach($templates as $key => $id) { $templates[$key] = wire('templates')->get($id); } $this->mailChimpTemplates = $templates;
-
Thanks for testing guys. I merged all the dev stuff into the master branch yesterday and it's now 2.2.5
-
Reading and displaying data from a custom table
ryan replied to einsteinsboi's topic in General Support
Based on what I'm understanding from your last message, I think you should skip keeping the separate table. It just sounds like extra, unnecessary work, unless there's something more to this project that I don't yet understand. Instead, I think you should have your cron job execute a script that bootstraps ProcessWire and takes care of all the adding, updating and deleting of records consistent with the web service you are reading from. This is something that I think ProcessWire is particularly good at, because it's been designed for this from the beginning (it's something I have to do with a lot of my client work). Whether XML or JSON doesn't matter much, as PHP includes the ability to read from either type quite easily. Though like the other guys here, I generally prefer JSON just because it's less verbose and less fuss. If JSON, you'll pull the feed and use PHP's json_decode() to convert it to an array. If XML, you'll use PHP's SimpleXML to convert it to an array. Once you've got the array of raw data, you'll iterate through it and add, update, or delete pages in ProcessWire to make it consistent with the data you are pulling from the web service. Live, working example I think that the best way to demonstrate it is with a live, working example. This one uses the existing modules.processwire.com/export-json/ feed. You might also want to see the feed in human-readable mode to get a better look at the format. Below is a shell script that bootstraps ProcessWire, reads from that feed and maintains a mini "modules directory" site, on your own site. I made this feed so that it can be tested and used on a brand new installation using the basic profile (included with PW). If left how it is, it'll create a mini modules directory site below the '/about/what/' page and use the template 'basic-page' for any pages it adds. But you can run this on any ProcessWire installation by just editing the script and changing the parent from '/about/what/' to something else, and changing the template from 'basic-page' to something else, if necessary. This script assumes that the template used has 3 fields: title, body, and summary. The 'basic-page' template in PW's default profile already has these. If you adapt this for your own use, you'd probably want to change it to use more specific fields consistent with what you need to store on your pages. In this example, I'm just building a 'body' field with some combined data in it, but that's just to minimize the amount of setup necessary for you or others to test this… The purpose is that this is something you can easily run in the default profile without adding any new templates, fields, pages, etc. 1. Paste the following script into the file import-json.php (or download the attachment below). For testing purposes, just put it in the same directory where you have ProcessWire installed. (If you place it elsewhere, update the include("./index.php"); line at the top to load ProcessWire's index.php file). 2. Edit the import-json.php file and update the first line: "#!/usr/bin/php", to point to where you have PHP installed (if not /usr/bin/php). Save. 3. Make the file executable as a shell script: chmod +x ./import-json.php 4. Run the file at the command line by typing "./import-json.php" and hit enter. It should create about 95 or so pages under /about/what/. Take a look at them. Run it again, and you'll find it reports no changes. Try making some changes to the text on 1 or 2 of the pages it added and run it again, it should update them. Try deleting some of it's pages, and it should add them back. Try adding some pages below /about/what/ on your own, run it again, and it should delete them. import-json.php #!/usr/bin/php <?php // replace the path in the shabang line above with the path to your PHP // bootstrap ProcessWire. Update the path in the include if this script is not in the same dir include("./index.php"); // if you want to run this as a PW page/template instead, remove everything above (except the PHP tag) // save our start time, so we can find which pages should be removed $started = time(); // keep track of how many changes we've made so we can report at the end $numChanged = 0; $numAdded = 0; $numTrashed = 0; // URL to our web service data $url = 'http://modules.processwire.com/export-json/?apikey=pw223&limit=100'; // get the data and decode it to an array $data = json_decode(file_get_contents($url), true); // if we couldn't load the data, then abort if(!$data || $data['status'] != 'success') throw new WireException("Can't load data from $url"); // the parent page of our items: /about/what/ is a page from the basic profile // update this to be whatever parent you want it to populate... $parent = wire('pages')->get('/about/what/'); if(!$parent->id) throw new WireException("Parent page does not exist"); // iterate each item in the feed and create or update pages with the data foreach($data['items'] as $item) { // see if we already have this item $page = $parent->child("name=$item[name]"); // if we don't have this item already then create it if(!$page->id) { $page = new Page(); $page->parent = $parent; $page->template = 'basic-page'; // template new pages should use $page->name = $item['name']; echo "\nAdding new page: $item[name]"; $numAdded++; } // now populate our page fields from data in the feed $page->of(false); // ensure output formatting is off $page->title = $item['title']; $page->summary = $item['summary']; // To keep it simple, we'll just populate our $page->body field with some combined // data from the feed. Outside of this example context, you'd probably want to // populate separate fields that you'd created on the page's template. $body = "<h2>$item[summary]</h2>"; $body .= "<p>Version: $item[module_version]</p>"; foreach($item['categories'] as $category) $body .= "<p>Category: $category[title]</p>"; $body .= "<p><a href='$item[download_url]'>Download</a> / <a href='$item[url]'>More Details</a></p>"; $page->body = $body; // print what changed $changes = $page->getChanges(); if(count($changes)) { $numChanged++; foreach($changes as $change) echo "\nUpdated '$change' on page: $page->name"; } // save the page $page->save(); } // now find pages that were not updated above, which indicates they // weren't in the feed and should probably be trashed $expired = $parent->children("modified<$started"); foreach($expired as $page) { echo "\nTrashing expired page: $page->name"; $page->trash(); // move to trash $numTrashed++; } echo "\n\n$numAdded page(s) were added"; echo "\n$numChanged page(s) were changed"; echo "\n$numTrashed page(s) were trashed\n"; import-json.php.txt Running the script as a cron job: You can instruct your cron job to run the script and it should be ready to go. You may want to move it to a non web accessible location for more permanent use. You'll also want to update your bootstrap "include()" line at the top to have the full path to your ProcessWire index.php file, as your cron job probably isn't executing it from the web root dir like you were manually. Running the script as a template file: You can run this script as a template file on a page by removing the include() line and everything above it with this line: <pre><?php Place it in your /site/templates/ directory, add the template from PW admin, and create a page that uses it, then view it.- 15 replies
-
- 12
-
-
-
When you get a chance, and when you feel its ready, it'd be great to get it posted in the modules directory too: http://modules.processwire.com/add/
-
How much SEO value to photos have anyway? I would think the SEO value would come from the descriptions of those photos, not the photos themselves. So by alternate links and/or sitemap, I was thinking something like this: <li><a href='/path/to/photo.jpg'>Description of photo</a></li> or <li><a href='/path/to/page/photo-jpg'>Description of photo</a></li> In the second example, 'photo-jpg' would be a URL segment and it would instruct the page's template to just display the 1 photo having that name, and with description (in alt tag and/or in the content area).
-
Reading and displaying data from a custom table
ryan replied to einsteinsboi's topic in General Support
This sounds like a fun project! Does the data need to stay in this external table, or can it live in ProcessWire instead? If the data can live in ProcessWire, your cron job could very easily update the data by just bootstrapping ProcessWire. Bootstrapping is as simple as: include("/path/to/pw/index.php"); One-time importing data from an external source is also very easy to do via the API. So if you can do it, I would just let the data live in ProcessWire instead of an external database, and it'll make the whole job a piece of cake. But if that data needs to stay external, then Sinnut's solution is a good way to go. You would use the DB's primary key (or some other unique column) to serve as the urlSegment that loads the page. You'd setup one page/template to handle all that data, and it would find it like this: $key = (int) $input->urlSegment1; if(!$key) throw new Wire404Exception(); $result = $yourDB->query("SELECT make, model, year FROM your_table WHERE id=$key"); if(!$item->num_rows) throw new Wire404Exception(); list($make, $model, $year) = $result->fetch_row(); echo "<ul>"; echo "<li>Make: $make</li>"; echo "<li>Model: $model</li>"; echo "<li>Year: $year</li>"; echo "</ul>"; Note: enable URL segments on the template where this code is (on the URLs tab), as URL segments are not enabled by default. This is another reason why it may make a lot of sense to keep all the data in ProcessWire. But if you can't do that, it won't be a problem: all you need is for your pages to contain a reference to the primary key of row they map to in your external table. I would suggest using the built-in "name" field for those pages to map to the primary key in your external table, because you know it'll be unique and relevant. But you could always create a separate integer or text field in ProcessWire do handle it too. But lets say you use 'name', then your page templates could load the data like this: $result = $yourDB->query("SELECT make, model,year FROM your_table WHERE id='{$page->name}'"); -
That's correct, if you want to find pages by a field called cats, then you'd use "cats=" instead of "id=". To find pages matching all the categories, specify multiple "cats=" statements (rather than 1 split by "|" OR bars). So the code example you posted is correct.
-
Nested repeaters may be technically possible, but the potential overhead would be huge and it seems like all sorts of possible complications could arise. So definitely not recommended. You are right that I should remove the capability to do it--I've just made this change and will be committed shortly.