Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 07/25/2024 in all areas

  1. A module to work around a few shortcomings of Fieldset and FieldsetPage fields. Not submitted to the modules directory because I'm hopeful that most if not all of this will be handled in the core eventually. https://github.com/processwire/processwire-issues/issues/1953 https://github.com/processwire/processwire-requests/issues/533 https://github.com/processwire/processwire-requests/issues/534 Fieldset Helper Adds some visibility features to fields of type FieldtypeFieldsetOpen and FieldsetPage, and adds a helper method to get the child fields of a FieldtypeFieldsetOpen field. Visibility The module adds support for the following visibility settings to FieldtypeFieldsetOpen and FieldsetPage fields: Open when populated + Closed when blank Open when populated + Closed when blank + Load only when opened (AJAX) * Open when populated + Closed when blank + Locked (not editable) Open when blank + Closed when populated "Blank" in the context of a fieldset means that all the child fields within the fieldset are blank. * The AJAX part of this option doesn't currently work for FieldsetPage. Get the child fields of a fieldset If you've ever wanted to get the child fields that are within a fieldset you will have noticed that the ProcessWire API doesn't provide a direct way of doing this. Although they appear indented in Edit Template, the child fields are actually all on the same "level" as the other fields in the template. The module adds a FieldtypeFieldsetOpen::getFieldsetFields() method that you can use to get the child fields of a fieldset for a given page. Example: $field = $fields->get('my_fieldset'); $template = $templates->get('my_template'); $fieldset_fields = $field->type->getFieldsetFields($field, $template); First argument: the fieldset Field, i.e. a Field whose type is an instance of FieldtypeFieldsetOpen (this includes FieldtypeFieldsetTabOpen). Second argument: the Template that the fieldset field is part of. Return value: a FieldsArray of child fields. If there is a nested fieldset inside the fieldset then the child fields of the nested fieldset are not included in the return value, but of course you can use the method again to get the child fields of that fieldset. https://github.com/Toutouwai/FieldsetHelper
    7 points
  2. @bernhard Good questions and I'll answer according to the design of the core. These considerations are rigid when it comes to what the core does with these Page classes, but flexible outside of that. You are using Page classes in a way that gives them multiple roles. Whether that's good or not depends on your architectural point of view. But it sounds like you are happy with how it's working, and you've got a well defined strategy, and this is valuable. So none of this may useful for your case, so keep that in mind. That sounds okay to me. Why does it load an extra 10-20 pages? Or does that just depend on what pages are used in the request? As long as that magic page interface isn't increasing resource usage each individual Page object significantly, then it should scale just fine. It shouldn't be related to either. It should be independent of environment. A page is a type that aggregates content for a location in the tree. The problem with having front-end-only or admin-only stuff in a Page object is that it's code in the page that steps outside of its role and applies for one environment and not another. It can be seen as a type of bloat, turning a Page object into a jack of multiple trades. If you've got significant code that's just for one environment or another then the idea is that it's better to keep it separate from the Page. Perhaps delegated to a different class like my earlier example (or even a function outside of a class). But if it's just for a one-off case or something then I wouldn't bother. Take that getPageListLabel() method, that's something to represent the Page in the admin only, which is why it makes me uneasy. It's a tradeoff. It's a one-off case, so I think it's okay as long as we don't add more like it. Potentially the method might have front-end value in some cases too, so maybe that's another reason it's an okay compromise. But if we found ourselves starting to put a lot of admin-specific methods in Page classes then those would be better in a separate supporting class or function, or better yet, decouple it from the class entirely. That way it doesn't add code to the page class for something that goes outside its responsibility. Another point is that often code added for one environment or another will be the same between different page classes. To avoid repeating yourself, you'd have to repeat the same methods in multiple classes, or more likely add complexity with more levels of inheritance or traits to share that code. Moving that code to a dedicated class makes it more contextual, maintainable and extendable across multiple classes. There's that word "things" which seems like it might mean "everything" in this context. Here we're treating the Page like it's a Swiss Army knife for the type, rather than the type itself. It's like being the car and the carwash, the lawnmower and the grass, or not just the ball but the bat and glove too. That editForm() method looks like it's got the Page class now involved in manipulating forms in the admin. In that case Page is not just a type representing an aggregate of content, but also a form editor, adding application logic, giving the Page class multiple unrelated responsibilities. Some responsibilities which apply to all instances of the page and others which might only apply to one instance on occasion. If the Page must be the gatekeeper for multiple responsibilities, the idea is that it would be preferable that it delegates those responsibilities rather than implements them itself. That would enable it to provide that interface without expanding the internal role of the class. If you find benefits in using them differently then I'm not suggesting you change anything. Since you asked I have to give you an accurate answer of what Page classes are originally designed for and what I consider best practices around them. These are how the core considers them and doing likewise ensures things stay simple, efficient and scalable in the long term. But in PW flexibility is more important than rules and this is also true of Page classes. There's more than one way to do things well, I like your creativity and if you've found something that works well for your projects over time and scale then stick with it.
    4 points
  3. You’re not losing your mind ? https://processwire.com/blog/posts/new-repeater-and-repeater-matrix-features/#custom-icons-for-repeater-types
    3 points
  4. Hey @ryan first of all let me thank you for ProcessWire in general and for all the work you put into it! Also thank you for mentioning me and my modules. Unfortunately I can second what @jploch said, that it's very hard to offer commercial modules in PW, but that's a topic for another post as this one has gotten very long already ? NOTE: Sorry, this post got very long, but I think it covers some important concepts and I think some of the concepts can really push PW forward on the web-application front. I think it's important to understand the details, so I ask you kindly to read carefully and try to understand the bigger picture ? In this post I'd ask for clarification about what you said about custom page classes attaching their own hooks: I think the phrase "everything necessary" might be worth considering here. A class ideally just has one responsibility. Page instances are there to provide an interface to one page's content and be swapped in and out of memory on demand. When there are things like hooks going into a Page, that changes the role completely. If you start doing this a lot on a large site then it may affect scalability. Maybe it's fine for one individual installation or another, however. I didn't want to believe that, because everything that I've built over the last few years relies heavily on the concept of "MagicPages" which is a feature of RockMigrations and which basically turns every custom page class into an autoload module where you can add hooks that are related to that pageclass. This concept makes maintainability of the whole project a lot easier imho. Before I invented that concept I had quite complex applications and they had hundreds of lines of code in /site/ready.php or /site/init.php; ---------- first some background, later the tests ------------------ ---------- the tests ------------------ ---------------- Very, very long story short: @ryan I'd appreciate if you could explain in more detail why you think it is bad to put - what I'd call - pageclass-related code into pageclasses? Also I'd like to invite you to re-think if it is really bad to have things like "getPageListLabel" in the pageclass. I think this is a great to have these methods and I really enjoy having "editForm($form)" as a shortcut for ProcessPageEdit::buildForm if($page->template == ...) and so on. At the very minimum I'd love to have an init() and a ready() method where we can add hooks as we need them. Thank you very much for reading. I'm sorry for the length, but I hope there was maybe something interesting in it. Please let me know if I did anything wrong with my tests or what I could do better. Thx. Long live ProcessWire ?
    3 points
  5. Since it came up in this question and I had some time to kill in front of my computer waiting for updates to finish which I had to verify, I got curios whether copy & paste from Word on Windows into a TinyMCE editor field could be made to insert the formatted text and keep the images. Surprisingly, with the help of rtf.js, this went pretty quickly. Ryan already built automatic upload functionality (called ImgUpload) into the TinyMCE field, so I only had to enable the option and select a target field. Even though pasting word processor generated HTML is and always has been a sin, I built a small module for it anyway. I called it (I know, it sounds a bit clunky, but it was the best I could come up with, I'm a backend guy): RtfPasterTinyMce Usage Download the contents of this repository and unpack into a folder in site/modules Open ProcessWre admin and select Modules -> Refresh Click "Install" for "Rtf Paster TinyMCE" Go to "Fields" and select your TinyMCE field where you want to paste office content including images Check "rtfpaster" in "Additional plugins" on the "Input" tab and save your field configuration Edit a page with that field and copy a passage that contains both text and images from MS Word into your TinyMCE field. You should see your images there. Advanced Go into InputfieldTinyMCE's module settings and enable "Image fields for ImgUpload" Edit your TinyMCE field and select an existing image field in the "Image fiels for ImgUpload" select on the Input tab Paste some text / images mixture from your word processor MS Word Tadaa! Your images are magically uploaded into the selected field. Since the RTF doesn't contain any information about the file name of the source image, your uploaded images will be named fieldname.png, fieldname-1.png, fieldname-2.png etc. Keep in mind that this is so far a proof-of-concept module and hasn't been tested with different scenarios and source applications yet. Don't use it in production unless you feel confident to fix any errors yourself! Edit: Only successfully tested with MS Word. Not working with LibreOffice's RTF.
    2 points
  6. Apologies if this is redundant, I'm convinced I've seen this discussed here but I just can't find it. If my memory isn't failing me, I'm sure there's a way of replacing the text links for adding new entries with thumbnails on RepeaterMatrix. I'm using it to create a content blocks solution and want to have a better visual cue of what the block the admin is adding looks like. Or maybe I'm losing my mind, which is quite possible.
    1 point
  7. @ryan In my original reply to this post I mentioned making PW a bit stronger for web application development. After reading these replies, it seems that a lot of the more intricate techniques that would be desired for advanced use cases are already possible natively, but even those of us here that I would consider highly experienced with PW and OOP are still picking up a few crucial nuggets of information that go a long way in structuring a web application with PW (at least it seems that way). So to elaborate on what I stated originally, I think demonstrating how you would approach some of these advanced techniques natively in PW (showing that a feature request to make it "nicer" is not necessary / technically problematic) in a real working app would be very helpful. I believe you attempted this with the site-invoices profile and building it out further with more web-application-y type techniques would really be great.
    1 point
  8. @cwsoft These methods don't make sense in the core, unless they are to initialize each individual instance of a Page class. But Page classes (like all objects in PW) already have methods for that: __construct() and wired(). As I understand the methods requested here, they are independent of Page instance, so honestly don't belong in a core Page class, and are not something that I think the core should suggest. See what I wrote above for why. Though they are trivial to implement on your own with a static variable should you want it. I'd still advise against it, especially for hooks, but here's an example: class MyPage extends Page { static private $ready = false; public function wired() { parent::wired(); if(!self::$ready) { // your init code... self::$ready = true; } } }
    1 point
  9. I found the solution – of course right after posting ? One simply has to wrap the latte markup into {try} ... {/try} {try} {func($var)} {/try} And if you want to log the errors, you can do the following: $loggingHandler = function (\Throwable $e, \Latte\Runtime\Template $template) { wire()->log->save('latte_errors', $e->getMessage()); }; $latte->setExceptionHandler($loggingHandler);
    1 point
  10. Again, regarding a more modern administration: I like how the Silverstripe backend works, both how the page tree is presented, as well how the page edit screen looks. I also like that expanding the tree works in a (imo) more standard way (meaning a click will edit the page, to expand it there's an arrow). I think we could get some inspiration from there. You can try the demo here, no need to sign up: https://demo.silverstripe.org/admin/
    1 point
  11. I can confirm that deployment worked for us after update to 5.0.1 only after removing the old release folders. When leaving them in place on the first deployment, the new release folder with new naming format did not get created. But current was symlinked to that non existing folder.
    1 point
  12. @ryan I like the idea. This could speed up the initial setup and if done well would also work for non technical users (They never heard of composer). It could be like a modules manager/installer/picker that lists all modules with short description. Like a more compact list/grid of modules (maybe an expandable accordion with checkboxes for each module). Anything that makes it easier to find and install modules from the backend/installer would be a good addition in my opinion. I have to say doing commercial modules in PW is hard. The target group is small and the marketing of the module is the sole responsibility of the developer (eg. building a website/shop to promote and get the module). It would be nice if there was a platform integrated into the PW site to list/promote and maybe even buy commercial modules (Not only Ryans pro modules but also commercial modules build by the community). What was asked here shows the interest in page/block builders, and I think both RockPageBuilder and PAGEGRID are already very promising and have the potential to attract more users to PW (Which in return would make it easier for other developers to sell modules). I could envision a light/free version of PAGEGRID, but I need to justify the time to maintain and develop it further, so I think there needs to be a commercial aspect to drive the project forward.
    1 point
  13. Thank you for all of the valuable comments last week! I'll reply to several of the comments soon in last week's thread. A couple months ago a new addHeaderAction() method was added to to our Inputfields JS API, enabling you to add custom header icon actions to any Inputfield (more details here). A short while later, the same method was added to our Inputfield PHP API. This week it's been expanded so that now you can also add drop down menu header actions to Inputfield, like the one in the screenshot below. Though this is just a simple example: Here's how we defined that action and menu in JS: $f = Inputfields.get('checkboxes_field_name'); Inputfields.addHeaderAction($f, { name: 'tools', icon: 'fa-wrench', tooltip: 'Select or unselect all', menuItems: [ { name: 'select-all', label: 'Select all', icon: 'fa-check-square-o', // called when the user clicks on the action callback: function() { $f.find('input[type=checkbox]').prop('checked', true); $f.trigger('change'); }, // called to determine whether action is available to click on (optional) active: function() { return $f.find('input[type=checkbox]').not(':checked').length > 0; } }, { name: 'unselect-all', label: 'Unselect all', icon: 'fa-square-o', callback: function() { $f.find('input[type=checkbox]').prop('checked', false); $f.trigger('change'); }, active: function() { return $f.find('input[type=checkbox]:checked').length > 0; } } ] }); For more details on the options, see documentation here in the inputfields.js file where the addHeaderAction() function is defined. How does that JS code get called in the admin in the first place? Well there's a lot of different ways you could do that, but in my case, I hooked after ProcessPageEdit::loadPage in my /site/templates/admin.php file and added a custom .js file (containing the code above): $wire->addHookAfter('ProcessPageEdit::loadPage', function(HookEvent $e) { $page = $e->return; /** @var Page $page */ $config = $e->wire()->config; if($page->template->name === 'tour') { $config->scripts->add($config->urls->templates . 'scripts/admin-tour.js'; } }); Have a great weekend!
    1 point
  14. @szabesz I think the phrase "everything necessary" might be worth considering here. A class ideally just has one responsibility. Page instances are there to provide an interface to one page's content and be swapped in and out of memory on demand. When there are things like hooks going into a Page, that changes the role completely. If you start doing this a lot on a large site then it may affect scalability. Maybe it's fine for one individual installation or another, however. In your case, this isn't an issue, as you've solved that concern with a static method. I think that's okay for an individual installation, but it wouldn't be okay for PW to suggest for everyone because it's not multi-instance safe. The core avoids also static methods when possible because they are disconnected from any instance of the class. When there are static methods, it's always worth looking closer if it's worth the convenience (sometimes it is) or if they would be better off somewhere else. Using hooks in your /site/init.php, /site/ready.php or /site/templates/admin.php (when appropriate) is a safe strategy, but I agree it's not great if you have a ton of them. At that point it might be good to have an autoload Module, or to start including your own files from one of the previously mentioned ones. But if you find it beneficial to use /site/classes/YourPageClass.php then also consider a separate class (ProductPageTools.php?). Rather than having a static method, you can have a static variable in your ProductPage class that refers to that instance, but there will only ever be one of them, and it will be multi instance safe. Page classes already do this for a lot of things, delegating to other classes (PageTraversal, PageValues, PageProperties, etc.) for code that can be shared among all Page classes. There's only ever one instance of those classes, no matter how many Page class instances there are. And they are all multi-instance safe. This also keeps the memory requirements of the Page classes very low, since the Page class has very little code itself, and mostly delegates to other classes where the same single instance operates on all Page classes. class ProductPage extends Page { static private $tools = null; public function wired() { parent::wired(); if(self::$tools === null) self::$tools = $this->wire(new ProductPageTools()); } } class ProductPageTools extends Wire { public function wired() { parent::wired(); $this->wire()->pages->addHook('saveReady(template=product)', $this, 'hookSaveReady'); } public function hookSaveReady(HookEvent $e) { // ... } } Maybe you have a method with a lot of code that you want to be callable from ProductPage (any number of instances) but maintain in ProductPageTools (only ever one instance). This is how the core delegates to other classes and limits the scale and scope of classes that may have many instances. class ProductPage extends Page { // ... public function foo() { return self::$tools->foo($this); } } class ProductPageTools extends Wire { // ... public function foo(Page $page) { $result = ''; // A whole bunch of code to generate $result from $page return $result; } } If the strategy you are using now works well for you then I'm not suggesting you change it. But just wanted to point out the strategy I'd suggest as a scalable and multi-instance safe one that also maintains separation of concerns and keeps the Page class from becoming complex or heavy.
    1 point
  15. I just had this same problem (some error in the translation csv I uploaded) and I fixed it through navigating to the admin page id folder in site/assets/files/LANGUAGEPAGEID/ and deleted all my templates*.json translation files.
    1 point
  16. Hi all, I'm having an issue with the HTML source editor on both TinyMCE and CKEditor formatting. I have a few images that I've inserted into a content field but every time I try to add an inline 'style' to these images, the style is automatically stripped when I click 'update'. I need to be able to add inline styles to these tags. Does anyone know how to prevent the editor from resetting the tags to default when I click update? Thanks Edit: Figured it out.Open CKEditors config.js and add config.allowedContent = true;
    1 point
  17. Update after a little more testing: it seems that rtf.js is unable to parse the image data when I paste from LibreOffice Writer and drops it silently ? Pasting from MS Word works.
    0 points
×
×
  • Create New...