Jump to content


Popular Content

Showing content with the highest reputation on 02/04/2020 in all areas

  1. 3 points
    Inputfield Selector Select ID Uses the Page List Select inputfield for user-friendly input of page IDs into Inputfield Selector. Overview This module adds a feature to Inputfield Selector, which is most commonly seen in Lister (Find) and Lister Pro. When adding a filter row for "Parent", "Has parent/ancestor" or "ID" the user is expected to enter a page ID to match against. But this is not as user-friendly as it could be because the user may be able to identify the desired page by its title or location in the tree but not know its ID. This is particularly the case for site editors who may not even understand the concept of a page ID. So this module adds a thunderbolt icon to relevant rows in Inputfield Selector. When the icon is clicked a Page List Select inputfield opens in a modal window, allowing the user to visually select a page from the tree. When the modal is closed with "OK" the ID of the selected page is inserted into the filter row. Tip After a page selection has been made in the modal window the "OK" button will automatically receive focus so if you prefer you can close the modal by hitting the Enter key rather than mousing to the OK button. Installation Install the Inputfield Selector Select ID module. https://github.com/Toutouwai/InputfieldSelectorSelectID https://modules.processwire.com/modules/inputfield-selector-select-id/
  2. 3 points
    Hi all, sorry this one has been dragging on so long - I'm aware of the issue, just trying to work around it in a sensible way that doesn't break with the next forum upgrade as I think that's what happened in the first place having looked into it last week. I'm hopeful I can sort it finally this week.
  3. 2 points
    @Robin S - I know you already know that your request has been done, but just letting everyone else know that the Restore functionality is now built into the interface's tab system. Hopefully everyone will find this a useful enhancement.
  4. 2 points
    Thanks teppo - I'll take a long-overdue look πŸ™‚
  5. 2 points
    Hey Pete, Thanks for looking into this! Note that this (probably) same issue is also discussed in the issues repository (https://github.com/processwire/processwire-issues/issues/1058). That issue lists some related threads here on the forums πŸ™‚
  6. 2 points
    Heya! Just released Wireframe 0.9.0. Here's the changelog for this version: ### Added - New EventListenerTrait. Currently used by Components only. Adds support for listening to and emitting events. - Support for Renderer modules for adding templating engine support for view files, component view files, etc. - New Page methods Page::viewTemplate(), Page::getViewTemplate(), and Page::setViewTemplate(). - New method Component::getData() for manually defining the data passed to the component view. ### Changed - Controller::init() and Controller::ready() are now hookable methods. - Component::setView() and Component::getView() are now final methods, preventing accidental overrides. - Layout file is no longer necessary; if it's missing, the page can be rendered using just a view file. The component system is a bit more mature now, and there are a couple of new features: Component::on('event-name', callable $callback) can be used to listen to events emitted by a Component instance, and in a Component class events can be emitted with $this->emit('event-name', array $args). This is mostly just syntactic sugar on top of the hook system, but it seemed fun and potentially useful, so... πŸ˜… The Component base class extends WireData, so by default what gets sent to the component view when a component is rendered is the internal $data array. Since 0.9.0 it's possible to alter this behaviour by implementing getData() method which returns the data that the component should expose to its view. Another "big" update is support for renderers. These are add-on modules that add support for different rendering / templating engines to Wireframe, and the first example is the Wireframe Renderer Twig module. I haven't really used a templating engine in a while so it's possible that I've missed something important, but in my limited tests the Twig engine seemed to work quite well right out of the box. I'll likely add a separate module for Latte at some point, just to make sure that the logic works for other engines as well πŸ™‚ By the way, it looks like I forgot to post here about Wireframe 0.8.0. It was released last month, and here's the related changelog entry: ### Added - Support for Components, along with a new static factory method Wireframe::component($component_name, $args). - Support for rendering pages that have not been "routed" to Wireframe using the altFilename template setting. - New static getter/factory/utility method Wireframe::page($source, $args). - New static utility method Wireframe::isInitialized(). ### Changed - Wireframe::$initialized is now a static property. This was a necessary change so that Wireframe::isInitialized() could be implemented effectively. On a related note I'm considering tagging the next release as 1.0.0. We've been using Wireframe on production sites for a while now, and I can't recall any truly breaking changes so far, so I think it's already quite stable πŸ™‚
  7. 1 point
    I believe it is also fixed in: https://github.com/adrianbj/MarkupSEO/commits/various-fixes-enhancements As far as I know, this is the most improved version of the original module available - lots of bug fixes and improvements. That said, I am not maintaining it anymore, but if anyone wants to keep it going, I would start from that version. Otherwise, take a look at SEOMaestro.
  8. 1 point
    @Pete Just out of curiosity: Did you find out what caused these outages? At one point I could access the forums, but wasn't able to post comments (disabled form), and saw lots of JS errors in the console. Did you update the forum or server recently?
  9. 1 point
    This is related to the Wireframe::page() static utility method (https://wireframe-framework.com/docs/static-utility-methods/) but not specifically covered in the docs, I think. This is also one of the things I struggled trying to explain briefly in the changelog πŸ™‚ The general idea is that since Wireframe works through the Alternate Template Filename setting of templates, in earlier versions you couldn't do things like $pages->get(1234)->setLayout(null)->render() and expect the output to come from a Wireframe view UNLESS the Alternate Template Filename for the template this page uses had the alt filename pointing to a Wireframe bootstrap file (i.e. /site/templates/wireframe.php). After this update the syntax mentioned above will β€” in most cases at least β€” work right out of the box. And even in cases where it won't work (such as when you're fetching data from ProcessWire via a command-line script), Wireframe::page(1234)->setLayout(null)->render() will almost certainly work. Typical use case for me are pages that are not supposed to be directly accessible, like a contact page that lives under a shared "bucket" (e.g. /data/contacts/lastname-firstname/) and is only ever made visible when rendered as part of another page's markup. If I only need to output a few fields from the page I can do that directly in the view file for the containing page, but if I need to reuse this data all over the site, I prefer to define a view file for the contact (e.g. /site/templates/views/contact/embed.php) and render it using that: <!-- some page that lists contacts --> <?php foreach ($page->contacts as $contact): ?> <?= $contact->setLayout(null)->setView('embed')->render() ?> <?php endforeach; ?> Hope this makes more sense πŸ™‚
  10. 1 point
    Great work @teppo! Could explain a bit this feature "- Support for rendering pages that have not been "routed" to Wireframe using the altFilename template setting." Or just point the docs - did not fins corresponding article there.
  11. 1 point
    Hey, would also love this. More links below: Some other options: https://github.com/madebymany/sir-trevor-js https://github.com/vigetlabs/colonel-kurtz https://editorjs.io https://docs.slatejs.org HN discussion on this topic: https://news.ycombinator.com/item?id=19555633 WSIWYG type editor lists: https://github.com/JefMari/awesome-wysiwyg https://gist.github.com/manigandham/65543a0bc2bf7006a487 What bard is build with: https://prosemirror.net https://tiptap.scrumpy.io Maybe we could all band together and buy an enterprise license for froala to allow redistribution: https://www.froala.com/wysiwyg-editor/examples Squarespace, despite being buggy and bloated, is actually quite an amazing application and a good example of complicated layout building ui. At the very least, it would be nice to have a split pane preview mode for matrix repeater that wasn't an overlay, eg like in that bard demo.
  12. 1 point
    While working on optimizing Comments field for my blog, I noticed it could have been useful to have a GDPR checkbox in the form, to get privacy acceptance before a comment is submitted. At the beginning I though to inject the checkbox processing the rendered comment form, but as I already modified FieldtypeComments to create a Language field, I decided to continue on that road to add the GDPR checkbox. We will not modify the original FieldtypeComments in wire/modules/Fieldtype/FieldtypeComments, but copy it to site/modules/FieldtypeComments. Please refer to the initial part of this tutorial for the detailed steps to duplicate the module and make PW aware of which module version to use. Now that FieldtypeComments is duplicated, we can proceed with the necessary modifications. Inside the module there are 14 files, but do not worry ... only ContactForm.php and (optionally) comments.css will have to be modified. First we will modify the $options class property of ContactForm.php to add a new 'gdpr' label. Later we will use this option to pass the label's text associated with the checkbox. protected $options = array( … 'labels' => array( 'cite' => '', // Your Name 'email' => '', // Your E-Mail 'website' => '',// Website 'stars' => '', // Your Rating 'text' => '', // Comments 'submit' => '', // Submit 'starsRequired' => '', // Please select a star rating 'gdpr' => '', // >>>>> ADD THIS LINE ), As a second step it will be necessary to create the markup of the checkbox and of its label. We will do that by modifying function renderFormNormal() in ContactForm.php. Uikit 3 Site/Blog Profile is indirectly calling this function, so for my purpose it was enough. In case your application is using threaded comments, it will be necessary to modify also renderFormThread(). "\n\t\t<textarea name='text' class='required' required='required' id='{$id}_text' rows='$attrs[rows]' cols='$attrs[cols]'>$inputValues[text]</textarea>" . ... "\n\t</p>" . "\n\t<p class='CommentFormGdpr {$id}_gdpr'>" . //>>>>> ADD THIS BLOCK - START "\n\t\t<input class='uk-checkbox' type='checkbox' name='gdpr' value='checked' required='required'>" . "\n\t\t<label for='{$id}_gdpr'>$labels[gdpr]</label>" . "\n\t</p>" . //>>>>> ADD THIS BLOCK - END $this->renderNotifyOptions() . ... The last ContactForm.php modification will include our checkbox in processInput() to block comments submissions if GDPR checkbox is not filled. Please note this will operate in case you do not place "required" in the <input> directive. ... $errors = array(); foreach(array('cite', 'email', 'website', 'stars', 'text', 'gdpr') as $key) { //>>>>> ADD 'gdpr' in the array ... Now let's see how to call the modified form. If you are using Uikit 3 Site/Blog Profile you will have simply to modify the template file where ukCommentForm() is called (example: blog-post.php template). There we will prepare our checkbox message and pass it to ukCommentForm() as an argument option. echo ukHeading3(__('Join the discussion'), "icon=comment"); $gdpr = __('I agree with the processing of my personal data. I have read and accept the Privacy Policy.'); echo ukCommentForm($comments, ['labels' => ['gdpr' => $gdpr]]); However, if you are using comments in multiple template files, it makes more sense to directly modify ukCommentForm() presetting the new options inside the function body: $defaults = array( 'headline' => '', 'successMessage' => __('Thank you, your comment has been posted.'), 'pendingMessage' => __('Your comment has been submitted and will appear once approved by the moderator.'), 'errorMessage' => __('Your comment was not saved due to one or more errors.') . ' ' . __('Please check that you have completed all fields before submitting again.'), // >>>>> SEE THE HONEYPOT TUTORIAL 'requireHoneypotField' => 'email2', //>>>> ADD THESE FOUR LINES 'labels' => array( 'gdpr' => __('I agree with the processing of my personal data. I have read and accept the Privacy Policy.'), ), ); Before testing, we will modify the file comments.css adding these two directives (that's purely optional): .CommentFormGdpr input[type=checkbox] { background-color: white; } .CommentFormGdpr label { padding-left: 10px } Now let's test our form. Once it is filled with our data and comment it will look like this: If the user is pressing the submit button without accepting the GDPR checkbox, the comment will not be submitted and an error is displayed (in case you have used "required" in <input> you get this tooltip box, otherwise you will get an alert message): Now we accept the box After acceptance and submission, the comment form will be successfully sent. The standard success message is shown and the form is again displayed empty (with just cite and email pre-filled) ready for a next comment submission. Again I hope you will find some useful hint with that.
  13. 1 point
  14. 1 point
    This profile can be used as a simple business card or blog. The profile does not use any framework css structure, only styles based on CSS GRID and FLEX. To minimize page loading, I added lazy load for images ( Tupola Lazy Load ). With include functions like: MarkupRegions FunctionsAPI CAN DOWNLOAD FROM THIS LINK: https://github.com/rafaoski/site-grayscale-pw https://github.com/rafaoski/site-min-grayscale-pw Screenshot:
  15. 1 point
    thanks for your reply. I have tried also the new version with support for InputfieldWrapper. Based on your example, wiretabs-example.php, I have tried to use 2 InputfieldFieldsets inside first wiretab (InputfieldWrapper inside InputfieldWrapper), the display of fields is fine, but it is not saving values of fields properly...depends on the position of the nested InputfieldWrappers, some values are saved, some are not. With only one InputfieldWrapper inside another InputfieldWrapper, all is fine, but with more nested InputfieldWrappers, something is wrong. for the config below for example, is not saving the field values form the first InputfieldFieldset. <?php /** * since you can only return 1 array to the module, you have to wrap * the tabs in an inputfield wrapper */ $inputfields = new InputfieldWrapper(); //--------------------------------------------------- $general = [ [ 'name' => 'site_name', 'label' => __('Site Name'), 'type' => 'InputfieldText', 'value' => '' ], [ 'name' => 'email_address', 'label' => __('E-mail'), 'type' => 'InputfieldEmail', 'value' => '' ], [ 'name' => 'telephone', 'label' => __('Telephone'), 'type' => 'InputfieldText', 'value' => '' ], [ 'name' => 'fax', 'label' => __('Fax'), 'type' => 'InputfieldText', 'value' => '' ], ]; $general2 = [ [ 'name' => 'set_1', 'label' => __('Setting 1'), 'type' => 'InputfieldText', 'value' => '' ], [ 'name' => 'set_2', 'label' => __('Setting 2'), 'type' => 'InputfieldText', 'value' => '' ] ]; $general3 = [ [ 'name' => 'set_3', 'label' => __('Setting 3'), 'type' => 'InputfieldText', 'value' => '' ], [ 'name' => 'set_4', 'label' => __('Setting 4'), 'type' => 'InputfieldText', 'value' => '' ] ]; $tab = new InputfieldWrapper(); $tab->attr('title', 'General'); $tab->attr('class', 'WireTab'); $advanced = new InputfieldFieldset(); $advanced->label = __('Advanced Settings'); $advanced->description = __('These settings are not required'); $advanced->columnWidth = 50; $advanced->add($general2); $tab->add($advanced); $advanced = new InputfieldFieldset(); $advanced->label = __('Advanced Settings'); $advanced->description = __('These settings are not required'); $advanced->columnWidth = 50; $advanced->add($general3); $tab->add($advanced); $tab->add($general); $inputfields->append($tab); //--------------------------------------------------- $location = [ [ 'name' => 'address', 'label' => __('Address'), 'type' => 'InputfieldText', 'required' => true, 'value' => '' ], [ 'name' => 'postal_code', 'label' => __('Postal code'), 'type' => 'InputfieldText', 'required' => true, 'value' => '', 'columnWidth' => 33 ], [ 'name' => 'city', 'label' => __('City'), 'type' => 'InputfieldText', 'required' => true, 'value' => '', 'columnWidth' => 67 ], ]; $tab = new InputfieldWrapper(); $tab->attr('title', 'Location'); $tab->attr('class', 'WireTab'); $tab->add($location); $inputfields->append($tab); //--------------------------------------------------- return $inputfields;
  16. 1 point
    Have done this for image fields, it adds a button to the end of the inputfield, code below. Checks for ZipArchive class before installing. Now that I read the code it should work for files but haven't tested! It's got configuration options too, to enable per field basis, either way should it should be super quick to edit if something is not working. <?php class ExportFieldImages extends WireData implements Module, ConfigurableModule { static public function getDefaultConfig() { return array( "includeFields" => '' ); } public static function install(){ $session = wire("session"); if(!class_exists('\ZipArchive')){ throw new WireException("ZipArchive is required to create the zip file"); } } public static function getModuleInfo() { return array( 'title' => 'ExportFieldImages', 'version' => 0.1, 'summary' => "Adds a button bellow images field to download a zip containing files.", 'author' => 'Eduardo San Miguel Garcia', 'singular' => true, 'href' => '', 'autoload' => true ); } public function init() { $this->addHookAfter('InputfieldImage::render', $this, 'afterInputfieldImageRender'); $this->addHookAfter("ProcessPageEdit::execute", $this, "pageEditExecuteBefore"); } public function pageEditExecuteBefore(HookEvent $event){ $session = wire("session"); $input = wire("input"); $page = wire('pages')->get($input->get->id); $name = $page->name; $zip = $config->paths->cache . "${$name}.zip"; if($input->get->downloadImages == "true"){ wire("log")->save("custom", "excecuted page edit!"); $array = wire('files')->zip($zip, $page->get($input->get->field)->explode("filename"), array("overwrite" => true) ); if(!empty($array["errors"])){ foreach($array["errors"] as $error){ $this->error($error); } return; } else{ header('Content-Type: application/zip'); header("Content-Disposition: attachment; filename='{$name}.zip'"); echo readfile($zip); return $this->halt(); } } } public function afterInputfieldImageRender(HookEvent $event){ $field = $event->object; $input = wire("input"); $configData = wire('modules')->getModuleConfigData($this); $out = $event->return; if(in_array( $field->name, $configData['includeFields'])){ $button = wire('modules')->get('InputfieldButton'); $button->class = $button->class . " ExportFieldImages"; $button->icon = 'download'; $button->href = "./?downloadImages=true&field={$field->name}&id={$input->get->id}"; $button->value = "Download Files"; $btn .= "<span style='display:block; position:relative; float:right;' class='InputfieldPageTableButtons'>" . $button->render() . "</span>"; $out .= $btn; } $event->return = $out; } public function getModuleConfigInputfields(array $data){ $modules = wire("modules"); $fields = wire('fields'); $defaults = self::getDefaultConfig(); $data = array_merge($defaults, $data); $form = new InputfieldWrapper(); $field = $modules->get("InputfieldAsmSelect"); $field->name = "includeFields"; $field->label = __("Fields to enable download button"); $field->description = __("Choose the image fields where download button should appear"); foreach($fields as $f){ if($f->flags & Field::flagSystem) continue; if($f->type == "FieldtypeImage" || $f->type == "FieldtypeFile"){ $field->addOption($f->name); } } $field->value = $data['includeFields']; $form->add($field); return $form; } }
  17. 1 point
    Here are some starters: Hook into InputfieldFile::render method to add (modify $event->return and concatenate html string) an anchor link with its href set to a custom template/PHP file where you build your own zip file. (Preferentially, add a form with a download button and a hidden field for current page id etc and set its action attribute to the template file) Inside the template file, you'll get the page id from $input->post then build your zip file foreach($page->files_field as $name => $file) { $filePath = $file->path // ... } Use $files->zip() method to zip multiple files https://processwire.com/api/ref/files/zip/ or (from /wire/core/WireFileTools.php) // Create zip of all files in directory $dir to file $zip $dir = $config->paths->cache . "my-files/"; $zip = $config->paths->cache . "my-file.zip"; $result = $files->zip($zip, $dir); echo "<h3>These files were added to the ZIP:</h3>"; foreach($result['files'] as $file) { echo "<li>" $sanitizer->entities($file) . "</li>"; } if(count($result['errors'])) { echo "<h3>There were errors:</h3>"; foreach($result['errors'] as $error) { echo "<li>" . $sanitizer->entities($error) . "</li>"; } } and build your zip file Once the zip file is built force browser to download https://stackoverflow.com/a/1628281 If you have any questions, ask away
  • Create New...