Jump to content


Popular Content

Showing content with the highest reputation on 10/07/2019 in all areas

  1. 3 points
    Last week I worked primarily on GitHub issues, and did some of that this week as well. Likely I'll be doing a lot of this in October. Thank you for all of your reports. While there's already a lot of commits on the dev branch, I'm going to wait till next week to bump the version, as I've got some stuff in progress that I want to get committed first (more on that below). Next week I'm releasing version 40 of FormBuilder that supports paginated forms, as well as forms within forms (not to mention some other minor additions). Basically, all the stuff that was covered in this video from a few weeks ago, plus a little more. I actually think it's ready right now, but as is often the case, I started writing instructions for using the new features today and thought of a couple minor tweaks that would be helpful along the way. So I'm going to apply those early next week, finish the instructions, test it all out again, and then release it... likely mid-week next week. For the ProcessWire core, one feature people have been asking for for quite awhile is the ability to specify custom fields with file and image fields. I've been working on that here quite a bit this week, and have the initial test cases working quite nicely! Unlike the Description and Tags fields that come as built-in options with file and image fields, the new option instead uses a subset of ProcessWire's Fieldtype and Inputfield modules to support this (note: it does not use pages like repeaters do). This gives you more flexibility in defining what you want and how you want it to look. Though there are some limitations of what kinds of fields you can use here, but I think you will like what it offers and how it works. For those that just need a description and/or tags, then of course those features will remain as they are. But for those that need something more for file/image fields, you are going to have a whole lot of new options in 3.0.142. Unless I run into any roadblocks in finishing development of this part, I'll have it ready by this time next week along with a blog post that outlines it in more detail.
  2. 3 points
    Hi Markus, Why do you want to parse it yourself? πŸ™‚ Twig and Smarty have built-in parsers and both offer to extend the parser with your own functions / filters / modifiers and so on. Twig even allows you to write custom expressions. Here is an example how to integrate the "__('text') function into Twig, assuming you are using the TemplateEngineFactory in combination with the TemplateEngineTwig module. $this->addHookAfter('TemplateEngineTwig::initTwig', function (HookEvent $event) { /** @var \Twig_Environment $twig */ $twig = $event->arguments('twig'); $function = new \Twig_SimpleFunction('__', function ($key) { return __($key, "/site/templates/translations/strings.php"); }); $twig->addFunction($function); }); As you can see, I'm collecting all my translatable strings in a PHP file "strings.php". But you could also extend the twig function to optionally accept the text domain. Cheers
  3. 3 points
    @tires This is the hook to remove the unwanted generator meta tag. Best regards
  4. 3 points
    Hey folks, @kongondo asked me some questions about how I integrated vue.js into ModulesManager2. I was already planning to release a tutorial video of the integration process soon, but I don't have much time now as I am busy with customer work. So here is a quick roundup, which will be improved over time and become a full-blown tutorial. I hope to cover the basics and don't forget anything. How did you implement the integration? I created a new vue project via vue create . inside my site/modules/mymodule folder Do your assets still live under the Vue JS public folder? I don't exactly know what you mean with assets. Are you speaking of images? I don't use images atm. Where do your view files live, i.e. under your modules directory or in templates? As I mentioned in point one, they are in the modules directory. Here is a screenshot of my directory: As soon as I release the beta of ModulesManager2 you can go through the source code in github. Where is your index.xxx file and how are you serving it? vue-cli comes with a built in server and the index.html is automatically generated on-the-fly. The command for running the server with HMR (hot media reload) resides in the package.json and is run via npm run serve This is the standalone server. Anything else I should know (maybe .htaccess issues, etc)? Create a vue.config.js in your custom module's root directory and add following parameters to it, to disable filename hashing: const webpack = require("webpack"); module.exports = { runtimeCompiler: false, filenameHashing: false, pages: { main: { // entry for the page entry: 'src/main.js', // the source template template: 'public/index.html', // output as dist/index.html filename: 'index.html', // when using title option, // template title tag needs to be <title><%= htmlWebpackPlugin.options.title %></title> title: 'Index Page', } }, configureWebpack: { plugins: [ new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery', 'window.jQuery': 'jquery', }) ] } }; Edit: After creating the config run npm run build Then you can reference these files in your module like I did here: $markup = '<div id="app"></div>'; $scriptPath = $this->config->urls->siteModules . $this->className; $markup .= "<script src='$scriptPath/dist/js/chunk-vendors.js'></script>"; $markup .= "<script src='$scriptPath/dist/js/main.js'></script>"; I added the configureWebpack part to have access to the $ and jQuery objects inside of my vue files. The install/uninstall overlay panel in MM2, is that something custom or a ProcessWire panel? Standard ProcessWire panels If it is a ProcessWire panel, did you have any difficulties implementing it into your Vue app? ProcessWire's panel init happens before vue is initiated or rendered, so pw-panel links inside of vue are not catched. To make pw-panel links inside of vue work, you have to defer (don't know if this is the correct term) the process to a body click event: $(document).on("click", "#app .pw-panel", function (e, el) { e.preventDefault(); let toggler = $(this); pwPanels.addPanel(toggler); toggler.click(); }); I hope this helps. If you have questions, please ask.
  5. 2 points
    This module does provide two different added functionalities to FormBuilder. 1. The module does allow users to set default "From" email addresses for emails send to administrators. There's a global address, which is set in the modules settings, and a additional field in the settings for "Send an email to administrator(s)", where one can set a form specific emailaddress to override the global one. Both of those are overridden if you chose to use the "Email from field" setting and the returned value is not blank. 2. The module does add an additional syntax for the textfield where administrator emails are set. If you have a pagefield in your form, where one can choose from the potential users, that should be emailed about your form submission, you don't need to write extensive conditional lines like this: personInCharge=SomeUser?someuser@example.com personInCharge=AnotherUser?anotheruser@example.com You can simply add a email field to the template of those users and use the email provided there. !personInCharge?email The syntax says get the user(s) from the field "personInCharge" and send email(s) to the addresses provided by the "email" field of the selected page. https://github.com/LostKobrakai/FormBuilderEmailExtentions Thanks to madeprojects.com for letting me share this. They'll pay the time I spend to build it.
  6. 2 points
    PWImage was disabled – I did not set up the page originally so not everything is in default settings. Thanks so much!
  7. 2 points
    That's what I was thinking about as well. Theming I'd divide up in 3 parts, which need to be handled: Getting data out of the persistance layer in a format, which is well defined to theme developers. Without knowing the data upfront I'm not sure how one would build a UI for it. Ways for theme code to format that data into markup snippets. A flexible way to compose those snippets into a whole page. (Having a way to handle http inputs (form submissions, get parameters, …)
  8. 2 points
    Thanks to this thread (and Adminer in TracyDebugger) I was able to fix a similar issue I encountered today! This community is awesome! πŸ‘
  9. 1 point
    Hi @teppo! Thx for your elaborate answer πŸ™‚ Thx, this sounds like the name of my module and the headline of this topic is somewhat misleading. Actually my take is closer to WireFrame than to WordPress Themes. Or maybe it is something in between. Well. I don't like site profiles πŸ™‚ Actually, I hate them πŸ˜… Site profiles are so rigid and one-way. What if I developed a new project, started with my great profile and implemented a new feature (like new SEO markup that was not supported yet. I'd have to implement that feature on my site, then I'd have to update my site profile if I wanted to use this feature for future projects. And then? What happens to the 10 sites that I've built using this site profile during the last year? I'd have to update all of them one by one. What I want is to implement a feature once and then just do a git pull to update all instances. Maybe I'm overengineering, though... I think our approaches are quite close. I might have only been missing the site-profile part when I played around with WireFrame. So I ended up with a blank site and thought: "Ok, that's not too much of help or not really what I wanted." The idea of RockThemes is to provide a framework on the one hand (with a helper class just like ryan has the uikit helper functions file), but in addition to that themes could also provide basic page blocks that one needs over and over again. In contrast to WordPress Themes this could be files that are completely decoupled from the site's field setup. An uikit slider for example could look like this: /site/templates/themeMaster/blocks/slider.php <?php // defaults if(!isset($images)) $images = $page->images; ?> <div class="uk-position-relative uk-visible-toggle uk-light" tabindex="-1" uk-slider> <ul class="uk-slider-items uk-child-width-1-2 uk-child-width-1-3@s uk-child-width-1-4@m"> <?php foreach($images as $image): ?> <li> <img src="<?= $image->url ?>" alt=""> <div class="uk-position-center uk-panel"><h1>Foo</h1></div> </li> <?php endforeach; ?> </ul> <a class="uk-position-center-left uk-position-small uk-hidden-hover" href="#" uk-slidenav-previous uk-slider-item="previous"></a> <a class="uk-position-center-right uk-position-small uk-hidden-hover" href="#" uk-slidenav-next uk-slider-item="next"></a> </div> This block (that can be shared across different projects and simply updated via git push/pull) could then be rendered in the theme like this: /site/templates/themeFoo/home.php <region id="main-slider"> <?= $theme->parent->render("blocks/slider.php", [ 'images' => $page->your_image_field, ]); ?> </region> This means that it would not be a problem at all if pages had different field names. I guess some of that is obsolet now? I think you also got the impression that I'm talking about WP-like themes?
  10. 1 point
    The dev2 I made to the master couple months ago. So the new commit is just a minor addition to the url parser. I think I had difficulties with making the dev2 to master and I couldn't delete the dev2 branch. 😒
  11. 1 point
    PWImage is enabled by default and the button itself is present by default as well. But it's only available in textarea fields and not text fields and you want to add an image field to your template as well. That makes things easier.
  12. 1 point
    @MFox, you need to install PWImage ckeditor plugin, enable it and add it to your toolbar (see screenshot).
  13. 1 point
    Also make sure that the "data" field/column in the field_body table is set to utf8mb4
  14. 1 point
    Have you set: $config->dbCharset = 'utf8mb4'; in config.php?
  15. 1 point
    Hey Bernhard! A few initial observations and notes – I will have to dig into your post deeper and check the video when I'm back home. On a trip at Spain at the moment, and the network connection here is... limited πŸ™‚ First of all I still find the idea of themes intriguing, but it's also a complex matter, and there are different aspects to consider. For an example WordPress has (as most here probably know) made great effort to support themes – yet in reality you cannot just switch between themes as you want (which is, in my opinion, a key point in the themes vs. starting profiles discussion) if you have something "out of the ordinary" going on: custom fields, custom views, plugins generating markup, etc. Wireframe doesn't actually try to solve the theming part. In fact it's quite the opposite: it's a framework that provides sensible structure for sites built with ProcessWire – one that separates the implementation into different files/classes ("business logic" vs. views vs. layouts and so on) in a meaningful way, allows for reusability, and continues to make sense even if/when the site scales. While it can be easy to make visual changes to a site built with wireframe – as long as it's used in its entirety, in which case making changes to a single layout file is often all you need – that's really the extent of it. The kind of things you've mentioned, like getting the framework and setting things up, are in that context resolved with site profiles: if you find yourself repeating these tasks over and over, you should build a site profile where you've got the defaults nailed down – or, if you find you often just swap the logo etc. then add a settings area and a custom field for it. The "official" Wireframe boilerplate is one of those, and here at Avoine we've got our own "boilerplate" specifically tailored for the type of sites we work with (and what you've listed are (some of the) things we've covered in it). Long story short: I think we're solving different problems, and as such I wouldn't draw too many lines between those two solutions πŸ™‚ I do agree that you're onto something here, though, and see a need for that. The way you've described RockThemes sounds pretty close to how WordPress works, although I've not had the chance to dig into all the details yet. For an example I don't have a good idea about how much you're planning to let a specific theme override vs. how much you plan to keep things "set in stone". That, I think, is actually one of the most important factors here: figuring out how much you can/should standardise stuff, and if one can "break loose" of the theme by overriding it in a way that changes it radically. ... and that, of course, depends on how far you want to go in terms of "themeability": if one wants to switch a theme and it should always "just work", you might go as far as define basic views and features (such list and archive views etc.) so that theme authors know what to provide markup/logic and styles for. On the other hand if it's enough that the layout is interchangeable and all content types (templates) that don't follow some specific convention (title and body fields, or something like that) will need per-site markup, then a much simpler approach is going to be quite enough. My "a few initial observations" got a bit out of hand, but I'll definitely check the video etc. later. And sorry if I've missed any important points here πŸ™‚
  16. 1 point
    @Orkun, do you know about $page->listable()? It determines whether a page may appear in Page List (and also PageListSelect inputfields, which is probably something you want too). So you could do: $wire->addHookAfter('Page::listable', function(HookEvent $event) { /* @var Page $page */ $page = $event->object; if(!$page->editable() && !$page->addable()) $event->return = false; }); You can hook ProcessPageListRender::getNumChildren to customise the child count. But in some cases depending on what is determining a page's listable status you might decide it's not worth the trouble. Here's one way you might do it (in /site/ready.php): // Work out which templates are editable or addable for the user and store them on the $user object // Doing this here to avoid doing it repeatedly in the getNumChildren hook if(!$user->isSuperuser() && $page->template == 'admin') { $user_roles_ids = $user->roles->explode('id'); $allowed_templates = []; foreach($templates as $template) { $edit_add_roles = array_merge($template->editRoles, $template->addRoles); if(count(array_intersect($user_roles_ids, $edit_add_roles))) $allowed_templates[] = $template->name; } $user->allowed_templates = $allowed_templates; } // Set a selector to correct the children count $wire->addHookBefore('ProcessPageListRender::getNumChildren', function(HookEvent $event) { $user = $event->wire('user'); if($user->isSuperuser()) return; $selector = 'template=' . implode('|', $user->allowed_templates); $event->arguments(1, $selector); }); Be aware that there are some other areas you probably want to consider if you are hiding pages in Page List. Here are some pointers to get you started: Pages that can be found in the admin search - look at manipulating the selector by hooking ProcessPageSearch::findReady Pages that can be found in Lister - look at manipulating the selector by hooking ProcessPageLister::getSelector
  17. 1 point
    @ryan Oh WOW, a dream comes true! 😍 😍 😍 😍 😍 😍 😍 😍 😍 😍 😍 😍 😍 😍
  18. 1 point
    Just played around with your example and did a little refactoring πŸ™‚ Thx for sharing your code! $this->addHookAfter('ProcessPageList::find', function(HookEvent $event) { $pages = $event->return; $excludePagesByTemplate = array('admin', 'basic-page'); $pages->each(function($p) use($pages, $excludePagesByTemplate) { if(!in_array($p->template, $excludePagesByTemplate)) return; if(!$p->editable() && !$p->addable()) $pages->remove($p); }); $event->return = $pages; });
  19. 1 point
    This is exactly what I want to do πŸ™‚ I will try to do it with ajax, thanks for your suggestions!! Right now Iam busy with other projects, but I will try this as soon as I can get back to it. I will keep you posted.
  20. 1 point
    Hi, @jploch . Can you explain the workflow, or maybe do a diagram. What I think you want is: - Admin interface uses gridstack.js - In this context (ie your module building on PageTableExtended), each item in the grid contains the child page preview, and the page that is being previewed is where the data for gridstack.js is stored / set If you want to save the data after dragging / resizing an element, I'd probably leverage events from the api here https://github.com/gridstack/gridstack.js/tree/develop/doc and set the data via ajax. To do that you just need an endpoint to send the data to, eg /pw/setgriddata/. Below code is not tested, just an example as I haven't used gridstack.. In the module js file: $('.grid-stack').on('gsresizestop', function(event, elem) { var $elem = $(elem); var data = { width: $elem.data('gs-width'); height: $elem.data('gs-height'); x: $elem.data('gs-x'); y: $elem.data('gs-y'); id: $elem.data('id'); } $.ajax({ url: "/pw/setgriddata/", data: data, }).done(function( msg ) { console.log( msg); }); }); At /pw/setgriddata/: if ($config->ajax && $input->post) { if ($p = $pages->get("id={$input->post->id}") { $p->data_gs_width = $input->post->width; // etc $p->save(); return "Updated page: " . $p->title; } else { return "Could not find page: " . $input->post->id; } } You probably don't want to do this on all resizes however. If you want to do it on page save, again I would probably use gridstack events to populate the input fields you are rendering. Again, very quick... $('.grid-stack').on('gsresizestop', function(event, elem) { var $elem = $(elem); $elem->find(".grid-stack-input#data-gs-width")->val($elem.data("gs-width")); // etc });
  21. 1 point
    When a blog post article receives many comments πŸ˜€ your start to consider to apply pagination to comments. Looking around this forum, I found some useful hints, but I couldn't find how to take full advantage of PW built-in pagination. In particular the ukBlogPosts() function in Uikit 3 Site/Blog Profile renders a list of paginated posts. ukBlogPosts() is calling a ukPagination() function which permits to navigate the posts. 😍 After some unsuccessful attempts to use it with CommentArray … 😠 I decided to insist. 😜 And here is the result. The ukPagination() function in _uikit.php expects an argument $items of type PageArray. Of course a CommentArray ... is not a PageArray ... πŸ€ͺ so we have to remove this blocking point. Going deeper inside the function you will notice that pagination is achieved through a call to a render() method of MarkupPagerNav. Let's look at its declaration: public function ___render(WirePaginatable $items, $options = array()) Interesting πŸ€” here $items are declared of type WirePaginatable. What's that? 🧐 a class? No! It's an interface! 😜 Looking around further I discovered that WirePaginatable interface is used by PageArray, PaginatedArray, and CommentArray.. bingo! 🍾🍾 We got what to do first, modify ukPagination() function in _uikit.php changing $items argument type from PageArray to WirePaginatable: //function ukPagination(PageArray $items, $options = array()) { function ukPagination(WirePaginatable $items, $options = array()) { //>>>>> REPLACE WITH THIS LINE Now the function accepts our CommentArray as argument, but to make it work we need to set its Limit, Start, and Total attributes as PW does automatically when dealing with pages. In order to do that we will use the WirePaginatable methods setLimit(), setStart(), setTotal(). In this process our Start attribute will have to be calculated dynamically to consider the page number when navigating the comments. To do that we will largely 😫 modify the function ukComments() in _uikit.php: function ukComments(CommentArray $comments, $options = array()) { if(!$comments->count) { //>>>>>NEW-start if(input()->pageNum > 1) { // redirect to first pagination if accessed at an out-of-bounds pagination session()->redirect(page()->url); } return ''; } //>>>>>NEW-end $defaults = array( 'id' => 'comments', 'paginate' => false, //>>>>>NEW 'limit' => 3, //>>>>>NEW ); //if(!count($comments)) return ''; $options = _ukMergeOptions($defaults, $options); $language = user()->language->id; //>>>>>NEW $comments = $comments->find("language=$language"); //>>>>>NEW if($options['paginate']) { //>>>>>NEW-start $limit = $options['limit']; $start = (wire()->input->pageNum - 1) * $limit; $total = $comments->count(); $comments = $comments->slice($start, $limit); $comments->setLimit($limit); $comments->setStart($start); $comments->setTotal($total); } //>>>>>NEW-end $out = "<ul id='$options[id]' class='uk-comment-list'>"; foreach($comments as $comment) { $out .= "<li class='uk-margin'>" . ukComment($comment) . "</li>"; } $out .= "</ul>"; if($options['paginate'] && $comments->getTotal() > $comments->count()) { //>>>>>NEW-start $out .= ukPagination($comments); } //>>>>>NEW-end return $out; } As you will notice we have added two options to the function [paginate => false] and [limit => 3]. In such a way the ukComments() function will operate as before. If you wish to use pagination you will have to pass [paginate => true] as argument of the function. Of course you can freely choose the limit value or by pre-setting it in the function or by passing it as an argument to the function. Please note that the modification implements the necessary changes (2 lines) to make comments language-aware as described in the tutorial: We are now just to steps away to see our paginated comments. 😜 First you will need to enable pagination in the templates where you want comments to be paginated. Login to PW Admin and select your template. In the tab URLs enable the "Allow Page Numbers?" option. Second you will need to modify the associated php template, in our example blog-post.php, to change the call to ukComments() as follows (sorting is not necessary, depends on your preferences): //echo ukComments($comments); echo ukComments($comments->find("sort=-created"), ['paginate' => true]); //>>>>> REPLACE WITH THIS LINE And here we are! Below a snapshot our paginated comments! 🍾🍾🍾 As you can see on the bottom of the comments you have the standard PW pagination navigator, without being forced to rewrite a new one for the purpose! 🀩 I hope you can find something useful with this tutorial.
  22. 1 point
    @horst Great, thanks for the explanation. Despite such rare errors I personally prefer those loose connections between Dev (local), Staging and Production, compared to the stricter ones you usually need with Craft.
  23. 1 point
    Just edit the url of the "Admin" page in the pagetree.
  • Create New...