Jump to content

ryan

Administrators
  • Posts

    17,307
  • Joined

  • Days Won

    1,725

Everything posted by ryan

  1. Take a look at this post from awhile back that documents one way to approach it: http://processwire.com/talk/index.php/topic,17.0.html There are also some changes in progress for the user system that may be worthwhile for your need as well. The only concern I have in your instance is the "clients can register" part. That means that you'll have to implement some extra security measures to ensure you don't get millions of automated spammer accounts. But I can help you through that. Whereas if you are creating the account and password, you don't have to worry about that aspect.
  2. Are you doing this from the admin or from the API? If from the API, what it's telling you is that you need to populate $page->name before saving it. But it sounds like you are in the admin, so it is a strange error to see when adding a page in the admin. What is the name you trying to give the page? The only instance I can think of where you might get this error is if the page name you are trying to use is a reserved word. Also you mentioned some other problems - Can you describe them further? If there is something environment specific, the more we know, the better chance we can track down what's going in in this environment. Since the installer couldn't detect the server type or mod_rewrite, that indicates potential incompatibility. A link to phpinfo may be helpful too (feel free to PM it to me if you prefer). Thanks, Ryan
  3. Not yet positive about the actual format of the file, but the two you mentioned are likely candidates. Though the files may end up being used just for importing/exporting, and DB used for storage. Regardless of the file format, we're going to make them editable in the admin so that the format won't matter too much.
  4. The structure is designed to support an auto update, so that this module can be written at some point in the future. The way it would work is that it would grab the latest zip (like from GitHub), then it would rename your /wire/ dir to /.wire.[version]/, and move the /wire/ dir from the zip into the location of the old one. At the same time, it would also replace the /index.php and /.htaccess files (unless you wanted to do that part manually). Lastly, it would run an update.php file from the zip to perform any necessary DB updates (if there were any). On most servers, we can't assume that the /wire/ dir is writable, so it would likely unzip the latest PW version into a writable dir in /site/assets/, and then FTP into itself to rename and replace the /wire/ dir with the new one. I believe this is the same method that the Wordpress automatic update uses.
  5. The way we solve it currently is to not actually have a /site/ dir in the repo, but instead have a /site-default/ dir. That way when you do a "git pull" to update your installation, it should leave your /site/ dir alone (which is in the .gitignore). That's the intention anyway. (I'm also new to git). Granted, PW will create a new /site-default/ dir, which you would then want to remove after an upgrade (or add it to your .gitignore so that it doesn't get pulled). We could certainly have a /wire/ only repo too, but the /index.php and /htaccess.txt files are an important part of each version, even though they can't be in /wire/. I'm not sure how to handle that... though maybe that would just be a disclaimer for using the /wire/ repo.
  6. Great update! Thanks for your work on this. I did run into one minor issue, and that's if the site is installed in a subdirectory, the redirect doesn't work. The reason is that it attempts to do the redirect to page's path without the subdirectory it's installed in. I just submitted a pull request to you with a possible solution. It converts PW urls to their page ID for storage purposes. This solution also ensures that redirects aren't broken if the redirect_to page is moved in PW. It only does this with redirect_to URLs that resolve to pages... it leaves all the others alone.
  7. Apeisa's example is correct. Thanks, Ryan
  8. Here's how to implement the page picker in your Redirects module. First, give your redirect_to field an id attribute in addition to the name attribute it already has. So replace this: $field->name = "redirect_to"; with this: $field->name = "redirect_to"; $field->id = "redirect_to"; Now add a new field to your form, right after the redirect_to field: <?php $field = $this->modules->get("InputfieldPageListSelect"); $field->label = 'Select Page to Redirect to'; $field->attr('id+name', 'select_page'); $field->startLabel = "Select Page"; $field->parent_id = 0; // tell it to start from homepage if(strpos($to, '/') === 0) { // if a value is already set, determine if it's a page // if it's a page, then populate the input with the page_id $page = $this->pages->get($to); if($page->id) $field->value = $page->id; } $form->add($field); // now add a script that makes it automatically populate the redirect_to field // with the URL of the selected page. $script = <<< _END <script type="text/javascript"> $("#select_page").bind('pageSelected', function(event, data) { if(data.url.length) $("#redirect_to").val(data.url); }); </script> _END; Lastly, at the bottom of your function, we want to include the $script in the output, so replace this: return $form->render(); with this: return $form->render() . $script; Let me know how that works? Thanks, Ryan
  9. Thanks Marcin! That's great -- I'll be glad to take you up on that. In about a month or so I hope to have a separate dev branch that includes the multi language support (at least the basics).
  10. Just tried it out and it works great! I will definitely use this, thanks for putting it together. Nice work! Two minor CSS suggestions (see attached screenshots showing before and after): /* adds padding at bottom so floated buttons don't touch footer */ #redirects_form { margin-bottom: 3em; } /* the margin-right addition ensures the button floats all the way right */ #submit_delete { float: right; margin-right: 0; } This is such a useful module, it makes me start thinking of what more could be added... any thoughts on future additions? A couple ideas are: ability to upload (or paste into a textarea) a CSV file with the "from, to" urls. A counter for each redirect that keeps track of how many times it's been used. Thanks, Ryan
  11. To uninstall something you want to do it through admin>modules. If the dir/files were removed without uninstalling, it's best to put them back where they were. It sounds like you may have already tried this? If so double check that the dirname and filenames are the same as before. If that still doesn't work, you can always remove the entry from the modules table manually, if you are sure it's not actively being used on any fields. Better yet, try deleting the modules cache file in site/assets/. I dont recall what the filename is (I'm not at computer) but I think the name is recognizable as being for modules. Please let me know how this works out.
  12. I'm not sure how I missed this before, but turns out we already have a hook that looks like it'll work. It's ProcessPageView::pageNotFound Here's one approach that could be used to handle the hook and redirect. This is coded in the browser/forum, so not tested and probably has parse errors, etc., but hopefully the general idea works. <?php public function init() { $this->addHook('ProcessPageView::pageNotFound', $this, 'checkRedirects'); } public function checkRedirects($event) { // determine the URL that wasn't found $url = $_SERVER['REQUEST_URI']; // if installed in a subdirectory, make $url relative to the directory ProcessWire is installed in if($this->config->urls->root != '/') { // you might need to double check that below results in a path with a slash still at beginning $url = substr($url, strlen($this->config->urls->root)-1); } // we'll check the DB against trailing slash version and non-trailing slash version // and escape them for the DB select while we are at it $url = $this->db->escape_string(rtrim($url, '/')); $url2 = $this->db->escape_string($url . '/'); // now see if it's in the DB $sql = "SELECT redirect_to FROM {$this->className} WHERE redirect_from='$url' OR redirect_from='$url2'"; $result = $this->db->query($sql); // if there is a match, then redirect to it if($result->num_rows) { list($redirect_to) = $result->fetch_array(); $this->session->redirect($redirect_to); } }
  13. Sorry I forgot something. Add this right after you get the $table from $modules: $table->setEncodeEntities(false); I recommend having ProcessRedirects live in it's own directory, like: /site/modules/ProcessRedirects/ProcessRedirects.module Then create another file in there called ProcessRedirects.css Use that CSS file to place the button element where you want. In your module file, you can give the button an id attribute so that it's easier to target in the css file: $button = $this->modules->get("InputfieldButton"); $button->id = 'submit_delete'; // assign an ID attribute $button->type = 'submit'; $button->value = 'Remove selected redirects'; Note that id attribute may actually be assigned to a containing element of the button (not positive). If it is, then you'd want to target in your CSS as "#submit_delete button" rather than just "#submit_delete". In your executeSave function, I recommend escaping the $from and $to vars before insertion into the DB. The reason is that there are some chars allowed in URLs that would need to be escaped for the database (at last I think there are). To do that, you'd just use PHP's mysqli escape_string function: $from = $this->db->escape_string($from); $to = $this->db->escape_string($to); I'm working on adding the hook and should have that this morning! Edit: One last thing is to add is "$count = 0;" at the top of your executeDelete function. Otherwise PHP will throw notice errors about an uninitialized variable when ProcessWire is in debug mode. Thanks, Ryan
  14. ProcessWire wasn't built to be a full-on MVC framework like CodeIgniter, and so most of what is used in developing the admin modules is fairly basic. Though I've never actually built anything in CodeIgniter, so I don't know all that it does specifically. I know CI maps URL segments to functions (like a Process module), but it also maps URL segments to function arguments (unlike a Process module). I didn't do that because I preferred to keep dynamic arguments as GET or POST vars. PW1 enforced MVC for processes, and enforced keeping everything in separate files. It had it's benefits, but in my case it was a net loss to time and maintenance compared to PW2's system. PW2 leaves it more up to you how much you want to enforce a pattern. I usually keep my views in the class, but under a function called "render()" ... that's stylistic only. You'll find that most Process modules call upon other modules to provide views, like the Markup and Inputfield modules. (i.e. MarkupAdminDataTable, InputfieldForm, InputfieldText, etc.). If you want to maintain a view in a separate file, use the TemplateFile class. It works much the same way that a view in CodeIgniter would work, and it's the same class that ProcessWire uses to render template files throughout the site. Though I don't actually do this in any of the Process modules, but here's how you would: public function ___execute() { $view = new TemplateFile($this->config->paths->MyModuleName . "view.php"); $view->set('videos', $this->getVideos()); return $view->render(); } ProcessWire assumes that all "execute" functions return the output that will ultimately be populated in the main part of the template. It includes a couple of helpers in the "fuel" for populating the headline (processHeadline) or breadcrumbs. These are optional -- if you omit these, the breadcrumb trail will be to the current page, and the headline will be the title field from the current page. Very often, this is what you want, so I don't bother with the headline or breadcrumbs until my Process module is up and running and I find it makes sense to add them. Any function that begins with 3 underscores, like "___execute()" is hookable. If you don't want it to be hookable, then just use the same function name, but without the 3 underscores, i.e. "execute()". ProcessWire doesn't care which version you use. When in doubt, make them hookable. For functions that you make hookable (by preceding with 3 underscores), any other module can add a hook either before or after it. Hooks that run 'before' can modify the function arguments sent to the hooked function. Hooks that run 'after' can modify the return values of the function. Beyond that, hooks can do whatever they want. See the /wire/modules/Helloworld.module for an example of a couple hooks. For more insight on Process modules, I would first look at the Module interface, defined in the /wire/core/Module.php file. This basically just lists the functions that all modules are required to have. Most are optional, so you'll see them documented, but commented out. As a result, this Module.php file is more for documentation purposes than anything else. Next look at the definition of a Process module in /wire/core/Process.php. You'll see it's pretty simple and short. It enforces an execute() function, which gets called on a request of /path/to/page/. Any others you create, i.e. executeEdit(), executeSave(), etc., it only calls if it finds "edit" or "save" in the URL. There are no predefined names after the "execute" part, so it could just as easily be "executeHello()" and that function would get called when the URL was "/path/to/page/hello/". The only other significant thing that Process module does for you is that it looks for a css and js file that follow the same name format (and are in the same dir) as your .module file: /site/modules/Process/ProcessVideos/ProcessVideos.module /site/modules/Process/ProcessVideos/ProcessVideos.css /site/modules/Process/ProcessVideos/ProcessVideos.js If it finds them, they get automatically included in the admin template's <head> section.
  15. I'd like to build a ready-to-go blog site profile in PW at some point. I think PW would lend itself very well to building a blog. But the need comes up so rarely in my own work, that I haven't done much other than experiment ... my site (ryancramer.com, in PW1) is loosely in a blog format, though I built that several years go and it's long past time for a redesign and redevelopment. On sites where the client has asked for a blog, I've used WordPress. ProcessWire and WordPress run alongside each other quite well. Then I'll use WordPress RSS feeds to pull blog entry headlines into ProcessWire so that they can be output anywhere on the site, so make it seem like it's all running together...
  16. Since it looks like you have some cached pages, another thing you might want to do when moving a site is to clear the cache. You can do it before or after, but probably do it before just so you are transferring fewer files. Go to Admin > Modules > Page Render > and click on the "clear cache" checkbox, and submit. This can be especially useful when moving a site from one path to another (like subdirectory to root), because cached pages might temporarily be looking for external assets like CSS and JS files in the wrong directory. So if you ever transfer a site and see your homepage show up with no stylesheets or images, go in and clear that cache and you'll be set. Another way to clear the cache is: rm -rf ./site/assets/cache/Page/* But I always get a little nervous with anything involving an "rm -rf" (one stray character could wipe out everything) so I usually do it from the admin.
  17. Technically modules are just a class that implements an interface. It doesn't have to extend any other class in order to implement that interface, but it does make life a little easier ... especially in the case of Process modules, because they have some flow control built in and know to look for specific CSS/JS files, etc. There isn't any requirement that you have to use Process modules in the admin. You can just as easily use any template of your own if you prefer. But if Process modules save work for what you need to do, then I think they are a good way to go. The ProcessPage modules are probably the broadest in scope of all of them. For most other instances, I'll usually integrate the List, Edit, Add, Delete all into a single Process module. In the case of ProcessPageEdit/Add/List, it was a lot of code so I split them up. But for your own needs, you may find you prefer keeping it to a single module, or splitting them up.. it doesn't matter. The main advantage of keeping them in a single module is that they are attached to a single page, so you don't have to worry about a multi-page dependency (not that it matters that much though). Now that I know people are going in and using these as examples to create others, it makes me think I need to go in there and make sure all my code is clean. That was fast! Glad that it worked out well. Though I think your previous approach was a good one too. But I also think that using pages will hopefully save you time and be more fun to work with. I'm in the process of doing the same thing that you did, but with the user system rather than videos. I'm converting the user/role/permissions system to use pages rather than WireSaveableItems, along with multi language support. From the user side, I'm doing it so that the system can accommodate any number of users (more scalable), and let you add custom fields to users, roles and permissions just like any other page template (more flexible), as well as reduce some redundancies on the multi language system. Sounds good, I look forward to seeing what you are doing. And of course, please let me know anytime I can be of any help with questions. You and a couple of other people are getting deeper into the code than what is currently in the documentation, and I'm here to help with any questions you have. Thanks, Ryan
  18. That's great to hear! Thanks for your message, this makes my day. That's impressive! Sounds like a great site, and that you've done a lot of great things with it. I'm always wanting to add new sites to this page, if you ever have any you want me to post there just let me know: http://processwire.com/about/sites/ There really isn't much in the way of a form builder right now, but it's something we've been talking about. I expect we'll be making progress on this in the summer (soon!), assuming someone else doesn't do it first. But multi-language support and a redesign of the user/role/permissions system are in progress now, and a form builder comes after that. This is relatively straightforward because pages in the Admin are just like any other pages on your site. You can add a page to the admin, and make it use any one of your templates. Internally, ProcessWire uses what are called "Process" modules to serve as a very basic framework for managing flow control on a given page (very loosely like CodeIgniter in terms of calling a function based on a URL segment). But it's not a requirement to use them. I've found that my clients relate well to the tree structure too. Or when they don't, I tell them to think of a family tree, and then they get it. Thanks for using ProcessWire and being part of the community here! I look forward to talking more. Ryan
  19. Looks like you've got a good start here! To create the table, I would do it in your install function. The only part of PW that creates tables are the fieldtypes, and those aren't useful here, so you'll want to issue the create table yourself. Something like this: <?php public function ___install() { parent::___install(); $p = new Page(); $p->template = $this->templates->get("admin"); $p->parent = $this->pages->get($this->config->adminRootPageID); $p->title = 'Redirects'; $p->name = 'redirects'; $p->process = $this; $p->save(); // hold off on the permissions if you can because this system is being redesigned. // $this->installPermission('Edit'); $sql = <<< _END CREATE TABLE {$this->className} ( id int unsigned NOT NULL auto_increment, redirect_from varchar(512) NOT NULL DEFAULT '', redirect_to varchar(512) NOT NULL DEFAULT '', UNIQUE KEY(redirect_from) ); _END; $this->db->query($sql); } public function ___uninstall() { $p = $this->pages->get('template=admin, name=redirects'); $p->delete(); $this->db->query("DROP TABLE {$this->className}"); } For your CRUD, I would make your default function (execute) the one that lists the redirects, and then create functions to edit, save and delete. Here's an example of how you might do some of it as a starting point (written in the browser, so not tested!): <?php /** * List Redirects - Called when the URL is: ./ * */ public function ___execute() { $this->setFuel('processHeadline', 'List Redirects'); // example here uses a PW module to output this table, but that's not required. $table = $this->modules->get("MarkupAdminDataTable"); $table->headerRow(array('Redirect From', 'Redirect To', 'Delete')); $result = $this->db->query("SELECT * FROM {$this->className} ORDER BY created"); while($row = $result->fetch_assoc()) { // output in table rows with edit link and delete checkbox? $table->row(array( $row['redirect_from'] => "edit/?id=$row[id]", $row['redirect_to'] => "edit/?id=$row[id]", "<input type='checkbox' name='delete[]' value='$row[id]' />" )); } $table->action(array('Add Redirect' => 'edit/?id=0')); return "<form action='./delete/' method='post'>" . $table->render() . "</form>"; } /** * Edit/Add Redirect - Called when the URL is: ./edit/ * */ public function ___executeEdit() { $this->fuel->breadcrumbs->add(new Breadcrumb('./', 'Redirects')); $id = (int) $this->input->get->id; if($id > 0) { // edit existing record $result = $this->db->query("SELECT id, redirect_from, redirect_to FROM {$this->className} WHERE id=$id"); list($id, $from, $to) = $result->fetch_array(); $this->setFuel('processHeadline', "Edit Redirect"); } else { // add new record $id = 0; $from = ''; $to = ''; $this->setFuel('processHeadline', "Add Redirect"); } $form = $this->modules->get("InputfieldForm"); $form->method = 'post'; $form->action = './save/'; $field = $this->modules->get("InputfieldHidden"); $field->name = 'id'; $field->value = $id; $form->add($field); // maybe use InputfieldURL for the 'redirect_from' and 'redirect_to' inputs return $form->render(); } /** * Save Redirect - Called when the URL is ./save/ * */ public function ___executeSave() { // perform save of existing record if id>0, or add new if id=0 $this->message("Saved Redirect"); $this->session->redirect("./"); // back to list } /** * Delete Redirect - Called when URL is ./delete/ * */ public function ___executeDelete() { if(!is_array($input->post->delete) || empty($input->post->delete)) { $this->message("Nothing to delete"); $this->session->redirect("./"); // back to list } foreach($input->post->delete as $id) { $id = (int) $id; $this->db->query("DELETE FROM {$this->className} WHERE id=$id"); } $this->message("Deleted " . count($input->post->delete) . " redirect(s)"); $this->session->redirect("./"); // back to list } I will work on setting up an appropriate hook in the ProcessPageView module. thanks, Ryan
  20. When replacing a site, I usually handle some of the 301 redirects in the .htaccess file: RewriteRule ^about.html$ /about/ [R=permanent,L] But a lot of these sites have thousands of URLs to replace. Very often, they are URLs with variables in them, like: www.site.com/products.cfm?product_id=8573 To handle that, I'll add a field in ProcessWire called old_product_id, and add it to the new "product" template. When importing the products into the new site, I make sure that old_product_id field gets populated with it's ID from the old site. If you are dealing with thousands of URLs, you want to make sure to automate this. Next, I'd either create a page in ProcessWire named "product.cfm" (along with a template), or create an actual file on the server called product.cfm. I'm more likely to create the file, since I don't really like pages from the old site in the new site tree. So I'd name that file /product.cfm and update the .htaccess file to run cfm files as php: AddType application/x-httpd-php .cfm Here's what the contents of product.cfm might look like: <?php // load ProcessWire's API require("./index.php"); // if no 'id' var present, send them to the new products page if(!isset($_GET['id'])) $wire->session->redirect("/products/"); // get the requested product id $id = abs((int) $_GET['id']); // find the new page with that product id $page = $wire->pages->get("old_product_id=$id"); // if it was found, redirect them to it. if($page->id) $wire->session->redirect($page->url); // otherwise send them to the new products index else $wire->session->redirect("/products/"); This is just one approach... but one I had to implement recently, so figured I'd paste it in as an example. The template-level redirects are great for future redirects that the client might setup. And it's easy to add a field called redirect_href and then setup a template that deals with it, i.e. /site/templates/redirect.php: <?php if($page->redirect_href) $session->redirect($page->redirect_href); Then your clients can select "redirect" as the template when they want to create a redirect. Though, they still might pollute the site tree over time with lots of extra and unnecessary redirect pages... I've seen it happen, and it's a problem. If you want a 302 redirect, specify 'false' as the second argument to the redirect function, i.e. $session->redirect($url, false). So you might add a checkbox field to the page's template called 'permanent', and handle it like this: $session->redirect($page->redirect_href, $page->permanent == 1); There was in PW1 but not yet in PW2. I think it would be a worthwhile module to have, especially for the post-launch redirects. If you are interested in building a module, I think it would be a great. I'll be happy to add any of the necessary hooks into the core or assist with anything you'd like me to. As for best route, I'm thinking I would add a hook before 'ProcessPageView::execute' that checks the value of $_GET['it']. That value is what PW's htaccess file uses to send PW the requested page URL. You could also look at any of the $_SERVER[] variables, to see if any of them suit your needs better, but I'm guessing $_GET['it'] is what you need. If your module finds a matching URL, it would redirect to it. Otherwise, it would just return, letting ProcessPageView continue on about it's way. A better solution might be to have your module hook into some function that only gets called when a page is not found. That way it wouldn't have to compare redirects for every request. I don't think I have a good function for you to hook into for that yet, but I will be happy to add one. One thing I should mention is that ProcessWire only sees URLs with the path/file containing ASCII these characters: -_.a-zA-Z0-9/ That's applicable to the path/file only, not the query string. Those characters accounts for most URLs you are likely to run across, but ProcessWire won't ever see URLs with commas or other more rare characters that are technically allowed in URLs. This is because the htaccess file filters out any requests for those requests and never sends them to ProcessWire (since they don't follow PW's name format). I mention this just because any redirect module built into PW also wouldn't see URLs that don't follow that format. Thanks, Ryan
  21. Hey Jim, The session start can't easily be hooked into the traditional way just because the session is initiated before the plugin modules are. So you have to take a little bit different approach to override the session: Place your Zend_Session::start() at the bottom of your /site/config.php file. That will override ProcessWire's session_start() because in PHP subsequent calls to session_start() are ignored. Also /site/config.php is a site-specific file, so you don't have to worry about it being overwritten during upgrades. Here is what I suggest placing at the bottom of your /site/config.php file, though of course tweak it to suit your needs: session_name($config->sessionName); ini_set('session.use_cookies', true); ini_set('session.use_only_cookies', 1); ini_set('session.gc_maxlifetime', $config->sessionExpireSeconds); ini_set('session.save_path', rtrim($config->paths->sessions, '/')); Zend_Session::start(); Let me know if that works? Thanks, Ryan
  22. Thanks that's very generous of you. If you ever find parts that aren't smart (there always are some) please let me know too. I sometimes come across my code (whether on this project or others) where I look at it and think to myself "what the hell was I thinking?". ;D I avoid coding at night for this reason.
  23. Thank you, that's kind of you to say. But I think you are giving me too much credit. You deserve the credit here for discovering this issue here that I missed, so thanks for that.
  24. This is one of the potential problems with using the WireSaveableItems, because the default behavior is that they load an entire table and all it's rows in memory at runtime. This is desirable for things like templates and fields because we need all this stuff in memory to build pages, and don't necessarily know what we'll need ahead of time. But for large inventories of information, there might be memory or speed issues as the number of items increases. To account for this, you can specify a selector to the load() function to tell it what you want with a selector string: $this->load($this->videosArray, "limit=10, sort=id"); That selector string can have both "start" and "limit" and "sort" options for pagination. You can also have it match any given field name in your table. For operators, you are limited to: =, <, >, !=. With those limitations in mind, they work like selectors on pages in all other respects. I also want to mention that ProcessWire does not currently use this feature of the WireSaveableItems class, so if you use it, you are the first to test it. Let me know if you run into any issues and we'll get them fixed. That's correct, but that pagination is only specific to pages. You can certainly use pagination with your module, but you'll need to specify the "start" and "limit" options to your load function manually. (When it comes to paginating actual pages, ProcessWire takes care of the "start" part for you.) When working in a database client (like PHPMyAdmin) it's true that your view of the data will be in a traditional single-table format, unlike pages where fields occupy their own table. But within ProcessWire, the view is just a manner of how you use the API code. The way you work with items coming from a WireSaveableItems class and items coming from pages is going to be the same. For example, in this part of your code: <?php $table = $this->modules->get("MarkupVideoDataTable"); $table->headerRow(array( 'Volume', 'Name', 'Description', 'Event Date', 'Price', )); foreach($videos as $video) { $volume = 'Vol. '.$video->volume; $table->row(array( $volume => "edit?id={$video->id}", $video->name, $video->description, $video->eventDate, $video->price, )); } ...$videos could just as easily be a PageArray (like from $pages->find(...) or $page->children(...), etc.), and it could be substituted without any changes. So while there is a difference in the view at the database table level, there isn't any difference at the ProcessWire API level. I don't want to talk you out of the approach you are taking, because I like what you are doing and am thrilled to see some of these classes get used outside the core -- so stick with it, and I'll be here to help. But I also want to make sure I communicate that the data you are working with is exactly what pages were designed for. Pages are data containers of a defined type (template) with defined fields... they may or may not represent an actual page on your web site, that's up to you. But pages are really useful for building huge inventories of data and making it easy to view, search and manipulate, and even change it's structure without changing your code. Think of pages like you would a node in Drupal... they may represent a page on your site, but their intended use is much more abstract than that. You can! Lets say that Users were pages. Here is how you would load an filter them by role at the same time: <?php $users = $this->pages->find("template=user, role=superuser"); foreach($users as $user) { ... } // output them in the same manner as ProcessUser.module This approach has an advantage over what's being done in ProcessUser.module right now, because it only loads the users that match the filter. The way ProcessUser works right now is that it iterates through all the users and skips the iteration if the filter doesn't match. Here's another example more specific to your case, filtering by volume number: <?php $videos = $this->pages->find("template=video, volume=123"); forech($videos as $video { ... } ...or... <?php $videos = $this->pages->get("/path/to/videos/")->children("volume=123"); foreach($video as $video) { ... } Of course, ProcessWire does this filtering at the database level (not in memory). That makes sense, and there are very good reasons for having something completely separate from a storage standpoint. From an organization standpoint, the main difference would be that the videos don't appear anywhere in the page structure. But I think your ProcessVideo.module would be equally useful whether videos are pages or WireSaveableItems (and the code behind it would likely be very much the same). Each page is assigned a template that defines it's data structure, and optionally provides a file where you can produce output for the page's URL. But that part is totally optional. If you choose to display them in some other way, the manner in which you do it should be the same. I built pages for this purpose, so that's the approach I would take myself. But I also think you have good reasons for the approach you've taken and there's nothing wrong with it. There are both tradeoffs and benefits which only you can evaluate what is best for your needs. If I'm understanding it all correctly, here are a few of the pros for your approach: 1) it can be more efficient if you are careful about how much data you load at once; 2) the data relates directly to a table, making it a simpler matter if you like to work directly in SQL; 3) it's compartmentalized, not relying upon prerequisite fieldtypes or templates (which may make for simpler portability if you distribute the module). I think in your case it is a good approach because you have PHP knowledge and know your way around code, and I am glad to see you doing it ... it also helps me to make a better product when people push the utility of these core classes. Thanks, Ryan
  25. Hani, welcome to the forums and thanks for your interest in ProcessWire! I'm glad to see you getting down into the code. Before we take a closer look at it, I just wanted to get a better idea of the approach you've taken with extending WireSaveableItems. It's admittedly something that hadn't occurred to me to do outside of the core, as pages are the primary data type for API usage. Have you looked at using pages, templates and fields as a way to structure this data? In your case, what are the benefits to the approach you've taken? I'm actually pretty impressed with your approach, because it means you've really gotten down into the code, and you are using it like a framework. But I also worry we might be creating more work for you than is necessary by doing it that way, so I just wanted to get a better idea. ProcessWire uses WireSaveableItems for some of it's core pre-defined data types like Templates and Fields. But Users, Roles and Permissions are in the process of being converted to Page-based data types -- I want them to be things that people can easily add additional fields and data to according to the needs of their site. Page-based data types also benefit from all the page selecting, finding, pagination and manipulation functions that are built in. I haven't taken a really close look at your code yet, but looking at it so far I am wondering if you might be better of using pages, since they would translate well to your videos datatype. But I could be wrong about that, as I'm not yet an expert on what you've put together in this module, so am just posing it as a question. With regard to the error you are experiencing, I'm thinking you might need to move line 189: Wire::setFuel('videos', new Videos()); ...into your init() function. The reason is that $this->videos won't exist in your executeEdit() function. executeEdit() is only called when the URL matches "/edit/" and executeList() is only called when the URL matches "/list/" (or "/" as you've coded it.) Since you are creating the $videos object in the executeList() function, that part never gets executed in the executeEdit() function. Also, you don't even need to do Wire::setFuel('videos', new Videos()). That setFuel() method is only for things that need to be system-wide. Things like $pages, $templates, $fields, etc. It's kind of like an alternative to a global variable, but used as a way to provide the basic API objects. In your case, it looks like $videos is only used in your ProcessVideo class. So you probably only need to make $videos local to that class, rather than creating a new system-wide API variable. So I'm thinking you could put this at the top of your class: protected $videos; And then update your $videos creation line (moved to the init() function) to be this: $this->videos = new Videos(); Let me know if this makes sense, or if I'm misunderstanding? As a side note, I don't think this is what you want... but if for some reason you did want $videos to be an always-accessible system wide variable, you'd have to create it somewhere that it was automatically loaded every time ProcessWire boots... The way you'd do that is by creating a new module: createVideos.module: <?php // move your Video, VideosArray and Videos classes into this module's file. // they will be available to your Process module since this one is always loaded. class createVideos extends WireData implements Module { public static function getModuleInfo() { return array( 'title' => '...', 'version' => 100, 'summary' => '...', 'autoload' => true // tells PW to automatically instantiate this module at boot ); } public function init() { Wire::setFuel('videos', new Videos()); } } Thanks, Ryan
×
×
  • Create New...