Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 04/15/2019 in all areas

  1. Mystique Module for ProcessWire CMS/CMF Github repo : https://github.com/trk/Mystique Mystique module allow you to create dynamic fields and store dynamic fields data on database by using a config file. Requirements ProcessWire 3.0 or newer PHP 7.0 or newer FieldtypeMystique InputfieldMystique Installation Install the module from the modules directory: Via Composer: composer require trk/mystique Via git clone: cd your-processwire-project-folder/ cd site/modules/ git clone https://github.com/trk/Mystique.git Module in live reaction with your Mystique config file This mean if you remove a field from your config file, field will be removed from edit screen. As you see on youtube video. Using Mystique with your module or use different configs path, autoload need to be true for modules Default configs path is site/templates/configs/, and your config file name need to start with Mystique. and need to end with .php extension. Adding custom path not supporting anymore ! // Add your custom path inside your module class`init` function, didn't tested outside public function init() { $path = __DIR__ . DIRECTORY_SEPARATOR . 'configs' . DIRECTORY_SEPARATOR; Mystique::add($path); } Mystique module will search site/modules/**/configs/Mystique.*.php and site/templates/Mystique.*.php paths for Mystique config files. All config files need to return a PHP ARRAY like examples. Usage almost same with ProcessWire Inputfield Api, only difference is set and showIf usage like on example. <?php namespace ProcessWire; /** * Resource : testing-mystique */ return [ 'title' => __('Testing Mystique'), 'fields' => [ 'text_field' => [ 'label' => __('You can use short named types'), 'description' => __('In file showIf working like example'), 'notes' => __('Also you can use $input->set() method'), 'type' => 'text', 'showIf' => [ 'another_text' => "=''" ], 'set' => [ 'showCount' => InputfieldText::showCountChars, 'maxlength' => 255 ], 'attr' => [ 'attr-foo' => 'bar', 'attr-bar' => 'foo' ] ], 'another_text' => [ 'label' => __('Another text field (default type is text)') ] ] ]; Example: site/templates/configs/Mystique.seo-fields.php <?php namespace ProcessWire; /** * Resource : seo-fields */ return [ 'title' => __('Seo fields'), 'fields' => [ 'window_title' => [ 'label' => __('Window title'), 'type' => Mystique::TEXT, // or InputfieldText 'useLanguages' => true, 'attr' => [ 'placeholder' => __('Enter a window title') ] ], 'navigation_title' => [ 'label' => __('Navigation title'), 'type' => Mystique::TEXT, // or InputfieldText 'useLanguages' => true, 'showIf' => [ 'window_title' => "!=''" ], 'attr' => [ 'placeholder' => __('Enter a navigation title') ] ], 'description' => [ 'label' => __('Description for search engines'), 'type' => Mystique::TEXTAREA, 'useLanguages' => true ], 'page_tpye' => [ 'label' => __('Type'), 'type' => Mystique::SELECT, 'options' => [ 'basic' => __('Basic page'), 'gallery' => __('Gallery'), 'blog' => __('Blog') ] ], 'show_on_nav' => [ 'label' => __('Display this page on navigation'), 'type' => Mystique::CHECKBOX ] ] ]; Searching data on Mystique field is limited. Because, Mystique saving data to database in json format. When you make search for Mystique field, operator not important. Operator will be changed with %= operator. Search example $navigationPages = pages()->find('my_mystique_field.show_on_nav=1'); $navigationPages = pages()->find('my_mystique_field.page_tpye=gallery');
    8 points
  2. Good Morning ProcessWIre Community, PW Review is a website that has a lofty goal of covering ProcessWire on a regular basis. That may seem like an impossible task, however I believe I'm up to the challenge. First, this is a strictly non-commercial endeavor and a project I believe in. This is not Wired Magazine or anything like that. I'm not a serious programmer or pretending to be one. My goal is to cover the modules (commercial and non-commercial) that may not get that much press or attention. I will also be covering modules that maybe you haven't heard about in awhile. There will also be screenshots, information and videos regarding the Commercial modules. I've been with this community since 2012 and have benefitted tremendously from ProcessWire. So this is my way of giving back, which is important to me. It's my hope that over time that what I'm doing myself will get some community participation. One person that I've had nothing but positive support from is Ryan Cramer. That's enough to keep me going at this for some time. That's it. Have a nice day and I hope you find something to like with PW Review. If not, please send me an email. I'm a great listener. Best Regards, Charles
    6 points
  3. $page->find()start from $page if $page != home { $page->find( 'parnet=/blog/' ) will.alwayse return NO things } so >>> u.must use $pages->find() noT $page->find() !! OAR u can.uses $pages->get('/blog/')->children( 'limit=3, include=all' ) if usesing $pages say shits bout array it mean yoU write-over it some where >>> u did a $pages = array some.where ! fix that.or u can uses wire( 'pages ')->find(' you.selectoro stuff here '); or functiones api pages()->find( 'selectros in this' ) amen
    5 points
  4. Hi @Sarnoc and welcome to the forum, It does not sound like you'll need any hooks or custom SQL to get what you are trying ? This page is your best friend: https://processwire.com/docs/selectors/ I think your setup is good as it is! I'd not recommend using repeaters for that. Pages are in general more flexible and better/easier to maintain in the long run in my opinion. I also wonder why the find operation does not work... IMHO it should. I guess your pages have a different NAME (not title)... Using TracyDebugger you can easily and quickly try such things: "poc" is a page reference field. And the referenced field has title="Jakob Enigl Pagetitle" and name="Jakob Enigl Pagename" That's why the first line does NOT work and the other lines do work. The first dump looks for the pagename (no subfield specified) and I guess it does work because the string is sanitized to a pagename (see last dump). When building your queries with user input or dynamic values it's sometimes easier and more readable to build your selectors as arrays:
    4 points
  5. Hi all, Sorry, I'm late to the party! Interesting conversation here. I am not a fan of duplicating modules, if what they offer are very similar. I would prefer to try a PR in the first instance :-). Interestingly, the other day when @adrian was reporting some issues with the module and suggesting changes, I had a rethink about your suggestions and decided it was worthwhile going with your suggestions, especially the auto include. I didn't mention it since I didn't have the time then to work on the module. So, my take is, we can improve the existing module, with new features and/or hooks. All PRs will be duly credited. I suggest we try that first. That's my opinion :-).
    3 points
  6. one-to-many would look something like this if($input->urlSegment1) { $seg = $sanitizer->name($input->urlSegment1); if($seg === 'clinics') { $loc = $page->name; } $clinics = $pages->find("template=clinic, location=$loc"); foreach($clinics as $clinic) { $content .= "<p><a href='{$clinic->url}'>{$clinic->title}</a></p>"; } } e.g. a location can have multiple clinics
    2 points
  7. Let's face something I had to deal with lately in a similar way. When it comes to things like payment, customer data and reliable processes that end in a contract of any kind... I need a solution and partner that is reliable and liable. Don't get me wrong here. I think you are good developer with lots of skills and experience but lets say someone uses one of these modules/profiles and there are critical problems with it. Either payments don't work properly in any kind, privacy flaws or whatever. Who is to blame then? Who is in charge? Where would you see yourself and your modules in this? a) Foundation/base for further development b) Full out-of-the-box ready-made solution c) Somewhere in-between I ask because of potential risks on your site. So it's absolutely not against you or your ideas but something I personally would be careful with.
    2 points
  8. For the event ones you could do it with modules no problem - there would just be a few modules required - the first 3 would be the same for each of the 3 booking systems on my list: Customer Management Payment System Core Booking Module Specific Booking module for the first 3 items on my list (events/accomodation/hotels) I'm actually happy to even get 15 votes for that top one ? I wouldn't see this as just PW modules either - I'd put together a website with a pre-bundled version (with site profile and modified PW installer so all the required modules are there from the start) that I can market as its own system to go up against other event booking systems out there. Upgrades aren't a problem - no more so than they usually are for Pro modules anyway. If all I make is half a month's wages overall and it takes me half a month to finish the first one then I've not lost anything by trying.
    2 points
  9. I vote against a site profile approach... ? I guess I'm biased (I simply don't like site profiles at all), but how should any of the mentioned ideas work as a site profile? How would your customers get updates? What if they needed customizations? What if they want to integrate it in an existing project? Besides, I wonder if there is a market for commercial modules at all. Since Friday, the event booking suggestion got 15 votes. If 10% purchased such a software, that would mean 1.5 purchases... But maybe someone that has already released commercial modules could provide some insights. I think what PW would need is a good way to handle exactly what you mentioned above as your challenges: That's what @LostKobrakai has (kind of) tackled some time ago with his Migrations module and what I have started to tackle with my version of it. I think a proper way to handle such things would be the best addition to PW. This would also make it possible to have a proper workflow for staging/dev setups that have been discussed over and over again here in the forum.
    2 points
  10. Heres a VERY rough script and it's very messy. (ran it from command line also) https://gist.github.com/elabx/ba4879fe36ca9430f8d614aaee52fc5a Specifically, check the functions addProduct() and updateProductVariant(), in those two functions is where I call the Shopify client, and all the setup starts here on line 175. This script is meant to grab data from a CSV, by if you look at addProduct you can basically tell how to add a product through the API so instead passing an array to that function you could pass a Page object for example and fill in the gaps. On shopify you would just need to setup a Private application, to get the required key/secret. BEWARE to run the script as is, because it deletes all products on the Shopify store every time the script runs.
    2 points
  11. Holiday bookings - here's a video (no sound, so might have to guess what I was doing in places!) showing backend management of calendars (so in this example two holiday cottages/apartments) and different fee structures per cottage/apartment throughout the year for different seasons (though I'd probably approach that differently now). For the backend I made it possible to click and drag so you can select an odd number of days for a booking as a member of a sales team if you're taking a booking over the phone. On the frontend calendar (not in this video) the customer can only pick from pre-defined dates that are availble. You'll see some dates has bookings (Burlingham - my surname) and clicking on those takes you to the booking details - obviously customers viewing the frontend wouldn't see the other booking details ? It doesn't show the whole parment process but I think that was because it wasn't working with the Reno admin theme at the time I last looked at this code in 2016. I think to be honest I'd forgotten how interesting some of the things were that I'd done on here that the event calendar is probably the one to do first, followed by this accomodation booking one.
    2 points
  12. This is the thread for the old version of RockMigrations wich is deprecated now and has been renamed to RockMigrations1 Here is the latest version of RockMigrations: -- Old post from 2019 -- https://github.com/baumrock/RockMigrations1 Quickstart First make sure you have backups of your database!! Install RockMigrations Create this ready.php <?php /** @var RockMigrations1 $rm */ $rm = $this->wire('modules')->get('RockMigrations1'); $rm->migrate([ 'fields' => [ 'myfield' => ['type' => 'text'], ], 'templates' => [ 'mytemplate' => [], ], ]); Open your PW Backend and you'll see the new field and new template! Now add the new field to the template: <?php /** @var RockMigrations1 $rm */ $rm = $this->wire('modules')->get('RockMigrations1'); $rm->migrate([ 'fields' => [ 'myfield' => ['type' => 'text'], ], 'templates' => [ 'mytemplate' => [ 'fields' => [ 'title', 'myfield', ], ], ], ]); Reload your backend and inspect the template: Now let's add a label for our new field and make title+myfield 50% width: <?php /** @var RockMigrations1 $rm */ $rm = $this->wire('modules')->get('RockMigrations1'); $rm->migrate([ 'fields' => [ 'myfield' => [ 'type' => 'text', 'label' => 'My Field Label', ], ], 'templates' => [ 'mytemplate' => [ 'fields' => [ 'title' => ['columnWidth' => 50], 'myfield' => ['columnWidth' => 50], ], ], ], ]); That's all the magic! You can easily lookup all necessary properties in Tracy Debugger: Let us add a Textformatter as an example! Add the textformatter via the PW backend (not via RM), save the field and inspect via Tracy: Now add this data to your migration: <?php /** @var RockMigrations1 $rm */ $rm = $this->wire('modules')->get('RockMigrations1'); $rm->migrate([ 'fields' => [ 'myfield' => [ 'type' => 'text', 'label' => 'My Field Label', 'textformatters' => [ 'TextformatterEntities', ], ], ], 'templates' => [ 'mytemplate' => [ 'fields' => [ 'title' => ['columnWidth' => 50], 'myfield' => ['columnWidth' => 50], ], ], ], ]); Ok maybe you noticed that migrations are now running on every request which may slow down your site a lot! RM2 will have a feature to automatically detect changes and fire migrations automatically. RM1 does not have this feature, but I've used a technique in all my projects that fires migrations on every modules refresh. Simply wrap the migration in a fireOnRefresh method call: <?php /** @var RockMigrations1 $rm */ $rm = $this->wire('modules')->get('RockMigrations1'); $rm->fireOnRefresh(function() use($rm) { $rm->migrate([ 'fields' => [ 'myfield' => [ 'type' => 'text', 'label' => 'My Field Label', 'textformatters' => [ 'TextformatterEntities', ], ], ], 'templates' => [ 'mytemplate' => [ 'fields' => [ 'title' => ['columnWidth' => 50], 'myfield' => ['columnWidth' => 50], ], ], ], ]); }); How to remove things you created before you may ask? Use the declarative syntax: <?php /** @var RockMigrations1 $rm */ $rm = $this->wire('modules')->get('RockMigrations1'); $rm->fireOnRefresh(function() use($rm) { $rm->deleteField('myfield'); $rm->deleteTemplate('mytemplate'); }); Refresh your backend and everything is as it was before! PS: Make sure you have proper Intellisense support in your IDE to get instant help:
    1 point
  13. Restrict Repeater Matrix Allows restrictions and limits to be placed on Repeater Matrix fields. Requires ProcessWire >= v3.0.0 and FieldtypeRepeaterMatrix >= v0.0.5. For any matrix type in a Repeater Matrix field you have the option to: Disable settings for items (cannot change matrix type) Prevent drag-sorting of items Prevent cloning of items Prevent toggling of the published state of items Prevent trashing of items Limit the number of items that may be added to the inputfield. When the limit is reached the "Add new" button for the matrix type will be removed and the matrix type will not be available for selection in the "Type" dropdown of other matrix items. Hide the clone button when the limit for a matrix type has been reached. Note that in PW >= 3.0.187 this also means that the copy/paste feature will become unavailable for the matrix type. Please note that restrictions and limits are applied with CSS/JS so should not be considered tamper-proof. Usage Install the Restrict Repeater Matrix module. For each matrix type created in the Repeater Matrix field settings, a "Restrictions" fieldset is added at the bottom of the matrix type settings: For newly added matrix types, the settings must be saved first in order for the Restrictions fieldset to appear. Set restrictions for each matrix type as needed. A limit of zero means that no items of that matrix type may be added to the inputfield. Setting restrictions via a hook Besides setting restrictions in the field settings, you can also apply or modify restrictions by hooking RestrictRepeaterMatrix::checkRestrictions. This allows for more focused restrictions, for example, applying restrictions depending on the template of the page being edited or depending on the role of the user. The checkRestrictions() method receives the following arguments: $field This Repeater Matrix field $inputfield This Repeater Matrix inputfield $matrix_types An array of matrix types for this field. Each key is the matrix type name and the value is the matrix type integer. $page The page that is open in ProcessPageEdit The method returns a multi-dimensional array of matrix types and restrictions for each of those types. An example of a returned array: Example hooks Prevent the matrix type "images_block" from being added to "my_matrix_field" in a page with the "basic-page" template: $wire->addHookAfter('RestrictRepeaterMatrix::checkRestrictions', function(HookEvent $event) { $field = $event->arguments('field'); $page = $event->arguments('page'); $type_restrictions = $event->return; if($field->name === 'my_matrix_field' && $page->template->name === 'basic-page') { $type_restrictions['images_block']['limit'] = 0; } $event->return = $type_restrictions; }); Prevent non-superusers from trashing any Repeater Matrix items in "my_matrix_field": $wire->addHookAfter('RestrictRepeaterMatrix::checkRestrictions', function(HookEvent $event) { $field = $event->arguments('field'); $type_restrictions = $event->return; if($field->name === 'my_matrix_field' && !$this->user->isSuperuser()) { foreach($type_restrictions as $key => $value) { $type_restrictions[$key]['notrash'] = true; } } $event->return = $type_restrictions; }); http://modules.processwire.com/modules/restrict-repeater-matrix/ https://github.com/Toutouwai/RestrictRepeaterMatrix
    1 point
  14. Edit: Because of the great response to this topic I wrote a guest blogpost: https://processwire.com/blog/posts/building-custom-admin-pages-with-process-modules/ One of the hidden treasures of processwire seems to be the creation of custom admin pages. Technically speaking those pages are ProcessModules - but i guess that's the reason why so many people out there seem to be afraid of building them... it sounds so hard! You've never created a module for ProcessWire? You have never created a plugin for any other CMS? You have no clue about OOP with all its classes, methods and properties? No problem! I'll show you how simple you can start: <?php class CustomAdminPage extends Process { public static function getModuleinfo() { return [ 'title' => 'Custom Admin Page Example', 'summary' => 'Minimalistic ProcessModule to show that nobody has to be afraid of building custom admin pages.', 'href' => 'https://processwire.com/talk/topic/17709-how-to-create-custom-admin-pages-aka-processmodules-yes-its-that-simple/', 'author' => 'Bernhard Baumrock, baumrock.com', 'version' => 1, // page that you want created to execute this module 'page' => [ 'name' => 'customadmin', // your page will be online at /youradmin/setup/customadmin/ 'parent' => 'setup', 'title' => 'Custom Admin Page Example' ], ]; } public function ___execute() { return 'This is the most simple Admin-Page you have ever seen :)'; } } Now save this file as CustomAdminPage.module and place it in your /site/modules folder. After a refresh it will show your module in the modules manager of your site where you can install it: After installation you already have your first very own admin page! Congratulations! Was not too hard, was it? It's as simple as that! Now lets add some more custom HTML. And to show you another nice feature we will add this code to a separate method called executeDemo(). And because everything is so simple we will also add some javascript to this page public function ___executeDemo() { $out = ''; $out .= '<h1>H1 has some special css styling in the admin, thats why it seems to have no effect</h1>'; $out .= '<h2>H2 looks different ;)</h2>'; $out .= '<h3>...and so does H3</h3>'; $out .= '<button onclick="myFunction()">Click me</button>'; $out .= '<script>function myFunction() { alert("this is a demo javascript"); }</script>'; return $out; return ''; } Now thanks to ProcessWire-magic your page will already have its own URL: Just append /demo to your url and see what you get: And of course don't forget to click the button Ok, now that code looks a bit hacky, right? Inputfields and especially InputfieldMarkup for the win! We add another method with some advanced code. To use inputfields we need a form that holds all those inputfields and that makes it possible to handle user input lateron. See somas great tutorial about forms here for a quickstart and more details: public function ___executeAdvanced() { $out = '<h2>A more complex Example</h2>'; $form = wire()->modules->get('InputfieldForm'); $field = wire()->modules->get('InputfieldMarkup'); $field->label = 'Markup Test 1'; $field->value = '<h1>h1</h1><h2>h2</h2><h3>h3</h3><h4>h4</h4>'; $form->add($field); $out .= $form->render(); return $out; } Ok, it get's boring Let's do something more fun and add a chart in a second field and change the fields to 50% screen width (I'm sure you know that already from the GUI template editor)! public function ___executeAdvanced() { $out = '<h2>A more complex Example</h2>'; $form = wire()->modules->get('InputfieldForm'); $field = wire()->modules->get('InputfieldMarkup'); $field->label = 'Markup Test 1'; $field->value = '<h1>h1</h1><h2>h2</h2><h3>h3</h3><h4>h4</h4>'; $field->columnWidth = 50; $form->add($field); $field = wire()->modules->get('InputfieldMarkup'); $field->label = 'Chart Sample'; $field->value = '$chart'; //$field->notes = 'Example code taken from here: http://www.chartjs.org/docs/latest/getting-started/usage.html'; $field->columnWidth = 50; $form->add($field); $out .= $form->render(); return $out; } OK, we are almost there... we only need to add the chart library! To keep everything clean we will put the code for the chart in another method. We will make that method PRIVATE to add some security. Our new Method: private function renderChart() { // prepare chart code wire()->config->scripts->add('https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.3/Chart.min.js'); ob_start(); ?> <canvas id="myChart"></canvas> <script> var ctx = document.getElementById("myChart"); var myChart = new Chart(ctx, { type: 'bar', data: { labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"], datasets: [{ label: '# of Votes', data: [12, 19, 3, 5, 2, 3], backgroundColor: [ 'rgba(255, 99, 132, 0.2)', 'rgba(54, 162, 235, 0.2)', 'rgba(255, 206, 86, 0.2)', 'rgba(75, 192, 192, 0.2)', 'rgba(153, 102, 255, 0.2)', 'rgba(255, 159, 64, 0.2)' ], borderColor: [ 'rgba(255,99,132,1)', 'rgba(54, 162, 235, 1)', 'rgba(255, 206, 86, 1)', 'rgba(75, 192, 192, 1)', 'rgba(153, 102, 255, 1)', 'rgba(255, 159, 64, 1)' ], borderWidth: 1 }] }, options: { scales: { yAxes: [{ ticks: { beginAtZero:true } }] } } }); </script> <?php return ob_get_clean(); } Now we just need to call $this->renderChart() in the right place! Here is the complete Module: <?php class CustomAdminPage extends Process { public static function getModuleinfo() { return [ 'title' => 'Custom Admin Page Example', 'summary' => 'Minimalistic ProcessModule to show that nobody has to be afraid of building custom admin pages.', 'href' => 'https://processwire.com/talk/topic/17709-how-to-create-custom-admin-pages-aka-processmodules-yes-its-that-simple/', 'author' => 'Bernhard Baumrock, baumrock.com', 'version' => 1, // page that you want created to execute this module 'page' => [ 'name' => 'customadmin', // your page will be online at /youradmin/setup/customadmin/ 'parent' => 'setup', 'title' => 'Custom Admin Page Example' ], ]; } public function ___execute() { return 'This is the most simple Admin-Page you have ever seen :)'; } public function ___executeDemo() { $out = ''; $out .= '<h1>H1 has some special css styling in the admin, thats why it seems to have no effect</h1>'; $out .= '<h2>H2 looks different ;)</h2>'; $out .= '<h3>...and so does H3</h3>'; $out .= '<button onclick="myFunction()">Click me</button>'; $out .= '<script>function myFunction() { alert("this is a demo javascript"); }</script>'; return $out; return ''; } public function ___executeAdvanced() { $out = '<h2>A more complex Example</h2>'; $form = wire()->modules->get('InputfieldForm'); $field = wire()->modules->get('InputfieldMarkup'); $field->label = 'Markup Test 1'; $field->value = '<h1>h1</h1><h2>h2</h2><h3>h3</h3><h4>h4</h4>'; $field->columnWidth = 50; $form->add($field); $field = wire()->modules->get('InputfieldMarkup'); $field->label = 'Chart Sample'; $field->value = $this->renderChart(); $field->notes = 'Example code taken from here: http://www.chartjs.org/docs/latest/getting-started/usage.html'; $field->columnWidth = 50; $form->add($field); $out .= $form->render(); return $out; } private function renderChart() { // prepare chart code wire()->config->scripts->add('https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.3/Chart.min.js'); ob_start(); ?> <canvas id="myChart"></canvas> <script> var ctx = document.getElementById("myChart"); var myChart = new Chart(ctx, { type: 'bar', data: { labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"], datasets: [{ label: '# of Votes', data: [12, 19, 3, 5, 2, 3], backgroundColor: [ 'rgba(255, 99, 132, 0.2)', 'rgba(54, 162, 235, 0.2)', 'rgba(255, 206, 86, 0.2)', 'rgba(75, 192, 192, 0.2)', 'rgba(153, 102, 255, 0.2)', 'rgba(255, 159, 64, 0.2)' ], borderColor: [ 'rgba(255,99,132,1)', 'rgba(54, 162, 235, 1)', 'rgba(255, 206, 86, 1)', 'rgba(75, 192, 192, 1)', 'rgba(153, 102, 255, 1)', 'rgba(255, 159, 64, 1)' ], borderWidth: 1 }] }, options: { scales: { yAxes: [{ ticks: { beginAtZero:true } }] } } }); </script> <?php return ob_get_clean(); } } I hope you enjoyed reading this and it will open up many new possibilities for you! Updates: permissions: https://processwire.com/talk/topic/17709-how-to-create-custom-admin-pages-aka-processmodules-yes-its-that-simple/?do=findComment&comment=174746 tutorial on file uploads: https://processwire.com/talk/topic/17709-how-to-create-custom-admin-pages-aka-processmodules-yes-its-that-simple/?do=findComment&comment=185261 snippet how to use NavJSON: https://processwire.com/talk/topic/17709-how-to-create-custom-admin-pages-aka-processmodules-yes-its-that-simple/?do=findComment&comment=216412
    1 point
  15. Hi guys... I love pw! I'm trying to update a template of the multilanguagge website. I created a field, i called it "headimage" and I added it to the home template. .. . But I can t see any image!!! Where is the issue?? Thanks my friends
    1 point
  16. Yes this solved my problem, Thanks @Autofahrn!
    1 point
  17. Looks very cool, @ukyo! Thanks for that, but the font-size on the video was hard on my eyes. ?
    1 point
  18. I think another approach is just having a "Homily" template.That uses a repeater field to storage dates and seasons. Maybe a structure similar to this <homilies> <homily> <title /> <about /> <repeater> <priority type="int"/> <date/> <season /> </repeater> </homily> <homily> <title /> <about /> <repeater> <priority type="int"/> <date/> <season /> </repeater> </homily> <homily> <title /> <about /> <repeater> <priority type="int"/> <date/> <season /> </repeater> </homily> </homilies> So you can search all the homilies and put the different dates and season configuration for that homily. If one or more homily is on the same date then you can use the priority field to sort them out.
    1 point
  19. A specific page, or list of pages for a specific day of the year is exactly what I'm using Processwire for with https://www.birthfactdeathcalendar.net/. I'm definitely not a PHP crack either. On my home page of course I only search for events on this day and month: $todayday = date("d"); $todaymonth = date("m"); $features = $pages->find("parent=/events/|/the-eyes/, bfd_day.name=$todayday, bfd_month.name=$todaymonth, sort=bfd_year"); To me id is more reliable than title and/or page name, because at some point it happens I find out a title (like a person's name or even a street address) was misspelled. When I change the title and/or page name I'd have to re-code every reference to it, while the id just stays the same. I have over 26000 pages by now and everything still runs very smooth for several years now. With help from dedicated members here I also learned a lot. Most of what I use is in Processwire itself, hardly using any modules apart from MapMarker. Good luck!
    1 point
  20. BINGO!! That was it. I had picked up some code from a (former) employee for generating the nav bar and he had used $pages as a array/var. Changed his code from $pages to $navPages and my code now works!!! Thanks for pointing me in the right direction!
    1 point
  21. Thanks @Robin S, you are the realest. I am going to poke into the issue later this week because I'd also like to know what's going on with it - especially why its failure took over ProcessWire's normal routing for edits as well as error logging.
    1 point
  22. @SamC Here is a module about auto page naming, You can get idea about naming pages automatically from source code, https://gitlab.com/baumrock/RandomPageName/blob/master/RandomPageName.module
    1 point
  23. I was assuming you only have a one-to-one relationship when I quickly wrote this, so you'd probably have to slightly alter this basic code: if($input->urlSegment1) { $seg = $sanitizer->name($input->urlSegment1); if($seg === 'clinic') { $loc = $page->name; } $clinic = $pages->get("template=clinic, location=$loc"); $content .= "<p><a href='{$clinic->url}'>{$clinic->title}</a></p>"; }
    1 point
  24. Unless you consider creating the page tree like this, URL segments would be the solution. I'd probably sort the date fields in descending order, though ('site.com/2019/04/09/day-1' or simply 'site.com/2019/04/09').
    1 point
  25. Well who knows! The devil is in the detail. And I do agree the javascript events would be a powerful enough reason. I also remember reading that conversation a while ago and do agree on your points. Particularly the "auto include" of files, pretty much like the modules.
    1 point
  26. Hi, I've added an addInlineImage() method to https://github.com/chriswthomson/WireMailMailgun <?php $img = $pages->get(1)->images->getRandom(); // An example $customFilename = "test.$img->ext"; // Don't want to use the basename for this example $mg = $mail->new(); $mg->to("your@email.com") ->subject("Test Inline Image") ->bodyHTML("<p>Testing, 123, is there an image for you to see?</p><img src='cid:$customFilename'>") ->addInlineImage($img->filename, $customFilename) // Second argument is optional ->send(); If the image isn't referenced in the HTML it just appears as a standard attachment from what I can see. Cheers, Chris
    1 point
  27. Hi @elabx, thx for asking ? Well, one (crucial) thing would be the javascript events. Maybe that could be added to kongondo's module, that's why I was asking for his opinion. Besides that I don't like how his module works (I'm picky, I know). We had some discussion about it (https://processwire.com/talk/topic/10804-module-runtimemarkup-fieldtype-inputfield/?do=findComment&comment=148083). And it was simply easier to have things under my control than modifying his version with custom hooks. But yeah.. It might not be necessary to have another module for this ?
    1 point
  28. Work continued in closing out issue reports this week. We’re down to about 60 of them now, though if we subtract issues that aren’t reproducible, are already marked as resolved (close pending), may be moved to the request repo, or are a discussion (rather than something to fix), then we’re closer to half that number. Over these last few weeks we’ve been working on issue reports, but I’ve also been enhancing the core in small ways as well. This post will cover a few of the useful enhancements that have been made in recent weeks— https://processwire.com/blog/posts/pw-3.0.130/
    1 point
  29. If someone has issues with generating an access token, I now have learned that it is important to include a language with the request for the access token: https://www.instagram.com/oauth/authorize/?client_id=YOUR_CLIENT_ID&redirect_uri=http://page.dev/processwire/module/edit?name=InstagramFeed/&response_type=token&scope=public_content&hl=en Source: https://stackoverflow.com/questions/55449162/https-api-instagram-com-oauth-authorize-api-login-error So inside the "InstagramFeed.module", you should add 'hl' => 'en' in line 385: <?php $request = array( 'client_id' => $this->clientId, 'redirect_uri' => $redirect, 'response_type' => 'code', 'scope' => 'public_content', 'hl' => 'en' ); Regards, Andreas
    1 point
  30. I'm using DynamicRoles in production on two sites to allow users with specific roles to edit specific page(s). I started with PW 2.4.x, when I upgraded to PW 3, I had to modify the module by adding the namespace to the files. Then I made changes proposed by Benjamin (issues and PRs), I hope I did it well...
    1 point
  31. For me it was a platform switch away from ProcessWire and even PHP. It had nothing to do with DynamicRoles at all. Without the issues mentioned/open prs on GitHub it should work fine. I've even looked into making it even more flexible once, but never finished the work.
    1 point
  32. I'm also no longer using DynamicRoles in a production env. It was working well though with the fixes I proposed in the repo and on pw 2.7.3. An addition to @szabesz comments above: The thing to keep in mind with $page->viewable() as the "Solution for Access-Control" is that it's easy for it to not be enough. As soon as pagination is needed one needs access control at the database layer or pages will have different numbers of elements (even down to none at all).
    1 point
  33. Amazing how Processwire, through time turns us into more professional people. Great work Charles.
    1 point
  34. That "/var/www" is the root path (within your filesystem) where your webserver places the content of your domains. This can only be changed when you point your server to some other folder. I guess you are currently working with the release version (1.2.9) of Duplicator, right? That one expands the site files into a directory level below which needs to be moved manually during the install. Please try the V1.3.10 above which should solve this. Btw, @flydev, do you plan to include the suggested domain name fix into ProcessDuplicator::getPackagesDetails to support dashes in domain names? If anyone else is using Duplicator on domains containing dashes, you may want to change these lines in aforementioned method (otherwise the package list stays empty): $parts = explode('-', $originalFilename); array_pop($parts); $tsstr = implode('-', $parts); with this dirty hack: $tsstr = substr($originalFilename, 0, 19); // 2019-03-20_01-15-41
    1 point
  35. In case someone else stumbles over similar demands like me, I put my modified AjaxSearch.js here: AjaxSearch.js It allows to use an existing #ajaxSearch element, so you may place the result anywhere in your markup (for example an overlay like I did here: https://pwflex.team-tofahrn.de/). Then I had a problem with excess GET requests which do not cancel server-side processing, so getting the result after typing many characters took seconds. That script uses a different scheduling. Its a direct replacement, so you may try and revert to the original safely. Edit: I've updated the script to send the value URI encoded like this: data: param_name+"="+encodeURIComponent(value), Allows to send queries containing special chars like &
    1 point
  36. Actually this is possible with the update: https://github.com/processwire/processwire/commit/12aede03fe297421b538ea6ef16c0f42424077a4#diff-fc95d41f7bed91883ef3f4f16d8c92cdR1104
    1 point
  37. Probably someone with experience in graphql can build a similar connector plugin for PW like https://github.com/jasonmccallister/craft-hasura Do you think that it would be a good idea?
    1 point
  38. Hi @gebeer! Did you find a solution to this? I added the code below before this which seems to work : if(!$page->id) $inputfield->checkboxChecked = 0;
    1 point
  39. Hi, I'd like to also return an excerpt from the field which matched. <?php namespace Processwire; /** * Search. * * Global site search. * * @author snipped. * @version 1.0.0 */ // Retrieve the GET request. $query = $sanitizer->text($input->get->query); if (!$query) { return; } // Sanitise the user input. $query = $sanitizer->selectorValue($query); // Instantiate the Search object. new Search($query); /** * Search. * * Return results from matching pages. * * @param query */ class Search { protected $query; protected $excludedFieldTypes; protected $templates; protected $matches; protected $output; public function __construct($query) { $this->query = $query; $this->excludedFieldTypes = ['FieldtypeImage', 'FieldtypeSelect', 'FieldtypeMediaManager', 'FieldtypeFieldsetTabOpen', 'FieldtypeFieldsetClose', 'FieldtypeRepeater', 'FieldtypeCheckbox', 'FieldtypeModule', 'FieldtypePassword']; $this->templates = $this->getTemplates(); $this->matches = $this->getMatches(); $this->renderResults(); } /** * getTemplates * * Return an array of templates & fields to be searched. * * @return pages */ public function getTemplates() { $templates = []; foreach (wire('templates') as $template) { $fields = []; foreach ($template->fields as $field) { // Exclude certain field types. if (in_array($field->type, $this->excludedFieldTypes)) { continue; } array_push($fields, $field); } // Turn the array to a string suitable for the API. $fields = implode("|", $fields); $templates[$template->name] = [ 'template' => $template->name, 'fields' => $fields, ]; } return $templates; } /** * getMatches * * Return an array of matching pages. * * @return matchingPages */ public function getMatches() { $matches = []; foreach ($this->templates as $template) { $selector = "template=" . $template['template'] . ", " . $template['fields'] . "%=$this->query"; $results = wire('pages')->find($selector); foreach ($results as $result) { array_push($matches, $result); } } return $matches; } /** * renderResults * * Render the results of the search. */ public function renderResults() { if (!$this->matches) { echo 'No results.'; return; } foreach ($this->matches as $match) { $this->output .= '<p>' . $match->title . '</p>'; $this->output .= '<p>' . $match->url . '</p>'; $this->output .= '<p>' . $match->field . '</p>'; } echo $this->output; } } /** * debug * * Helper method for visualising arrays. */ function debug($variable) { echo '<pre>'; print_r($variable); echo '<pre>'; }
    1 point
  40. I've been using a very similar solution at weekly.pw, i.e. indexing data into a field called "search_index". The content is mostly built using PageTable, and I'm indexing content from those for the parent page's search_index field as well. No Repeaters or RepeaterMatrix fields here, but they'd require similar processing. The whole solution is built into some methods placed in /site/init.php. Might be useful for someone, so here's the code: https://gist.github.com/teppokoivula/83036c6e73d7460be7706def620d80d4. Note that in this case I'm not using the same index as an excerpt, but rather the summary field of the page. Excerpt would be better – will probably add them at some point. Once you have the index at hand, it's relatively simple ? Another thing to note here is that I'm currently using the same index field to store links in link:url format. Since tags are stripped from the index text, URLs wouldn't otherwise be a part of the index – and this also allows me to perform specific link queries, such as https://weekly.pw/search/?q=link:https://modules.processwire.com/modules/sanitizer-transliterate/ ?
    1 point
  41. There's a bit more more work needed before my module could be released publicly but I'll put it on my "to do" list. Yes, but in my use cases that's a good thing. This approach only suits certain types of search needs, where you want to implement a broad text search across most/all content. In my cases I want the search to match as many pages as possible, so I do things like explode the search phrase on space characters and then match each term with the %= LIKE operator so I'm catching part words too and without regard to the order of terms: $search_terms = $sanitizer->selectorValue($input->get->search); $search_terms = preg_replace("/\s+/", " ", $search_terms); // replace multiple spaces with single space $terms = explode(' ', $search_terms); foreach($terms as $term) { $selector .= "index%=$term, "; } //... And I want the excerpt to be Google-like in that it deals with the page's text content as a whole rather than caring about what fields were used behind the scenes.
    1 point
  42. I'm curious, how would your module be different from FieldtypeRuntimeMarkup?
    1 point
  43. Perhaps this will help? Oh, and welcome to the forum! ?
    1 point
  44. @ngrmm, you can do this by not defining a parent for selectable pages in the field settings (just define a template) and add the following in /site/ready.php: $wire->addHookBefore('InputfieldPage::renderAddable', null, 'setAddParent'); $wire->addHookBefore('InputfieldPage::processInputAddPages', null, 'setAddParent'); function setAddParent(HookEvent $event) { $inputfield = $event->object; // Only for this one Page Reference field if($inputfield->hasField != 'tags') return; // Only for ProcessPageEdit if($event->wire('process') != 'ProcessPageEdit') return; // Set the ID of the parent page that new pages should be added under $inputfield->parent_id = 1234; }
    1 point
  45. @ethanbeyer, no worries, just glad to help. ? It would be good to get to the bottom of what is causing ImageMagick to suddenly fail though. @netcarver linked to this article recently in another topic: https://alexvanderbist.com/posts/2018/fixing-imagick-error-unauthorized It deals with a new security policy relating to PDF conversion but I wonder if some other policy has been introduced that affects JPGs too. If so there might need to be a fix applied to ImageSizerEngineImagick. Maybe your host could shed some light on what policy is being triggered by your uploads? Or maybe @horst has heard something about an ImageMagick update that might be connected?
    1 point
  46. Quite correct ? Migrations are supposed to be immutable files, so running them always yields the same effects. This is best accomplished if those migrations are managed in one central place by the entity affected. Nothing outside should implicitly be able to affect what migrations do. Having migrations in modules however does exactly that. If you update a module the migrations in it might be updated, which is not good (it might have already been executed in prod with the old code and later migrations depending on the new code might fail). Even having immutable migrations in the module might quickly become a burden as each new user must be moved through all „mistakes“ and „changes“ as well before being migrated to a final state needed by the most current version. While I‘ve talked a bit about the mentioned downsides I also think modules should come with migrations, but in a different form. There should be generators, which generate migrations in the central migrations folder of the site using the module. They imho should always just generate the most recent version of migration needed. If a module does change the way to update old clients should be by having migrations to update from e.g. V1->V2. Those could be put in another generator or even just be put into a changelog. New users of a module wouldn‘t need to care about it, they directly get migrations generated for V2, existing users can leave their existing migration for V1 in their system and just add the update migration for V1->V2. This way one can have the benefits of modules supplying migrations to users, while not having the downsides and a imo simpler option for not needing to keep track of all the old stuff already changed till eternity, especially for new users, which really don‘t need to care about old versions. One last benefit of generation of code is that it‘s editable by the user. Like I could add localized labels to a field generated for a certain module, without any extra effort needed by the module creator. Another example might be adding additional fields to a template a module uses/creates, which might be needed just on the single site. This is also a downside, as editing could break the functionality, but I think it’s a quite clear danger and one can always generate the file(s) again to get to a working migration. Edit: One last point. Migrations in modules like they're intended by @bernhard can work as well. I hope it's clear that most issues I pointed out above are about control, the one for the actual system using the migrations. I'm not keen on automatic stuff happening, which could break my system. Others might be happy to let module creators be in charge of not skrewing things up. On the other hand control means more responsibility, like changes in versions are a little bit more work for the module user.
    1 point
  47. OK, I'm back in the office and can share some more thoughts. Thx for your input so far. I'm talking about "no authentication whatsoever", yes. But I'm not talking about "free-for-all-editing". Of course I don't want to have public access to all my pages, that would be nonsense. But I was wondering if it could make sense to have some tools of the backend available to the public. ProcessModules are one example. Forms could be another. I'll explain the idea by examples: Shopping Cart Have you ever developed a shopping cart for one of your clients? It's not that easy, right? You have to take care of data storage (cookies or localstorage or session), you have to take care of the user interface (adding items, removing them, changing amounts etc), you have to make sure everything is safe (sanitization, csrf etc) and so on. This is a lot of work to do and we all know that our website's frontends are all different, so building this for one client would mean that you have to build a lot of this functionality over and over again for the next client. The alternate approach: What if we could build backend modules (process modules) and grant access to guest users? We could build a shopping cart module, that handles all these complicated things for us and uses built in tools, such as csrf, inputfields, routing ( executeFoo(), executeBar() ). It could have expiration features based on the session time. It could make it easy to register new user accounts. It could be And the best: It would be reusable! That's a huge benefit and could make developing several tools so much more efficient and fun (and maintainable). Also think of forms in general. They are always a pain, because they need field validation (front + backend), need to remember their state on failure etc.; This is all already there in the backend! It's just hidden behind a login and I wondered if it couldn't be a huge possibility to bring certain parts of those great featureset to the public. Similar to FormBuilder, as I already stated. Another example: Think of a kind of photo-book service. Users could visit your site, start building their photobook, upload pictures etc.; Just to try things out. It would be great to give those features to your users without any signup process and create the account afterwards. Eg. during checkout.; Or what about a theatre booking service? Display a seatmap, book tickets, show prices, etc: All a LOT to do in the frontend. If we developed a backend-oriented module where we can change colors of links, masthead etc, this would make things so much easier and it would be one click for the next project!! I hope you get my point. Until now, I didn't really think of developing such things in the backend, because I thought it would be a lot more effort to style the backend to my needs than building a new custom frontend solution. But that's totally wrong! It's now really just changing some color codes in LESS when using RockSkinUikit module. These pages could even be loaded in an iframe in the frontend. The height of this iframe could be adjusted via javascript, so the user wouldn't even see that he is actually in the backend. Tools are already there: It's just displaying the admin page with ?modal=1 GET parameter. While thinking of this approach new ideas and possibilities keep popping up in my head: We could build a forum software. It could be a set of process modules, using CKEditor, image upload etc. - but of course only EDITABLE for logged in users. But the thing is: The process-modules (read-only) part would need to be visible also for guests. Building a forum software with ProcessWire as it is (or better to say: as we use it) now, would be a pain, because every installation of PW has a different backend. Moving all parts that can (and should!) be standardized to the backend would be a logical step IMHO. Well... while writing I had another idea: Maybe it would be possible to create a new user + role for such modules, like "public-backend". When a website visitor add's a product to the shopping cart, he could be automatically logged in as user "public-backend". This user could then have access to the process modules that handle all the shopping cart features. We already have the demo-mode of the backend, so I think it should be possible in general to bring non-registered users into the backend. What do you think? Sorry, ideas and visions are quite hard to explain in another language. Did I make things clearer? ?
    1 point
  48. Recently Bitrix (a popular commercial CMS here in Russia, known worldwide for its Bitrix24 CRM/PM/... solution) introduced similar functionality they called Site Constructor. This thing allows to build pages or parts of the pages from pre-defined blocks which can be static or dynamic. Site developer can style, modify or add their own blocks. They recommend this for landing pages for now, but are aiming to move all content management to those blocks. So there is some trend. I actually do use (almost) the same approach in PW. Most of my pages have content-page template with content_blocks Repeater Matrix field holding most of the content in repeater items. What is missing in my solution is: the easy ability to restrict the order, allowed types of those items (though possible with this module); the ability to easily move/duplicate content blocks from project to project (still think Repeater Matrix should be PageTable Matrix); the ability to easily preview the page built (like with this solution) in admin / edit it inline on the frontend. I see this way of building content very flexible, but still somehow unfinished. We have all the parts in PW to build a full-blown page bulder that will not allow too much for ones that do not need it, but will make it easy to build something really complex and interesting without programming. But those parts are not yet combined in a polished solution. I would certainly like to have it in PW (as a PageTable-like PageBuilder FieldType/Inputfield combo, probably).
    1 point
×
×
  • Create New...