Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 03/09/2020 in all areas

  1. Memo to myself and mybe to safe someone else from losing hours trying to understand why a multi-language Inputfield does not render() as expected... Learnings: 1) Inputfield::render() is only hookable for single-language Inputfields! When PW renders a multi-language Inputfield the LanguageSupport module adds a hook that fires after the original Inputfield::render(). This hook calls the render() method for each language and to avoid circular references it does that directly on $inputfield->___render() and not $inputfield->render(): https://github.com/processwire/processwire/blob/51629cdd5f381d3881133baf83e1bd2d9306f867/wire/modules/LanguageSupport/LanguageSupport.module#L445-L453 2) When building a custom Inputfield that supports a multi-language setup it is critical that its render() method is defined with 3 underscores! Otherwise the LanguageSupport hook that adds the markup for the other languages' fields would not fire.
    4 points
  2. @creativejay I must admit, now I've gotten myself into a bit of a mess. Unfortunately, most of the hookable methods are not yet ready. I am currently working on the final implementation. Sorry, you'll have to be patient. i’ll let you know when it is ready.
    3 points
  3. I need to build two online stores in the near future. @kongondo, I was wondering if you could share any updates on the progress. Or is it still too early for any kind of announcement? ?
    3 points
  4. JqueryFileUpload This module is a ProcessWire implementation of the awesome Blueimp jQuery File Upload plugin. Server-side, the module provides a custom uploads' handler enabling you to perform various tasks with ease. The module is an interface of the feature-rich Ajax File Uploads widget provided by the jQuery File Upload plugin. The module is completely customisable and can be used both in the front- and backend (e.g. in a third-party module). Please read the README carefully and completely before using the module Release Status: Stable. Module Download: http://modules.processwire.com/modules/jquery-file-upload/ Issues tracker Project page: GitHub Security The module has been written with security in mind and makes no assumptions about any client-side validation. Instead, the module provides robust server-side validation of uploaded files. Server-side, no Ajax requests are honoured unless specifically set via configurable options server-side. This means that client-side requests to upload, delete and list files are not executed unless allowed server-side. By default, files are uploaded to a non-web-accessible (system) folder and files previously uploaded on the server are not sent back for display unless that setting is enabled. However, developers are still strongly advised to implement any other feasible measures to guard against malicious uploads, especially if allowing frontend uploading. For instance, developers can use native ProcessWire checks to limit access to the widget (e.g. only allowing uploads by registered/logged-in users). Demo A short video demo can be found here (and below )(CSS is WIP! ). In the backend, you can see it in action within the (upcoming) module Media Manager Features Fast Ajax uploads. Client and server-side validation. Client-side image resizing (highly configurable options). Beautiful touch-responsive image gallery preview. Audio and video previews pre-upload. Chunked and resumable file uploads (currently client-side only; server-side handling planned). Drag and drop support. Copy and paste support (Google Chrome only). Progress bars. Cross-domain uploads. Single or multiple uploads. Delete uploaded files. Documentation On GitHub. Have a look at the long list of available options. License Released under the MIT license @Credits: Sebastian Tschan @Thanks: Pete and BernhardB for the idea. Please test and provide feedback. Thanks!
    1 point
  5. Hey @bernhard - sure thing. I did the same for template deletion as well.
    1 point
  6. Nope. One uses React, the other plain vanilla JS (ES6). You are actually asking more than one question. How to display such stuff in the frontend? (to some degree answered by your CodePen examples... but I doubt you want to use React just for one single widget like that) How to display such stuff in the frontend with contents that come from PW? How to save the user's combinations / choices (result lists) in PW? How familiar are you with ProcessWire? How familiar are you with JS?
    1 point
  7. Not with the built-in anchor select. If you don't mind a bit of regex ugliness: wire()->addHookAfter("ProcessPageEditLink::execute", function(HookEvent $event) { $page = wire('pages')->get((int)wire('input')->get('id')); if(wire('input')->get('href')) { $currentValue = wire('sanitizer')->url(wire('input')->get('href'), array( 'stripQuotes' => false, 'allowIDN' => true, )); } else { $currentValue = ''; } $inp = wire('modules')->get('InputfieldSelect'); $inp->attr('id+name', 'select_anchor'); $inp->label = wire()->_('Select Repeater Anchor'); $inp->icon = 'anchor'; $inp->collapsed = $currentValue ? Inputfield::collapsedNo : Inputfield::collapsedYes; // Instead of addOptions here, fill your select however you want $inp->addOptions([ "" => "", "#some_anchor" => "some_anchor_text", "#other_anchor" => "other_anchor_text", "#third_anchor" => "third_anchor_text" ]); if($currentValue) $inp->attr('value', $currentValue); $wrap = new InputfieldWrapper(); $wrap->append($inp); // It's a bit ugly, but to get our InputfieldSelect to render correctly, we need // it rendered by an InputfieldWrapper. We do that, then strip out the wrapping // parts afterwards (i.e. everything before the first <li> and after the last </li>). $html = $wrap->render(); $html = preg_replace('~^.*?(?=<li\b)~is', '', $html); $html = preg_replace('~</li>.*?$~is', '', $html); $js = '<script> $("#select_anchor").bind("change", function(evt) { $("#link_page_url_input").val($(evt.target).val()).change(); }); </script> '; $html .= $js . "</li>"; $event->return = preg_replace("~(?=<li[^>]+id='wrap_link_page_file')~", $html, $event->return); });
    1 point
  8. Are you saying that there is something wrong with my proposed solution, or are you saying that there is something else wrong with the existing field type? I think either your post went over my head, or you may be misunderstanding my proposed solution. I think the solution is not actually that complicated. ProcessWire natively works with timestamps at runtime, which are always UTC-based. If I save something as a certain timestamp and then my PW/server/php time changes for any reason, I should be able to expect that the timestamp I get back from the field remains the same as the one I put into it. This is how it would work if the field stored a UTC string instead of a local string. Currently, what I get back is effectively a corrupted/meaningless value. I cannot change my $config->timezone once I've initially set it. What timezone the user sees or inputs a date in on the site's front end is a separate issue and is up to the programmer to determine and make clear to the user and convert to/from as necessary. But the programmer should be able to trust the timestamp they are working with when they save and retrieve it from the database. What does this mean?
    1 point
  9. These updates sound great. Thanks, Ryan! On a related subject, I recently (and painfully) discovered that the DateTime field stores dates in the database as a string (MySQL DateTime) in whatever timezone PW is currently configured to. So if you change your PW time zone, the date string you get back from the DB is now interpreted to be in that new time zone rather than the one it was originally entered as. In other words, the unix timestamp you get out of it is no longer the same as the one you put into it. It seems to me that the "correct" way to handle this would be for the unix timestamp to always be converted to and stored as a UTC string in the MySQL DateTime field, and then converted back to PW's current timezone at run time. This would be an extremely simple change to PW's DateTime field (using gmdate() instead of date() when storing and using php's DateTime class with UTC timezone specified when getting the value back). Then when someone changes their timezone in PW, the absolute values of the dates would stay the same. Only the time zone (how they are represented on the front end) would change. So different users could have different time zone settings and view the PW back end in their own time zone, etc. The problem, of course, is that this would break existing installations, so this would have to be added as a new module or as an alternative version included in the core. Ideally when you converted an existing datetime field to the new version it could automatically update your database values from the current PW timezone to UTC.
    1 point
  10. Hi @creativejay, you can hook into SnipWire‘s predefined webhook methods. Please have a look into the Webhooks class starting at this lines: https://github.com/gadgetto/SnipWire/blob/8d38ced82bbfc5633a3d6de7cae6217fcd408c39/services/Webhooks.php#L312 For creating/importing products it would be best to write a small importer using the PW API which creates SnipWire (ProcessWire) pages.
    1 point
  11. @adrian could you please add the name of the field in the admin actions panel so that it is more obvious and secure? Here I'm deleting the field "summary" but it looks more like I am deleting the field "Text". A simple "Delete field > summary < " would do. I think it's a little dangerous as it is now - it's common to have many tabs open at the same time, so there's a high risk of clicking that button in the wrong tab accidentally. Thank you ?
    1 point
  12. Related Issue was opened https://github.com/processwire/processwire-issues/issues/1113
    1 point
  13. I don't know if I understood correctly, but it looks like you can also use $files->include() or $files->render() methods in your _main.php file. A simple example from the Regular Uikit 3 profile, which we will include the _header.php inside file _main.php with some variables: <?php namespace ProcessWire; // _main.php template file, called after a page’s template file $home = pages()->get('/'); // homepage $siteTitle = 'Regular'; $siteTagline = $home->summary; // as a convenience, set location of our 3rd party resources (Uikit and jQuery)... urls()->set('uikit', 'wire/modules/AdminTheme/AdminThemeUikit/uikit/dist/'); urls()->set('jquery', 'wire/modules/Jquery/JqueryCore/JqueryCore.js'); // ...or if you prefer to use CDN hosted resources, use these instead: // urls()->set('uikit', 'https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-beta.40/'); // urls()->set('jquery', 'https://code.jquery.com/jquery-2.2.4.min.js'); ?> <!-- HEADER --> <?php $files->include('_header', ['home' => $home, 'siteTitle' => $siteTitle, 'siteTagline' => $siteTagline]); // echo $files->render('_header', ['home' => $home, 'siteTitle' => $siteTitle, 'siteTagline' => $siteTagline]); ?> _header.php: <?php namespace ProcessWire; // _header.php file ?> <!DOCTYPE html> <html lang='en'> <head id='html-head'> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title id='html-title'><?=page()->title?></title> <meta name="description" content="<?=page()->summary?>"> <link rel="stylesheet" href="<?=urls()->uikit?>css/uikit.min.css" /> <link rel="stylesheet" href="<?=urls()->templates?>styles/main.css"> <script src="<?=urls()->jquery?>"></script> <script src="<?=urls()->uikit?>js/uikit.min.js"></script> <script src="<?=urls()->uikit?>js/uikit-icons.min.js"></script> </head> <body id='html-body'> <!-- MASTHEAD --> <header class='uk-background-muted'> <div id='masthead' class="uk-container"> <h2 id='masthead-logo' class='uk-text-center uk-margin-medium-top uk-margin-small-bottom'> <a href='<?=urls()->root?>'> <img src='<?=urls()->templates?>styles/images/coffee4.svg' alt='coffee'><br /> </a> <?=$siteTitle?> </h2> <p id='masthead-tagline' class='uk-text-center uk-text-small uk-text-muted uk-margin-remove'> <?=$siteTagline?> </p> <nav id='masthead-navbar' class="uk-navbar-container" uk-navbar> <div class="uk-navbar-center uk-visible@m"> <?=ukNavbarNav($home->and($home->children), [ 'dropdown' => [ 'basic-page', 'categories' ] ])?> </div> </nav> </div> </header> It is also important to include <?php namespace ProcessWire; ?> in each of template files. Sometimes it also helps to enable $config->useFunctionsAPI in the config.php file ( Allow most API variables to be accessed as functions? ), this will be more useful in your custom functions, where for example you can call page()->title instead of $page->title, which will not work inside a your custom function if you do not enter any arguments, as in the example below: <?php function showTitle() { echo '<h1>' . page()->title . '</h1>'; // This work // echo $page->title; // This not work } // Show Title showTitle(); ?> The setting() helper function is a good addition to the _init.php file, to which I usually attach the most important page options, which are also available in custom functions without entering arguments such as the init.php file: <?php namespace ProcessWire; /** * * This _init.php file is called automatically by ProcessWire before every page render * * Get or set a runtime site setting * @link https://processwire.com/api/ref/functions/setting/ * */ /** @var ProcessWire $wire */ // set or replace multiple settings setting([ 'siteTitle' => 'ProcessWire CMS / CMF', 'siteDescription' => 'ProcessWire is like a power tool for your website, able to deliver any output, at any scale, to any number of people. ', ]); // Your custom functions that are better placed in the _func.php or _uikit.php file (this is just an example) function siteBranding() { echo "<h1>" . setting('siteTitle') . "</h1>"; echo "<h2>" . setting('siteDescription') . "</h2>"; } include_once('./_uikit.php'); Now you can display the function wherever you want: <?php siteBranding() ?> For example, in this Site Profile I have put site options or translations inside file _init.php: <?php namespace ProcessWire; /** * * This _init.php file is called automatically by ProcessWire before every page render * * Get or set a runtime site setting * @link https://processwire.com/api/ref/functions/setting/ * */ // as a convenience, set location of our 3rd party resources (jQuery)... urls()->set('jquery', 'https://code.jquery.com/jquery-3.4.1.min.js'); $home = pages('/'); $blog = pages()->get("template=blog"); $options = pages()->get('/options/'); // Basic Settings setting([ // Custom html classes 'htmlClasses' => WireArray([ 'template-' . page()->template->name, 'page-' . page()->id, ]), // Basic 'home' => $home, 'privacyPolicy' => pages()->get("template=privacy"), 'options' => $options, 'siteName' => $options->text, 'logo' => $options->logo, 'favicon' => $options->favicon, 'socialProfiles' =>$options->social_profiles, 'metaTitle' => page('meta_title|title'), 'metaDescription' => page()->meta_description, 'noindex' => false, 'poweredUrl' => 'https://processwire.com', // Home Sections 'hero' => $home, 'about' => pages()->get("template=about"), 'projects'=> pages()->get("template=projects"), 'recent' => $blog->child(), // Path to template parts 'homeParts' => 'parts/home', 'blogParts' => 'parts/blog', // Blog 'blog' => $blog, 'disableComments' => $options->more_options->get("name=disable-comments"), // Contact Page 'saveMessages' => $options->more_options->get("name=save-messages"), // Images 'img' => page()->images && page()->images->count() ? page()->images : '', // Main 'mainTitle' => page('title'), 'mainImage' => true, // Set basic background image for all pages // Bottom Panel 'bottomPanel' => $options->bottom_panel, // Basic Translations 'lang' => __('en'), 'edit' => __('Edit'), 'next' => __('Next'), 'previous' => __('Previous'), 'search' => __('Search'), 'search-site' => __('Search the entire site'), 'found-matches' => __('Found %d page(s)'), 'no-results' => __('Sorry, no results were found.'), 'maintenance-mode' => __('Maintenance Mode'), 'site-disabled' => __('Our site is currently disabled.'), 'to-top' => __('To top'), 'we-sociable' => __('We are sociable'), 'powered' => __('Probably supported by ProcessWire CMS'), // Contact Page Translate 'message-error' => __('Some errors, please update your form'), 'message-success' => __('Success, your message has been sent'), 'txt-from' => __('From'), 'form-legend' => __('Contact Us'), 'form-name' => __('Name'), 'form-email' => __('Email'), 'form-privacy' => __('I agree with the %s terms.'), 'form-message' => __('Message'), 'form-spam' => __('To help prevent automated spam, please answer this question'), 'fs-placeholder' => __('* Using only numbers, what is 10 plus 15?'), 'fs-error' => __('Fill out the spam prevention box correctly'), 'form-submit' => __('Submit'), // Blog Translate 'in-blog' => __('In the blog'), 'posted-in' => __('Posted in'), 'all-posts' => __('All posts'), 'recent-posts' => __('Recent posts'), 'read-more' => __('Read More'), 'written-on' => __('Written on'), 'byline-text' => __('Posted by %1$s on %2$s'), 'also-like' => __('You might also like:'), 'author' => __('Author'), 'authors' => __('Authors'), // is also url segments ( blog/authors/author-mame ) 'category' => __('Category'), 'tag' => __('Tag'), 'author-checkbox' => __('You probably need to check the author checkbox in your user profile'), 'rss' => __('RSS'), 'recent-entries' => __('Recent entries'), // Comments Form Translate 'previous-comments' => __('Previous Comments'), 'next-comments' => __('Next Comments'), 'post-comment' => __('Post a comment'), 'comment' => __('Comment'), 'comments' => __('Comments'), 'no-comments' => __('No Comments'), 'comments-closed' => __('Comments are closed'), 'comment-header' => __('Posted by {cite} on {created}'), 'success-message' => __('Thank you, your comment has been posted.'), 'pending-message' => __('Your comment has been submitted and will appear once approved by the moderator.'), 'error-message' => __('Your comment was not saved due to one or more errors.') . ' ' . __('Please check that you have completed all fields before submitting again.'), 'comment-cite' => __('Your Name'), 'comment-email' => __('Your E-Mail'), 'comment-website' => __('Website'), 'comment-stars' => __('Your Rating'), 'submit' => __('Submit'), 'stars-required' => __('Please select a star rating'), 'reply' => __('Reply') ]); include_once('./_func.php');
    1 point
  14. The replies above by Ryan and BitPoet pretty much covered the questions you raised, so just dropping a quick comment regarding this ? I tend to fuss a lot over things like organising the project, so I'd say that I'm familiar with your concerns. In my opinion a sensible structure is a key design decision, and even more so when you're working with others. So yeah, I can't disagree with anything you've said here. Regardless, after giving this a lot of thought I ended up organising files by type — at least I think that's what you'd call it — in Wireframe. By this I mean that I've indeed bundled controllers in one directory, components in another, views in a third one, etc. For me this just makes more sense: When I add a new template and a view for it (assuming that it isn't already covered by some shared code, which is quite often the case), I'll probably start from the controller. I may copy-paste some parts from other controllers, or perhaps I'll extend another one if I have a very similar need at hand. Some controllers may use other controllers as sort of "subcontrollers", and often there are shared libraries as well. After the "backend" is mostly done, I'll move on to the view in order to actually put those methods to use. In other words I tend to work in layers, going from backend to the frontend. (This is just the way I like to work, and I definitely don't assume everyone to prefer the same approach.) Things like components or partials are made to be shared. If I only need a specific piece of code once — or in one template — then it probably doesn't need to be abstracted away. That'll just make things harder to grasp and add a tiny bit of overhead without actually providing much extra value. Copying files from project to project has been a relatively rare need for me, so when it does come up, it doesn't matter much if I have to copy one directory, or perhaps a couple of files from a couple of directories. That being said: in an old environment this need did come up often (and the structure was pretty complex), so I ended up writing a script to automate it ? It's true that the process of building a new site often involves moving back and forth between backend and frontend, but I still prefer this sort of structure over the alternatives. Also: I find it a tad easier to maintain after the site is already live, since often a specific change will only affect one "layer" of the site. Anyway, this is largely a matter of preference ? Exactly. When I first read about custom Page classes, my first thought was that this stuff is frigging cool... but I probably won't be using it anytime soon. I fully agree that it's a great feature, but in my case I've already solved it in a way that — in my opinion — fits my needs better. I may end up using this for some stuff eventually, but right now I don't have a need for it, and that's fine ?
    1 point
  15. @Zeka Thanks, I think I tracked down the issue (caching issue that occurs only when multiple date fields) and have pushed a fix on the dev branch. @szabesz From your /site/init.php you can set the location with $config->setLocation('classes', 'path/to/classes/'); for example $config->setLocation('classes', 'site/templates/classes/'); You can also use the method mentioned earlier by adding your own directories to the $classLoader. However, the more potential locations there are for something, the more overhead, so I would recommend sticking to the $config->setLocation() and keep all of your classes in one directory. This keeps everything fast and efficient. If that's the case, then you would have to add locations with the $classLoader and accept the overhead of doing so (though it's probably negligible so long as things don't grow too much). But I also think with that kind of structure, things look isolated when it comes to reusability, limiting their usefulness. That may be fine for your use case, but as far as classes go, one of the primary purposes is reuse and extending one another where appropriate, so you ideally want to keep them together. @bernhard ProcessWire preloads a few pages on every request, since they always get used. Preloaded pages are loaded as a group rather than individually, enabling it to boot faster. This happens before the init state for modules, so a module will not have the opportunity to specify a class for these preloaded pages, but a file in /site/classes/ will. See the $config->preloadPageIDs setting for a list of what those pages are. But essentially they are the homepage, admin root page, guest user+role, and superuser role. For your case, it sounds like only the homepage is a consideration here. I would suggest just asking PW to uncache the homepage at your module's init() state. This should force it to reload (when it's next requested) using your custom class. $homePage = $this->pages->cacher()->getCache(1); if($homePage) $this->pages->uncache($homePage); You can also use $config->setLocation() for this, i.e. $config->setLocation('fieldTemplates', 'site/templates/my-custom-directory/'); I think this is fine if you are just doing it once or twice, but if you find you are repeating yourself, typing out "/articles/fields" or similar in multiple places, then you always want to abstract away stuff like that into a single location so that if you ever need to change it, you change it in just one place. PHP functions are ideal for this. This will be simple to do, but when it comes to the directory and file names that you are using, I recommend using the exact same names that you do with your templates and fields, which will make it a simple matter to abstract away repetitive code or locations into functions. "All these directories" are 3 directories, 2 of which are for optional features that are disabled by default — the absolute minimum number of directories necessary to support these things. ProcessWire is focused in what is the most simple, efficient, familiar (as it relates to the admin), and maximizes reusability. It's not opinionated about it outside of those factors. For something like field-templates, a primary purpose of having them is so that you can reuse them across different templates (to avoid repeating yourself); otherwise they aren't that useful. For the structure that you appear to be using, it looks to me like your intention is to isolate them by template, so why have them at all? If the purpose is code/markup isolation (which is still worthwhile) then I think just using an include() or $files->render(); would be preferable, more efficient and flexible for your use case. So that's what I'd recommend there. The same goes for the new page classes—if you are isolating these things in the way you've suggested, it'll be confusing for classes to extend one another or be shared across multiple templates, and it'll take more overhead for PW to find them. If you don't intend to use them like that, maybe they can still be useful, but not nearly as much so. So at that point I would reconsider whether you want/need custom Page classes. Basically, you are using a highly opinionated structure for your site files, and that can be a good thing because you've decided where everything goes and have a plan/system for how it will grow in terms of your file structure. ProcessWire is built to support a lot of different types of websites and applications, and at any scale. But it's not opinionated about this stuff beyond the bare minimum, precisely so that you can be as opinionated as much as you want with your own projects. That's why you are able to build a more complex structure for your files that suits your need, and also why it's also just as suited for others that might prefer a simpler structure in their projects. There's also no need to feel obligated to use things like field templates or custom page classes just because they are there. The point of these features is to reduce redundancy and prevent you from having to repeat yourself. But you may have your own preferred ways of doing that—whether it's with functions, file/directory structure, or anything else, it's all perfectly fine in PW. A template file receiving a $page is the only assumption needed in ProcessWire and everything else is optional. So use these things when they help you do that, and avoid using them when they don't. The reason these features are disabled by default is because not everyone needs them. Personally, the only time I use field templates is for multi-value fields with lots of properties, like repeaters, where I need to reuse the same output/markup across multiple template files. Though custom Page classes I'm already using quite a bit and likely will in most cases.
    1 point
  16. With klaro consent manager you can gain control over the scripts loaded by GTM. See this issue. (I'm not affiliated with that project in any way, just using it on a couple of sites) It is implemented via custom callback functions for each app that is managed by the consent manager. @joshua Maybe you could go a similar route to make configuration more flexible and tweakable? And thanks for putting this together! And a suggestion for improvement: The type="optin" attribute is not a valid script attribute. So W3C Validator will not like it. You could use text/plain instead. That is not very semantic but could help to pass validator tests...
    1 point
  17. It's Linux for me anyday anytime, there are so much work I get down easily, also the terminal, I can pipe and do crazy stuff, I just installed awesomeWM and looking to play with that, I like i3 but learning C++ to customize my UI is too big of a task to take, with awesomeWM I can pick up Lua and customize my os. Been running Linux since Ubuntu 6 I think and always loved it as my programming environment.
    1 point
  18. You are why the internet thrives. Thanks, Robin.
    1 point
×
×
  • Create New...