Jump to content

ryan

Administrators
  • Posts

    16,715
  • Joined

  • Last visited

  • Days Won

    1,517

Everything posted by ryan

  1. @doolak, it's working for me here, but I found it may depend where you are displaying the value. If you are using it for something on the admin side (where output formatting is off by default) it may not have worked. I just updated it to force the output formatting. Try it again with the latest and let me know if it works now?
  2. @mindplay.dk: Separation of concerns, single responsibility principle, and so on -- I get it. You seem to think we're designing these things without understanding of these principles, and that couldn't be further from the truth. I'll also assume that you understand what I was stating before, that we've tried to adopt enough flexibility so that a developer can choose to adopt these principles, while still using the module interface. But we don't want to enforce that upon everyone because it would be counterproductive design. Why? There are principles, context, audience and goals. Our context is not the same as that of the Symphony framework, for example. Our audience has crossover, but is not the same. Most module cases are relatively tiny components. We have a project goal of making module development as simple and bulletproof as possible... something that might encourage web developers to take an interest in exploring their own module development. Part of that is an original design requirement of being able to share and communicate simple modules as 1 file / 1 class, when one chooses to (as many do). The ability to be super-simple and self contained like this encourages growth for the entire ProcessWire module ecosystem. We derive a lot of benefit from this, and that was the goal. It's not at the expense of the underlying principles unless you choose to make that sacrifice yourself. Don't develop your big module using the same strategy as your small module. We don't have many implied strategies for big modules and what's probably lacking most is just the documentation to explain when and why you might take one approach over another. Don't think I'm making any claims of perfection with any bit of code or design. There is always room for improvement in anything, in any project, in any system, in any file, in any class, in any method, and so on. The definition of a bad coder is someone that thinks they've got it all figured out or something is beyond improvement. I am very far from that. As most here know, I'll defend that which is worth defending and agree on things that aren't. But there's a lot more thinking behind the design than you often assume. I feel strongly about the decisions behind our current module system, and that they were the right ones. I also have an open mind. If someone proposes something better that understands not just the principles, but the context, audience and goals too, then I welcome it.
  3. Wow this looks great Teppo! I can't wait to try this one out tomorrow.
  4. This is due to the way strtotime() interprets dates. It interprets dates with slashes as US-style mm/dd/yy and dates with hyphens or periods as dd-mm-yy.
  5. You might find the attached LazyCronTest.module helpful. This basically just demonstrates LazyCron in action. Install this module after LazyCron is installed, and it'll record a log entry every 5 minutes (or so) to /site/assets/logs/lazytest.txt. That's assuming the site is getting non-cached pageviews so that LazyCron gets a chance to run. LazyCronTest.module <?php class LazyCronTest extends WireData implements Module { public static function getModuleInfo() { return array( 'title' => 'Lazy Cron Test', 'version' => 100, 'summary' => 'Tests lazy cron by writing to a log file in /site/assets/logs/lazytest.txt', 'singular' => true, 'autoload' => true, ); } public function init() { $this->addHookAfter('LazyCron::every5Minutes', $this, 'lazyTest'); } public function lazyTest($event) { $seconds = $event->arguments[0]; $log = new FileLog($this->config->paths->logs . 'lazytest.txt'); $log->save('LazyCron 5 minute test - ' . date('Y-m-d H:i:s') . " - $seconds seconds"); } } Here's an example of the log file it generates: 2013-02-02 11:19:02:LazyCron 5 minute test - 2013-02-02 11:19:02 - 356 seconds 2013-02-02 11:24:04:LazyCron 5 minute test - 2013-02-02 11:24:04 - 302 seconds 2013-02-02 11:31:52:LazyCron 5 minute test - 2013-02-02 11:31:52 - 468 seconds 2013-02-02 11:37:22:LazyCron 5 minute test - 2013-02-02 11:37:22 - 330 seconds 2013-02-02 11:43:14:LazyCron 5 minute test - 2013-02-02 11:43:14 - 352 seconds 2013-02-02 11:49:13:LazyCron 5 minute test - 2013-02-02 11:49:13 - 359 seconds 2013-02-02 11:54:37:LazyCron 5 minute test - 2013-02-02 11:54:37 - 324 seconds 2013-02-02 12:00:07:LazyCron 5 minute test - 2013-02-02 12:00:07 - 330 seconds 2013-02-02 12:05:07:LazyCron 5 minute test - 2013-02-02 12:05:07 - 300 seconds 2013-02-02 12:12:42:LazyCron 5 minute test - 2013-02-02 12:12:42 - 455 seconds 2013-02-02 12:18:35:LazyCron 5 minute test - 2013-02-02 12:18:35 - 353 seconds 2013-02-02 12:23:37:LazyCron 5 minute test - 2013-02-02 12:23:37 - 302 seconds 2013-02-02 12:30:54:LazyCron 5 minute test - 2013-02-02 12:30:54 - 437 seconds 2013-02-02 12:36:22:LazyCron 5 minute test - 2013-02-02 12:36:22 - 328 seconds 2013-02-02 12:41:57:LazyCron 5 minute test - 2013-02-02 12:41:57 - 335 seconds 2013-02-02 12:50:48:LazyCron 5 minute test - 2013-02-02 12:50:48 - 531 seconds 2013-02-02 12:56:58:LazyCron 5 minute test - 2013-02-02 12:56:58 - 370 seconds 2013-02-02 13:03:03:LazyCron 5 minute test - 2013-02-02 13:03:03 - 365 seconds 2013-02-02 13:15:03:LazyCron 5 minute test - 2013-02-02 13:15:03 - 720 seconds 2013-02-02 13:26:03:LazyCron 5 minute test - 2013-02-02 13:26:03 - 660 seconds 2013-02-02 13:31:46:LazyCron 5 minute test - 2013-02-02 13:31:46 - 343 seconds 2013-02-02 13:37:37:LazyCron 5 minute test - 2013-02-02 13:37:37 - 351 seconds Notice how the number of elapsed seconds is always more than 5 minutes (and sometimes a lot more). That's just because it has to be triggered by a PageView. In most cases, this is okay because a lack of pageviews usually means there's nobody there to see what was updated anyway.
  6. Twig is a nice template engine. I'd like to explore the possibilities of expanding upon what porl started with that module sometime. Though ProcessWire will always be based on a PHP API, but I'd like us to have a simple integration method for supporting other template engines for the people that want them.
  7. Awesome work Wanze! Thanks for posting it. I'm also thrilled to see the awesome things people are doing with the blog profile as a base.
  8. Soma, I'm not totally sure I understand just because I've not seen buttons look like that before. I could probably figure it out by digging a little deeper in the code, but I'm trying to get through a large queue of messages in a short amount of time (they dont' give me much on the weekend!). If there's a code change you think would be worthwhile in the core CSS, let me know what to change (post code here or pull request, etc), and I'll make it.
  9. It wouldn't be from PW's cache unless you aren't logged in, and you are using cache features. But it's not much of a mystery when you encounter that. I'm guessing it's not the case in your instance. Lots of the *AMP installs (like MAMP) come with a PHP cache built-in. It's certainly possible that could cause something like you've mentioned. But personally, I've not experienced any unexpected behavior like you are describing, and I'm literally using PW all day every day. Check and see what PHP cache you might be using, just in case.
  10. Nik's AfterSaveActions module is also a great time saving optimization for when you are creating pages, templates, fields. I was using it just yesterday on a site where I needed to add a hundred or so pages via copy/paste (the content was too inconsistent in order to automate it with something API-driven). I set my 'after save action' to 'create another sibling page', and it probably cut the time it would have taken in half.
  11. That's a good idea. Though I'd still be nervous about it. Something to think about adding though... One way you can override things on your own is to edit the Fieldtype module file that you want to convert from. So if you've got a float that you want to convert to a string, edit /wire/modules/Fieldtype/FieldtypeFloat.module and add the following function to it (temporarily): /** * Return Fieldtypes that are compatible with this one (i.e. ones the user may change the type to) * * @param Field $field Just in case it's needed * @return Fieldtypes|null * */ public function ___getCompatibleFieldtypes(Field $field) { $fieldtypes = new Fieldtypes(); $fieldtypes->add($this->fieldtypes->get('FieldtypeText')); return $fieldtypes; } If the Fieldtype you are editing already has that function, that you could just modify it according to what you need.
  12. For the most part, you only want to hook into Process modules if you are manipulating something on the admin interface side. Process modules don't get used for anything else. So if a page is added from some other method in the API, your ProcessPageAdd hook would never get called. Instead, you'd want to hook Pages::added to get a $page that was just added. Or you'd want to hook into Pages::saveReady if you wanted to get it before it was added. If using saveReady, you'd just confirm that it's a new page being added by it not having a $page->id (already existing pages will have a $page->id). If you find you still need to hook ProcessPageAdd, tell me more about what you are trying to do and I can add an appropriate hook. public function init() { $this->pages->addHook('added', $this, 'hookPageAdded'); $this->pages->addHook('saveReady', $this, 'hookSaveReady'); } public function hookPageAdded(HookEvent $event) { $page = $event->arguments[0]; $template = $page->template; // here's your template } public function hookSaveReady(HookEvent $event) { $page = $event->arguments[0]; if(!$page->id) { // new page about to be added $template = $page->template; // here's your template } }
  13. Nearly all built-in Fieldtype functions are passed a copy of the $page and $field being edited. So you pretty much always have a copy in your Fieldtype. For example, every Fieldtype has this function: /** * Return a new Inputfield, ready to be used * * @param Page $page Page being edited * @param Field $field Field that needs an Inputfield * */ public function getInputfield(Page $page, Field $field) { // $page is the Page you want } Since there is only 1 copy of any given Fieldtype in memory at once, you don't want to keep any copies of $page outside of the individual functions because the same Fieldtype instance might get called several times for different pages within the same request. Inputfields are different: They aren't supposed to know anything about the page being edited, because Inputfields are used for far more than just pages. There will be multiple instances of the same Inputfield in memory at once. Basically, there is a new instance of it for every single input that's needed. Unlike Fieldtypes, Inputfields don't get reused for multiple fields of the same type. Since Inputfields aren't supposed to have a $page context, they don't already have a copy of it anywhere. However, if you need one, you can pass it a copy from your Fieldtype's getInputfield method, shown above: /** * Return a new Inputfield, ready to be used * * @param Page $page Page being edited * @param Field $field Field that needs an Inputfield * */ public function getInputfield(Page $page, Field $field) { $inputfield = wire('modules')->get('InputfieldSomething'); $inputfield->set('editPage', $page); // give it a copy of $page being edited return $inputfield; } Now InputfieldSomething can access the $page from $this->editPage. This is what FieldtypeRepeater does for InputfieldRepeater, btw.
  14. When you change a field's type, it doesn't keep the configuration settings for the old one around. That's because the new one may have very different settings or may conflict with the new one. So it's safest to start fresh on this. Tags is one that could feasibly stick around, so maybe we'll find a way to do that in the future, but for now we reset the field's settings when you change it's type.
  15. I've updated it so that it can now support using formatted values before concatenation--like you would want with dates. Grab the new version (1.0.1). Then, in your format string, just specify an exclamation point after any field names you want to use the formatted version for. For example, "title, date!" rather than "title, date".
  16. What I've extracted is that you don't like statics. You've set a goal of avoiding statics at all costs. And that's fine--I understand your reasons. I'm middle of the road on statics--I avoid them, but not at all costs. There is a reason they don't appear in PW's public API. But if they are the best fit for a given situation, given all factors, then I'd consider it a bad choice to use something else. We've got very good reasons for using them where we do. I hope you can understand why we wouldn't abandon an interface that works very well for us just because one person doesn't like statics. What I wrote in my previous message was that I support your ideas, but as an alternative method of configuration, not as a replacement. Step out of the ivory tower and propose something tangible and realistic. Use some code. For instance, I understand you'd like to have a separate class to handle configuration. I like this idea for many situations. So how would you formalize that in code? What specific methods would be in the ModuleConfig interface or abstract base class? How would you propose relating the configuration class to the module class? I'd suggest that it could be done by an agreed-upon class name, like "Config[ModuleName] implements ModuleConfig", or a module's getModuleInfo() could specify it's configuration class in it's info array. But maybe there's more ideas too. Get some momentum going here and maybe we'll have something we can commit to the code base now rather than later. I understand why it matters in many situations. But this distinction does not matter in ProcessWire modules. Can you think of any instance where it does? We go out of our way to keep these considerations away from those actually using the module. I don't yet know much about Laravel, so can't knock it or praise it. But I was turned off by the syntax I was seeing in the examples. Though I can find plenty to like after a quick look too. But it does seem like a lot of people are treating Laravel as God's greatest gift to PHP. So far I don't understand. Maybe there's something to learn here, even if there is some to dislike as well. While I don't hate statics like you do, I do agree in general -- I don't much like seeing them in APIs. That's why you don't seem them in ProcessWire's API.
  17. In real usage, ^= and $= just aren't that useful when paired with a fulltext index, because fulltext indexes store words, not partials. I actually cant think of any situation where the old fulltext implementation for those is worthwhile. I dont think anyone was using them, as a result. The %^= syntax is far more useful. But I don't want to have something as cryptic as %^= and %$= being part of our official set of operators... Would rather leave it as undocumented and eventually deprecate it, and let ^= and $= take the behavior. That's really how they were supposed to behave in the first place. So that change you linked was my attempt to correct the behavior of those.
  18. You should also be able to retrieve them via just $field->tags or $template->tags
  19. There isn't a page like this that exists in the system unless you are an admin and can view user accounts in Access > Users. However, you certainly could create a template on your front-end that does something similar. You can iterate all users by: foreach($users as $user) { // ... } You can retrieve any user by: $u = $users->get(123); // user ID; or... $u = $users->get('ryan'); // user name Here is the $user API which shows you what you can do once you've located the user you want to display.
  20. I think it just depends on what you are trying to do. But as a guideline, only consider using your own custom tables if the data you are pulling doesn't need to be represented by unique URLs. Pages are designed to be represented by URLs, though they don't always have to be. But if you've got complex data that is large in quantity or fields, and individual records don't benefit by each having their own URL, then you may find it more efficient to use alternate data storage like SQL (if you are comfortable doing so). If you want the data to be managed in ProcessWire admin and queried from PW's API, you can create custom Fieldtypes by extending Fieldtype or FieldtypeMulti. An individual Fieldtype can represent as complex of a table structure as you want it to.
  21. A page does not need to have two parents of the same generation like we do. In the case of ProcessWire, all pages are female.
  22. Biorn, here's some more info about LazyCron: http://processwire.com/api/modules/lazy-cron/ Ultimately, it's lazy, which you will notice if you don't have consistent traffic to your site. So if you need something to run every 5 mins consistently, then you should either use regular cron (from your server) or use regular cron to ping a non-cached page on your website (thus triggering Lazycron).
  23. I don't think there's a way to reliably change system paths after bootstrap has occurred. I also don't recommend trying it, just because it seems like there's potential to break things by doing that, and it may work in some cases and not others. Ultimately it wasn't designed for runtime changes to system paths. It probably works from $config.php just because all that happens before bootstrap. It is however designed for runtime changes to template paths via the $template->filename = '...'; syntax.
  24. I'm guessing you don't like Laravel much? There is a reason why statics exist as a language construct, and there is a reason why we use them where we do. Though our usage is admittedly rare, there has never been a goal to "avoid statics as much as possible". The goal has been to use the tools available to us to make ProcessWire as simple, flexible and extendable, to the intended audience, as possible. It's not often that we have use for a static method in ProcessWire, but when we use them it's because they are the most appropriate solution. Keep in mind, we don't actually "need" static methods. We could certainly do without them. They just make a whole lot more sense in our context than the alternative. It's important to realize that Module is just an interface for communication between ProcessWire and the functionality you want to provide. It is not a class or even an abstract class. It provides no implementation for you (other than what you might optionally choose to extend from some other class). The only requirement for a module is that ProcessWire can ask it what it is. That comes from the Module interface's 1 static method: getModuleInfo(). Without that, it is not a Module. The ConfigurableModule interface has 1 static method: getModuleConfigInputfields(), which you can choose to delegate elsewhere if you choose. Beyond the obvious benefits, these methods are static for correctness: they are about all instances, not a specific one. The existence of the interface is not a suggestion that you implement everything in the class itself. That's entirely up to you. If the scope or philosophy of your need is such that you want to split every part of it into separate classes and files, then you should (this is what I do in FormBuilder). The Module interface facilities this. But the reality is that most modules are not of that scope, and there's rarely a tangible benefit in our context to being more verbose. But we ultimately leave that choice to the developer. A module is a singleton only if the developer specifies "singular" in his/her module definition. Otherwise you will get a new instance every time you ask for a module. I understand what you are trying to get at here. There may be someday when the configuration needs of modules increases in scope to the point where we might benefit from such an approach, but we're not near that yet. In the present, I think the majority of module cases benefit more from less verbosity and the current approach. We already have the door open to this approach, even if it's not implicit--FormBuilder uses something very similar, for example. But I would be happy to support this more implicitly as a second option for module configuration in the future. Instance types are not loaded before they are needed. They are loaded on-demand, unless the module's definition specifies "autoload". ProcessWire caches its module information so that it doesn't need to even include a module's file until ready to instantiate. It's the module developer that should make the call about whether their module is designed to be singular or multi-instance. A singular module might very well be coded differently than a multi-instance one. I don't want the consumer to have to think about this variable. I'm not opposed to making architectural changes in major releases so long as they are geared at make things simpler or easier for the users of the software. While I don't share all your opinions on how some things should work, I appreciate and respect them, and am especially glad for your interest in them. If we were to implement an architectural change to make module configuration more implicit to a separate class, I'd support it (after all, it's an approach I already take in some modules). But it would be added as an option, rather than a replacement. In terms of future major releases, I don't like breaking backwards compatibility unless absolutely necessary. But if there's a net benefit to the wider audience, then I have no problem with it. The only thing in my mind that carries that status right now is the switch to namespaces in 2.4 (and the changes that would go along with it), which I'm looking forward to collaborating on.
  25. How about: $level = count($page->parents);
×
×
  • Create New...