Jump to content

ryan

Administrators
  • Posts

    16,772
  • Joined

  • Last visited

  • Days Won

    1,531

Everything posted by ryan

  1. Great update, thanks Pete! What do you think about putting this on GitHub? (whether now or in the future)? For the cache time, you could always make it configurable with the module if you wanted to. Let me know if I can assist. I also like your alternative for time based caching, though I'm guessing time based caching is adequate for most. But if you want to pursue that alternative, you'd use the CacheFile class directly and have a Pages::save hook clear the cache file (or maybe just Pages::added and Pages::deleted hooks). I wasn't aware of the <sitemapindex>, this sounds like a good idea for the large sites. But also sounds like a much more complex module.
  2. Now I understand what you mean. I've just updated the source so that now it uses the field labels rather than the names. That way it'll pull in the field label in the language that it's in. So for the examples you posted, you'd want to edit the language and language_files fields in Setup > Fields > Filters > Show Built in Fields. I've also made ProcessPageType translatable so that everything else in that file can be in future language packs (including the 'Name' field, in the column headers). That's a good observation. There are a couple rules set by the parser that vary from regular gettext. One is that there can only be one translation per line. Another is that a translation block cannot contain split strings, and thus cannot span more than one line (though you can put literal "\n" returns in it if you want). So to achieve the example you posted, you'd need to do this: <?php $this->method(array( $this->_('Text1'), $this->_('Text2') )); or this: <?php $field->value = "<span>".$this->_('partoftext')."</span> ". $this->_('another part of the text'); or this: <?php $a = $this->_('part of text'); $b = $this->_('another part of the text'); $field->value = "<span>$a</span> $b"; The reason for this is that the parser considers comments on the same line as the translation to be notes for the translator: <?php $this->method(array( $this->_('Text1'), // This will show up as an optional label for the translation field $this->_('Text2') // Some label // a second comment on the line adds an extra 'notes' field visible to translators ));
  3. Pete's right that there probably are a lot of considerations here. You don't really want to add the watermark to the original because then it's going to be in other sizes (thumbnails, etc.) and you may not want that. You don't want to use a filename based on the original because then someone can pry and find your non-watermarked version. But then if you create a different filename, it's no longer tied to the original and will stick around if the original ever gets deleted–we might need to add a new hook to Pageimage::getVariations in order to solve that one. Actually I think all the issues are solvable, but just a little more work than it sounds like at first. One approach is to put the watermark capability in it's own module: <?php $w = $modules->get("WatermarkImage"); $w->setWatermark("mark.png"); $image = $w->watermark($page->image, "new-file.png"); // or just $w->watermark($page->image); I'm guessing something like this would return the URL to the new file, or a new Pageimage object. Another approach would be to make an autoload module that adds a new watermark() function to the Pageimage class and works the same way as the size(), width() or height() functions, returning a new Pageimage: <?php $image = $page->image->watermark("mark.png"); Still another option would be to just create a new Image fieldtype that automatically adds a watermark (configured in the field settings) to every image uploaded to it. Or, every image with the checkbox, like you mentioned. Antti's CropImage fieldtype is probably a great example to look at for how to implement a module like that. If you decide to build a new module, just let me know anything I can do to help.
  4. Pete, nice job with this module, it looks very well put together and provides a helpful function. I like the simplicity of it being a module and how easy that makes installation. But there are a couple potential issues with this approach I wanted to mention. First is that it's taking the place of the 404 page and a 404 header is getting sent before the sitemap.xml output is displayed. This will probably be an issue for any search engines that hit it. You can avoid this by moving everything from your generateSitemap() function into your init() function. You don't need to use any hooks. Of course, you won't have a $page to check at the init() stage, but I don't think you need it–just get rid of the $page->template != 'admin' check... your strpos() below it is likely just as fast. Next concern is scalability. Generating this sitemap could be a slow thing on a large site. Anything that's slow to generate can be a DDOS hole. Because it's in a module, you can't as easily cache the output with a template setting. However, you could use the MarkupCache module to perform your own caching, and that's something I'd probably recommend here. Last thing is that I'd suggest renaming the module since it falls in the Markup group of modules. Perhaps the name MarkupSitemapXML? Other than that, I think it looks great.
  5. Sorry, I forgot about this earlier. Here's how to add such a configuration option. First off, tell ProcessWire that your module will be configurable by adding 'implements ConfigurableModule', like this: class SchedulePages extends WireData implements Module, ConfigurableModule { Next, determine the name of the variable you will use to hold the template IDs: I'll use $scheduleTemplateIDs. Set that in your __construct() to initialize it as a known variable in your module: <?php public function __construct() { $this->set('scheduleTemplateIDs', array()); } Now you need to implement the ConfigurableModule interface by adding a static function called getModuleConfigInputfields. It is static so that ProcessWire's Modules section can call upon it without actually initializing the module. Or to put it another way, it ensures that if you have CSS or JS files being loaded in your __construct() or init() then they won't be altering the display of the module configuration screen. This function is given an array of the current configuration data saved with your module. If it's the first time your module is configured, it will very likely be empty. <?php static public function getModuleConfigInputfields(array $data) { // create a wrapper to hold our field(s) $form = new InputfieldWrapper(); // set our default if this config var isn't already present if(!isset($data['scheduleTemplateIDs'])) $data['scheduleTemplateIDs'] = array(); // create a field to select templates $f = wire('modules')->get('InputfieldCheckboxes'); $f->label = 'Select the templates you want'; $f->description = 'Additional description if you want it'; $f->notes = 'Some optional extra notes at the bottom'; $f->attr('name', 'scheduleTemplateIDs'); // add the checkbox options foreach(wire('templates') as $template) { $f->addOption($template->id, $template->name); } // set the current value $f->attr('value', $data['scheduleTemplateIDs']); // add it to the form $form->add($f); return $form; } Now when the user clicks on your module in Admin > Modules, they will see checkboxes they can use. After they save, PW will now know to automatically send a $scheduleTemplateIDs var to your module every time it's loaded. That variable will be set before your module's init() is called. So you could do something like this in your init() or anywhere in your module: <?php $page = wire('page'); if(in_array($page->template->id, $this->scheduleTemplateIDs)) { // do something }
  6. The item description field is actually configurable in the module settings. In the admin, go to Modules > Markup > RSS. Click on it to configure the various settings it can use. This sets the defaults for all instances of MarkupRSS. If you prefer, you can also change the settings at runtime for any individual instance like this: <?php $rss = $modules->get('MarkupRSS'); $rss->itemDescriptionField = 'body'; echo $rss->render($yourPages); Also, thanks for the great example stillmovingdesign!
  7. Sorry about that, had an extraneous debug message in the dev branch. I just committed an update that should remove it.
  8. Oliver thanks for testing out the Language modules and for your feedback. The initial release of PW22 focuses on making the client-side stuff translatable. Ultimately all the developer-side stuff (like Setup and Modules) will be translatable too, but I consider that secondary. I will be focusing on a module or two every week for translation and adding them to the repository. Another factor here is that there is a large amount of translatable stuff in the developer-side part of PW, relative to the client (Pages) side. I estimate there's 3-4 times as much translatable copy in that stuff. So in the short term, I want to make sure that everything your client sees is in their language. Then we'll focus on all the stuff that we–the web designers/developers–see. When it comes to the Modules section, we'll make the stuff in getModuleInfo() translatable for the core modules. It'll be up to other module developers whether they want to make their modules translatable, but it'll be recommended. I'm not sure that I understand what you mean? At least, I'm not clear about this example/where it's from? But want to mention that table column names (and field names) will not be translatable–when it gets down to that level, we need to have something concrete in order to relate to the database schema. Of course, field labels and descriptions are translatable though.
  9. All of the language features are now in place in the dev branch and I think we're at a good RC1 release with PW 2.2. Here's a short video intro on how to use the multilanguage fields (though I'll plan to make a better one next week). http://processwire.com/pw-multilang.mov
  10. I think it's totally fine to go with the bucket approach here and so not suggesting you change it. I'd use the bucket approach for blog entries too (unless I was a really prolific writer). But if there are times when you want to build a structure around categories, and there can be multiple categories, then you can still do this with a tree. You just have to think in terms of primary category and secondary categories. The primary category becomes the parent, and the secondary categories become page references to the siblings of the parent. Even though there is a structure there, you can still pull them all out of a bucket when you want to by using $categoriesParent->find('...') or $pages->find('template=entry, ...');
  11. It sounds like you've got everything is living off the root namespace, at least on the front end? That can only scale so far, unless you are using page ID as a url segment or something like that. Though it may be fine for the scale of a blog, but it's something to be careful with if the site gets really big. I take it that on your back end, the entries are living at /articles/ or /entries/ or something like that; and you are loading the entry with an $input->urlSegment1 from your homepage? This seems like a fine way to go, though it is treating the site as a bucket rather than a tree. A more structured way of doing it would be to have several categories and then place each entry under a category (or at least, primary category if multiple) and make that structure consistent both on the back and front-end. But you set the rules and PW is there to adapt, so don't take this as a suggestion to change anything. But what I would recommend is to make it clear to Google what you are doing (to avoid dup content penalties): add a 'canonical' meta tag to the pages in your root-level namespace so that they map to your permalinks or actual page URL. If your date-based links are the ones you consider permanent, then make sure that if the page is rendered at it's actual URL (/article/some-page/) then it also outputs with the canonical meta tag. That is just a way to tell Google what you are doing and that it should consider them the same pages, even if they live at different URLs.
  12. Great module Nico! This is a way of going about it that I hadn't considered before. It seems to work quite well on the pages I tested it with. A few comments/suggestions: 1. This is another module that shouldn't be a 'Process' module the way it's currently implemented. But I think you may want to make this a real Process module for another reason (below). But if you stick with the preview.php method, then you likely want to make this a regular non-Process module and make it extend WireData rather than Process. 2. I'm concerned about the safety of preview.php. This gives any logged-in user access to view any page they want to. If you had to stick with this method, I would change it as follows: <?php include './index.php'; // add "./" before index.php to ensure it's not picking up another from PHP's path if(!wire('user')->isLoggedin()) throw new Wire404Exception(); $page = wire('pages')->get((int) $_GET['id']); // typecast to (int) if(!$page || !$page->id) throw new Wire404Exception(); // check that it exists if(!$page->editable()) throw new WirePermissionException("You don't have access to edit that page"); // check for edit permission foreach(wire('input')->get as $key => $value) { // change from $_GET to wire('input')->get $key = wire('sanitizer')->name($key); if($key != $_GET['key']) continue; // skip over invalid field name if(!$page->fields->getField($key)) continue; // skip over field that this page doesn't have $return->setFieldValue($key, $value, false); // add the extra 'false' for optimization: prevents PW from loading previous value } echo $return->setOutputFormatting(true)->render(); 3. What may be a preferable way to implement this is to move everything from preview.php into an execute() function of your ProcessPreview.module. Then have ProcessPreview's install method add a page using the 'admin' template to /processwire/page/preview/ and assign ProcessPreview to the 'process' property of that page. See the install() method of this module for an example of how to do that: https://github.com/ryancramerdesign/ImportPagesCSV/blob/master/ImportPagesCSV.module The reason may be be a preferable way to do it is because it ensures that PW admin is still the gatekeeper to an admin activity. And it also removes the necessity for someone to have an extra php file that they might or might not have to copy to your site root. However, if you still prefer preview.php, I don't necessarily see a problem with using the preview.php if the security of it is updated like in the example above. --- Edit: added 2 more lines in the foreach
  13. Nice work Nico! I'll get those additional hooks put into the dev branch. The only one I'm not sure I can do is the Page::path hook, because that one is called so often that it's possible that making it hookable could introduce more overhead than we'd want. But maybe there's another way around it. I'm with Antti in that I'm not sure I totally understand the drive to have alternate URLs outside of the site's structure. Primarily because it makes me worry about duplicate content penalties and inconsistent offsite/incoming links to the same content, and how that dilutes pagerank. But we all have different needs and if you've had the need for this type of setup then I know others will as well. I think you've done a nice job building this module and the code quality looks good. I only have a few suggestions: 1. The first line of the last method in your class looks like this: $page = $this->pages->get($_GET['id']); That's not desirable because it's sending unfiltered input to $pages->get(). Since $pages->get() can deal various kinds of input (selectors, integers, etc.) you want to make sure you are giving it the right thing, so in your case I would do this (typecast to integer): $page = $this->pages->get((int) $_GET['id']); Or better, yet, use PW's input class: $page = $this->pages->get((int) $this->input->get->id); However, you may not need to do this at all since ProcessPageEdit will already have a copy of the page that you can use: <?php if($this->process == 'ProcessPageEdit') { $page = $this->process->getPage(); } getPage() is a helper method I added to ProcessPageEdit back when first building the PageEdit process, primarily so hooks could see what Page it was editing. So this is exactly the sort of situation that it's there for, if you want to use it. 2. Stepping back a bit, why is this a 'Process' module? It looks to me like it should actually be a regular module because I don't see any execute() method. A Process module is one that is assigned to a Page (usually an admin page) and executes some actions that generate output for the page being viewed. This module hooks into other things to change their behavior, rather than generating it's own output. I think you may want to change it to extend 'WireData' rather than 'Process' and perhaps rename to 'PagePermalink' or something like that? 3. You may not need your own pageNotFound() method? ProcessWire already has one that is called whenever you throw a Wire404Exception, i.e. throw new Wire404Exception();
  14. Here are the 4 different kinds of hooks, in order of use. The method you use to hook them is in parenthesis. 1. After hooks – execute after the hooked method (addHook or addHookAfter). Can access/modify return value and arguments. 2. Before hooks – execute before the hooked method (addHookBefore). Can access/modify arguments. 3. Method hooks – adds a new method to a class, assuming it's not already there (addHook). 4. Property hooks – adds a new variable/property to a class, assuming it's not already there (addHookProperty). Rarely used so far.
  15. Steve, I think I understand now. The solution is easier than you might have guessed. ProcessWire doesn't include any templates until after all the output is generated. So if you want to handle your own output generation, then you should be able to just send it out, and then exit. For instance, try placing this in your executeExport() function of your Process module: die("Happy New Year!"); So I'm thinking you'd do something like this in your executeExport function: <?php header('Content-type: application/force-download'); header('Content-Transfer-Encoding: Binary'); header('Content-disposition: attachment; filename=spreadsheet.xls'); $fp = fopen('php://output', 'w'); fwrite($fp, $spreadsheet_output); fclose($fp); exit();
  16. Nice, I think that's a definite improvement. I also think it would be just fine to not have any red background over there–it's a great looking admin theme either way. But I understand the purpose of the red is weight and balance, so can see why you want the color in the sidebar – it anchors the right side, sets the boundary and carries some weight to balance the sidebar with the content area. From that standpoint, I think your solution (with the search box background) is a good compromise. Thanks again for your great work with this admin theme!
  17. Looking great Nikola! For debug timer, you might also try giving it a name just in case it's getting confused with another, like this: <?php Debug::timer('futura_timer'); // 1. start a new timer called futura_timer // do a bunch of stuff echo Debug::timer('futura_timer'); // 2. echo how long it took // do more stuff echo Debug::timer('futura_timer'); // 3. Echo how long it took (since timer started, step 1)
  18. That's correct, and that would be the simple fix in this case. But because this particular module isn't singular (i.e. a new instance is created whenever you load the module) it would have to keep track of how many instances of it are running (via a static variable in the class).
  19. Here's a couple examples. This first one grabs 'ProcessLogin' and places the output in $content: <?php $process = wire('modules')->get("ProcessLogin"); $content = $process->execute(); Here's another example. This one uses ProcessController, which will take care of access control and matching URL segments to functions in the Process module. This is the preferred way to do it in most cases (specific to Process modules only): <?php $controller = new ProcessController(); $controller->setProcessName('ProcessLogin'); $content = $controller->execute();
  20. It may be better to come in with a 'before' hook and as the class attribute directly, rather than doing a preg_replace. But I don't know the full context here so can't say for certain. Can you hook in before it? i.e. $this->addHookBefore('ProcessPageEdit::executeSave', $this, 'myExecuteSave');
  21. Thanks for the log. The function nesting looks right to me. I can follow the path of functions in the log and it looks as expected, so I guess that means we just need a higher function nesting level than what xdebug provides by default. I would bump it up to at least 200.
  22. You should be able to hook InputfieldPageName::render, but note that it will return markup (and should just be the <input /> tag). If you want to modify some attributes from it or something, then you'd want to hook in before(render). If you want to modify the returned markup you'd want to hook in after(render).
  23. Nice work Nicola! Another beautiful and fantastic admin theme–looks great! I like the design, colors, layout, sidebar, footer, and it all flows very nicely. Thank you for taking the time to put this together! A few questions/suggestions/ideas: 1. I'm not sure I understand why there is a headline in the sidebar that says "sidebar"? Is this to make sure I don't confuse it with the footer or masthead? ;D just kidding, but was mainly wondering if it might not be necessary? 2. Depending on the number of buttons someone has in their admin, they may run out of space in the top nav–a good solution might be to move the label "you are logged in as ..." into the right side of the bar below it (the breadcrumb). That's just an idea. It works great how it is, but just wanted to point out that many people may have more pages in their admin root than what will fit up there (depending on screen width). 3. The last "s" in "Process" of the ProcessWire logo appears to be slightly cut off. Though I'm viewing it away from my desktop so might be this computer too. 4. I'm seeing some strange behavior in PageList when clicking the "move" link... some extra rows show up when the tree is opened up a bit. Like in the default profile, if you click 'move' on 'Child Page Example 1'. 5. If you are interested, PW has a built-in debug timer in the file /wire/core/Debug.php. It works like this: $t = Debug::timer(); echo Debug::timer($t);
  24. Got it–I see what's going on. I was thinking that 'config' was not yet set, but I should have read more closely as you said it was just $config->urls->admin and $page. PW has to start loading pages before it knows what the admin URL is, so that's actually something that happens after the modules are initialized. And that's the only $config->urls property that's determined after module initialization. So what I think you'll want to add a ready() function to your module and use that instead of init() here. PW calls that ready() method after it's determined the current page but before its rendered it. So it's pretty much the same as init, except that init() happens before any page activity (should you need to add a hook before such activity occurs). Whereas ready() is more appropriate in your case since you need to know things about the requested page.
  25. I probably need to take a look to see what's up. Do you want to email the module file over to me? Email my first name @rc-d.net or PM it to me. Rest assured, all the $config vars should be available in any module's init() (and even __construct for that matter).
×
×
  • Create New...