Leaderboard
Popular Content
Showing content with the highest reputation on 04/29/2017 in all areas
-
After this tutorial you'll have learned how to: Build a Process module Make an AJAX request to backend Serve JSON as response Let's say you want to display the latest orders in a dashboard that you can access from admin panel. And you want it to refresh its content with a button click. Most straightforward and proper way (that I know of) is to create a Process module, as they're built for this purpose. First, create a directory under /site/modules/, call it ProcessDashboard, and create a file named ProcessDashboard.module under that directory. Following is about the least amount of code you need to create a Process module. <?php namespace ProcessWire; class ProcessDashboard extends Process { public static function getModuleInfo() { return [ 'title' => 'Orders Dashboard', 'summary' => 'Shows latest orders', 'version' => '0.0.1', 'author' => 'abdus', 'autoload' => true, // to automatically create process page 'page' => [ 'name' => 'order-dashboard', 'title' => 'Orders', 'template' => 'admin' ] ]; } public function ___execute() { return 'hello'; } } Once you refresh module cache from Modules > Refresh, you'll see your module. Install it. It will create an admin page under admin (/processwire/) and will show up as a new item in top menu, and when you click on it, it will show the markup we've built in execute() function. All right, now let's make it do something useful. Let's add create a data list to display latest orders. We'll change execute() function to render a data table. public function ___execute() { /* @var $table MarkupAdminDataTable */ $table = $this->modules->MarkupAdminDataTable; $table->setID($this->className . 'Table'); // "#ProcessDashboardTable" $table->headerRow([ 'Product', 'Date', 'Total' ]); // fill the table foreach ($this->getLatest(10) as $order) { $table->row([ $order['title'], $order['date'], $order['total'] ]); } // to refresh items $refreshButton = $this->modules->InputfieldSubmit; $refreshButton->name = 'refresh'; $refreshButton->id = $this->className . 'Refresh'; // "#ProcessDashboardRefresh" $refreshButton->value = 'Refresh'; // label of the button return $table->render() . $refreshButton->render(); } where getLatest() function finds and returns the latest orders (with only title, date and total fields) protected function getLatest($limit = 5, $start = 0) { // find last $limit orders, starting from $start $orders = $this->pages->find("template=order, sort=-created, limit=$limit, start=$start"); // Only return what's necessary return $orders->explode(function ($order) { return [ 'title' => $order->title, 'date' => date('Y-m-d h:i:s', $order->created), 'total' => $order->total ]; }); } When you refresh the page, you should see a table like this Now we'll make that Refresh button work. When the button is clicked, it will make an AJAX request to ./latest endpoint, which will return a JSON of latest orders. We need some JS to make AJAX request and render new values. Create a JS file ./assets/dashboard.js inside the module directory. window.addEventListener('DOMContentLoaded', function () { let refresh = document.querySelector('#ProcessDashboardRefresh'); let table = document.querySelector('#ProcessDashboardTable'); refresh.addEventListener('click', function (e) { // https://developer.mozilla.org/en/docs/Web/API/Event/preventDefault e.preventDefault(); // Send a GET request to ./latest // http://api.jquery.com/jquery.getjson/ $.getJSON('./latest', { limit: 10 }, function (data) { // check if data is how we want it // if (data.length) {} etc // it's good to go, update the table updateTable(data); }); }); function renderRow(row) { return `<tr> <td>${row.title}</td> <td>${row.date}</td> <td>${row.total}</td> </tr>`; } function updateTable(rows) { table.tBodies[0].innerHTML = rows.map(renderRow).join(''); } }); And we'll add this to list of JS that runs on backend inside init() function public function init() { $scriptUrl = $this->urls->$this . 'assets/dashboard.js'; $this->config->scripts->add($scriptUrl); } Requests to ./latest will be handled by ___executeLatest() function inside the module, just creating the function is enough, PW will do the routing. Here you should notice how we're getting query parameters that are sent with the request. // handles ./latest endpoint public function ___executeLatest() { // get limit from request, if not provided, default to 10 $limit = $this->sanitizer->int($this->input->get->limit) ?? 10; return json_encode($this->getRandom($limit)); } Here getRandom() returns random orders to make it look like there's new orders coming in. protected function getRandom($limit = 5) { $orders = $this->pages->find("template=order, sort=random, limit=$limit"); return $orders->explode(function ($order) { return [ 'title' => $order->title, 'date' => date('Y-m-d h:i:s', $order->created), 'total' => $order->total ]; }); } And we're done. When refresh button is clicked, the table is refreshed with new data. Here it is in action: 2017-04-29_19-01-40.mp4 (227KB MP4, 0m4sec) Here's the source code: https://gist.github.com/abdusco/2bb649cd2fc181734a132b0e660f64a2 [Enhancement] Converting page titles to edit links If we checkout the source of MarkupAdminDataTable module, we can see we actually have several options on how columns are built. /** * Add a row to the table * * @param array $a Array of columns that will each be a `<td>`, where each element may be one of the following: * - `string`: converts to `<td>string</td>` * - `array('label' => 'url')`: converts to `<td><a href='url'>label</a></td>` * - `array('label', 'class')`: converts to `<td class='class'>label</td>` * @param array $options Optionally specify any one of the following: * - separator (bool): specify true to show a stronger visual separator above the column * - class (string): specify one or more class names to apply to the `<tr>` * - attrs (array): array of attr => value for attributes to add to the `<tr>` * @return $this * */ public function row(array $a, array $options = array()) {} This means, we can convert a column to link or add CSS classes to it. // (ProcessDashboard.module, inside ___execute() method) // fill the table foreach ($this->getLatest(10) as $order) { $table->row([ $order['title'] => $order['editUrl'], // associative -> becomes link $order['date'], // simple -> becomes text [$order['total'], 'some-class'] // array -> class is added ]); } Now, we need to get page edit urls. By changing getLatest() and getRandom() methods to return edit links in addition to previous fields protected function getLatest($limit = 5, $start = 0) { // find last $limit orders, starting from $offset $orders = $this->pages->find("template=order, sort=-created, limit=$limit, start=$start"); return $orders->explode(function ($order) { return [ 'title' => $order->title, 'date' => date('Y-m-d h:i:s', $order->created), 'total' => $order->total, 'editUrl' => $order->editUrl ]; }); } protected function getRandom($limit = 5) { $orders = $this->pages->find("template=order, sort=random, limit=$limit"); return $orders->explode(function ($order) { return [ 'title' => $order->title, 'date' => date('Y-m-d h:i:s', $order->created), 'total' => $order->total, 'editUrl' => $order->editUrl ]; }); } and tweaking JS file to render first column as links function renderRow(row) { return `<tr> <td><a href="${row.editUrl}">${row.title}</a></td> <td>${row.date}</td> <td>${row.total}</td> </tr>`; } we get a much more practical dashboard.12 points
-
This site was one of my first PW attempts a couple of years ago. I recently upgraded it to PW 3.0.60 and changed it to delayed output rather than header/body/footer format. URL: https://rockpoolpainting.com.au/ PW: v3.0.60 Modules: Admin Help Page Admin Help Setup Admin Help Tab Admin Save Actions Database Backups Email Obfuscator File (for FormBuilder) Form Builder Forms Jquery jQuery DataTables Plugin Json-LD Schema Markup Simple Navigation Markup Sitemap XML Mobile Detect Modules Manager Modules Manager Notification Page Delete ProCache ProFields: Table Template Editor Templates Reference (multiple) Features: To keep the home page content fresh, the list of services (repeater field) and 'Examples of our work' (Gallery page children, limit = 3) are randomly shuffled. Display changes subject to ProCache refresh. Testimonials are also randomly shuffled to keep content fresh Submission of the testimonial form adds the form data to an unpublished item in a pagetable on the testimonials admin page so the admin doesn't have to go to Setup->Forms->testimonial->Entries to view/edit/approve/delete the new submission (thanks netcarver ) Framework is Bootstrap although didn't use Bootstrap profile All pages validate on https://validator.w3.org/ Considering the number of images and the fact it's on a shared host, page speeds are acceptable on https://developers.google.com/speed/pagespeed/insights/ Photos of Tuggerah are my kitchen that Chris painted10 points
-
This week we’ve got lots of details on our successful migration to Amazon AWS services for the processwire.com network of sites. We've also got updates on the current core and ProDrafts status. https://processwire.com/blog/posts/amazon-aws-now-powering-processwire.com-sites/5 points
-
AJAX or normal request doesn't really matter to backend. You just detect if request is an AJAX request and act accordingly. Because I had some things to do, I decided to procrastinate by building a simple module and write a tutorial for it.4 points
-
You can use regex to split lines into parts (name, amount, unit) and use a combination of WireArray and WireData objects. In the end you'll have turned a list like this Proteins: 45.3 g Calories: 650 kcal Fat: 30g Carbs: 1.2g Sugar: 0.5g Salt: 1.1g Saturates: 15g Fibre: 0.2g Into one that looks like $text = $sanitizer->textarea($page->body); // remove markup from body $nutrition = new WireArray(); // regex pattern to extract values $pattern = '/^' . // beginning of each line '\s*' . // may start with some whitespace '(?P<name>[^:]+)' . // name (first part until colon) '\s*:\s*' . // a colon that may be surrounded by whitespaces '(?P<amount>[\d,\.]+)' . // value (a number that may include , or . like 1,234.56) '\s*' . // after the number there may be some whitespace '(?P<unit>\w+)'. // unit (one or more letters) '\s*$/'; // there might be some whitespace before the end foreach (explode("\n", $text) as $line) { preg_match($pattern, $line, $parts); $nutrition->add((new WireData())->setArray([ 'name' => $parts['name'], 'amount' => floatval($parts['amount']), 'unit' => $parts['unit'] ])); } // Since $nutrition is a WireArray, you can do things like // $nutrition->sort('-amount'); // sort by amount descending // $cal = $nutrition->get('name=calorie'); // then create a list $content .= "<ul class='nutrition-list'>"; $content .= $nutrition->each('<li class="nutrition"><span class="nutrition__name">{name}</span> <span class="nutrition__value">{amount}</span> <span class="nutrition__unit">{unit}</span></li>'); $content .= "</ul>"; // Keep CSS out of PHP, in its own file $content .= "<style> .nutrition-list { list-style: none; display: flex; flex-wrap: wrap; } .nutrition { flex: 1 0 25%; } .nutrition__name { display: block; font-weight: bold; } .nutrition__value { font-size: 2em; } .nutrition__unit { opacity: 0.75; } </style>";4 points
-
@skylundy, turns out ProcessWire has a built-in class loader. It uses spl_autoload_register() internally. Given a folder structure: /site/templates/ components/ Composer.php Writer.php app.php where Composer.php and Writer.php are classes under \ProcessWire namespace You can autoload them using // /site/templates/app.php $loader = new WireClassLoader($wire); // or use $classLoader since it's already an API variable. // autoload classes inside /site/templates/components/ $componentsPath = wire()->config->paths->templates . 'components/'; $loader->addNamespace('ProcessWire', $componentsPath); // then you can reference classes just fine. $b = new Composer(); $w = new Writer(); $w->write($b->compose('hello')); http://processwire.com/api/ref/class-loader/3 points
-
Little admin helper module: Using this module you can view all template cache settings at once. E.g. cache status, cache time (configurable). Furthermore it adds the functionality to clear the entire template cache or just the template cache for a given template. Note that this may cause a temporary delay for one or more requests while pages are re-cached. GitHub: https://github.com/justb3a/processwire-templatecacheoverview2 points
-
try this: https://modules.processwire.com/modules/fieldtype-yaml/ or this: https://modules.processwire.com/modules/textformatter-multi-value/2 points
-
Some more links you might also find useful: Multiple templates or parents for users front-end system to handle logins, password resets and changing passwords (old but should still help you a lot if you want to implement it on your own without Front-End User module) @adrian's Some User related actions @adrian' Tracy Debugger with loooots of developer tools, such as User Switcher Panel lots of other info in the forum Hope this helps too.2 points
-
2 points
-
TextformatterTypographer A ProcessWire wrapper for the awesome PHP Typography class, originally authored by KINGdesk LLC and enhanced by Peter Putzer in wp-Typography. Like Smartypants, it supercharges text fields with enhanced typography and typesetting, such as smart quotations, hyphenation in 59 languages, ellipses, copyright-, trade-, and service-marks, math symbols, and more. It's based on the PHP-Typography library found over at wp-Typography, which is more frequently updated and feature rich that its original by KINGdesk LLC. The module itself is fully configurable. I haven't done extensive testing, but there is nothing complex about this, and so I only envisage a typographical bug here and there, if any.1 point
-
@abdus I found the issue and eliminated it. It appeared that the problem was caused by another check for $nutrition->name before printing the $content if(!empty($item->name)) { echo $content; } ?> As soon as I removed the if-check as we are already checking up for empty $item->name before that it worked like a charm! The full code shared above has been edited to provide the fully working sollution.1 point
-
Solved. @Zeka's suggestion to disable templateCompile solved all caching issues with class files. I was also using: spl_autoload_register() To dynamically load classes when they're called, unfortunately this doesn't seem to play well with PW. I used a regular include loop to add classes to app.php and it all works with namespacing. Big thanks to @abdus for the help!1 point
-
Does every character light up when you paste your content here? Can you see all groups matched up correctly on the right? https://regex101.com/r/sZsQg5/21 point
-
Great site! One recommendation: use an internal anchor after form submission to get back to the contact form if there are errors. At the moment you get to the top of the page and have to scroll down to the contact form to see the errors. Best regards1 point
-
1 point
-
// $nutrition = new WireArray(); $order = [ 'Calories', 'Fat', 'Saturated', 'Salt', 'Carbs', 'Fibre', 'Sugar', 'Proteins', ]; $markup = ''; foreach ($order as $o) { $item = $nutrition->get("name%=$o"); // render item $markup .= $item->name; $markup .= $item->amount; $markup .= $item->unit; } // use markup echo $markup; A more flexible way would be to create a Page field with AsmSelect that you can add/remove and reorder items. But for a simple setup, this should suffice.1 point
-
Hey @horst - now that Chrome (https://9to5google.com/2017/04/27/chrome-59-animated-png/) (and others) are supporting aPNGs have you looked into what is needed to support this in PW?1 point
-
@incognito.ms - I just took a look into that notice and I have fixed it in the latest version - thanks for reporting!1 point
-
@adrian, It's not a problem, just an idiosyncrasy. lol This is a great module and I just wanted to post an FYI. PS. Enjoy your 'time off'! Post pics of the native women from the island when you get a chance!1 point
-
Hi @adrian, welcome back. The first image shows the initial console window size and position with a code sample (not yet executed). This image shows the window size has automatically expanded vertically (after executing code) where the 'title bar' is now off screen. I am using chrome (maximized window). This doesn't prevent me from clicking the console icon (TD's toolbar) to close the window, then click it again to re-open it to display the console within the window boundaries. I can also toggle the browser window size to reset the console display size. I'm not reporting this as a bug because it doesn't prevent me from continuing, nor does it generate incorrect results. It is just something that I noticed.1 point
-
Since $nutrition is a WireArray, you can get calories using selectors and echo it $cal = $nutrition->get('name%=cal'); echo $cal->name; // "Calorie" echo $cal->amount; // "650.2" echo $cal->unit; // "kcal" anywhere you like. Check out WireArray documentation https://processwire.com/api/ref/wire-array/1 point
-
This is because the module extends Fieldtype Textarea but removes certain config inputfields (including contentType) that are not to be used with Fieldtype YAML. I'm guessing that the core Fieldtype Textarea has added an inputfield dependency for contentType since the Fieldtype YAML module was created, hence the warning. @owzim, maybe the module could set the unused config inputfields to hidden rather than not including them in the field config at all? foreach($parentInputfields as $inputfield) { if(in_array($inputfield->name, $forbidden)) { $inputfield->collapsed = Inputfield::collapsedHidden; } $inputfields->append($inputfield); }1 point
-
Thank you so much! You're all very helpful. Really appreciate it. thanks, Ray1 point
-
1 point
-
BTW, if you have GhostScript installed on your server, you can use it to compress PDFs locally using a command-line call... gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/ebook -dNOPAUSE -dQUIET -dBATCH -sOutputFile=out.pdf in.pdf Source: SuperUser.com (See 2nd answer.)1 point
-
Hi @ktagel, Welcome to the forums! That information, I believe, is described here. Have a look around at the other articles too, as they are indispensable. Feel free to post any questions you may have and someone will be along shortly to help.1 point
-
I might take a closer look at https://www.myresponsee.com/ and https://groundworkcss.github.io/. Edit: there are so many of them, like http://ink.sapo.pt/ also. I'm going to concentrate a bit on Responsee 4.1 point
-
I ran into an issue a Wednesday, where a client on satellite getting logged out every few seconds. The above fixed it, however, I had to change the config.php file in the /wire/ directory. I didn't see the setting in the /site/config.php, so I assume that it's due to changes in PW since this post was originally written. Thanks for the tip @adrian1 point
-
1 point
-
@adrian, I have a suggestion. Is there any way to make the results pane of the console scrollable? When code executed in the console produces a long output, the results pane expands vertically and the top (Close) heading is forced above the browser window. There is no way to move the window back down other than clearing the results which shrinks the window back to original size.1 point
-
I love this module! Maybe it's just me and my weird server, but installing it on ProcessWire 3.0.61 (fresh install) throws the following notices though: Notice: Undefined index: showDebugBar in /var/www/site/assets/cache/FileCompiler/site/modules/TracyDebugger/TracyDebugger.module on line 544 Warning: in_array() expects parameter 2 to be array, null given in /var/www/site/assets/cache/FileCompiler/site/modules/TracyDebugger/TracyDebugger.module on line 544 They vanish if the modules configuration is saved for the first time.1 point
-
I've created a quick module implementation of @Can's work. https://github.com/benbyford/PostProcessUrl Hard Truncates any page name / url to a max length if exceeded.1 point
-
Hi @nabo You can use http://modules.processwire.com/modules/process-admin-actions/ By default, it includes Page Active Languages Batcher that lets you enable or disable active status of multiple languages on multiple pages at once.1 point
-
1 point
-
I built this one on bootstrap 4 with a number of extras. ie. Animate on scroll for photos and carousels, navbar animate on scroll, lightbox, facebook sdk for page embed, google map embed, and font awesome. I am using the page tree as a row builder instead of navigation. I detached the menu system from the page tree to give more control on external links, single page website situations that need to link to sections, and add new window target options. It also lets you have different footer links vs header links this way. You can add something like a privacy policy to the footer and not the header and still have one place that controls your menus. Extensions used: Color Picker, Inputfield ACE Extended, Google map marker, MarkInPageTree, Media Library, and Hana Code. I made a little module that will combine all of my ACE code fields into one merged javascript file and one merged css file on page save also. (Filename time stamped for cache busting.) Its pretty neat! I have code tabs on pages that contain css and js ace fields tied to various things. containers, themes, quick styles, and my sitewide code snippets can all have css or js that are merged on page save. While the page rows are great for adding elements to single pages, the sitewide snippet template is very powerful also. Located under the template page, these are filed under the location name. It has fields for files, css, js, php, and html code. By adding this one page to the correct location; Top, Head, Body, Bottom, it will autoload and add code to various locations all at the same time and keep everything organized. ie. Install a header file, and it pulls in all of the related css styles and javascript code at the same time for you. You can check out my screenshots to see how it all works! You can insert bootstrap rows, columns, and add bootstrap elements to columns. https://www.lakehamiltonoutfitters.com/1 point
-
Welcome Slav! You might also be interested in this one: Or this one: Hope this helps.1 point
-
I used it at least here: https://nogajski.de http://joerg-hempel.com http://bella-italia-aachen.de/1 point
-
$pages->addHookAfter('saveReady', function($event) { $pages = $event->object; $page = $event->arguments(0); //check if on template if($page->template == 'item') { //is the checkbox checked to add children if ($page->add_children == 1) { //get the values in the page field type field foreach ($page->page_assignment as $child) { //if selected pages have childen if ($child->numChildren > 0) { //loop through those children foreach ($child->children as $grandchild) { //set values. $page->page_assignment->add($grandchild); } } } } } I figured it out. Two things to change if anyone is looking to do the same. 1. Use the right hook. saveReady instead of save. 2. Add the page object. $page->page_assignment->add($grandchild);1 point
-
Use AdminOnSteroids and enable Field and Template edit links, then hover on field label to see field name in a tooltip, or edit on click.1 point
-
Tense Tense (Test ENvironment Setup & Execution) is a command-line tool to easily run tests agains multiple versions of ProcessWire CMF. Are you building a module, or a template and you need to make sure it works in all supported ProcessWire versions? Then Tense is exactly what you need. Write the tests in any testing framework, tell Tense which ProcessWire versions you are interested in and it will do the rest for you. See example or see usage in a real project. How to use? 1. Install it: composer global require uiii/tense 2. Create tense.yml config: tense init 3. Run it: tense run For detailed instructions see Github page: https://github.com/uiii/tense This is made possible thanks to the great wireshell tool by @justb3a, @marcus and others. What do you think about it? Do you find it useful? Do you have some idea? Did you find some bug? Tell me you opinion. Write it here or in the issue tracker.1 point
-
Bootstrapping means 'require'-ing ProcessWire website from elsewhere, like this: <?php require_once '/absolute/path/to/your/PW/site/index.php'; foreach( \ProcessWire\wire('pages')->get('/')->children() as $ch ) { echo "{$ch->title}\n"; } I've just tried this so I won't lie to you: I literally just created this in in my home directory, ran it in terminal with PHP and got a list of page titles in the console. So by writing your own importer, people mean something like this: <?php require_once '/path/to/processwire/index.php'; $wire = \ProcessWire::wire(); // get stuff you want to import $articlesToImport = \OldCMS\DB::query($oldSiteDB, 'select * from articles join whatever'); // iterate over it and create pages for it foreach($articlesToImport->next() as $oldArticle){ $newArticle = new \ProcessWire\Page(); $newArticle->template = 'article'; $newArticle->parent = $wire->get('/articles/'); $newArticle->title = $oldArticle->get('title'); $newArticle->body = $oldArticle->get('html'); $newArticle->save(); } And that's it. You've just imported some articles from your old CMS to your brand new ProcessWire installation. Of course, on "real" pages you'll want to think about this for a bit; Maybe create pages for authors first, some categories, some tags, and then maybe add the pages in parts, so you don't timeout mid-import, or turn off timeout.1 point
-
Very interesting, that CSS-Grid stuff. I'm thrilled. Here is a short list of usefull links: (must read) http://jensimmons.com/post/feb-27-2017/learn-css-grid http://gridbyexample.com/examples/ (some specs and technical info) http://gridbyexample.com/browsers/ https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/CSS_Grid_and_Progressive_Enhancement https://drafts.csswg.org/css-grid/#grid-containers (some more) https://css-tricks.com/css-grid-one-layout-multiple-ways/ PS: you also can support older browsers with different fallbacks, there are tuts over that too.1 point
-
it's pretty simple, you just set a counter before the loop and increment it at the end of each loop increment... then you can use that incremental counter to conditionally add a class where needed. for example: $count = 0; foreach($items as $item) { $class = ['default-class']; if($count == 2) $class[] = 'some-other-class'; $classOut = implode(' ', $class); // echo your div with the imploded classes.. $count++; }1 point
-
There is a monolog plugin for Tracy: https://componette.com/nextras/tracy-monolog-adapter/ so if you're already using Tracy for logging errors in production mode (no debug bar), this might be an option. If people are interested in that approach, I'd be willing to add. PS - @BitPoet - not meaning to take away from your approach - just throwing another option out there.1 point
-
I just made the 0.3.0 version the new stable version. I've also added 0.3.1-beta with a new migration type: AccessMigration. This migration is only meant to change access rules for templates like shown below. List templates with changes, prepend a + or - if the useRoles setting does need to change. For those templates then list all roles which have changes (can be none) and supply which types of access should be added or removed. <?php class Migration_… extends AccessMigration { public static $description = "Update template access rules"; protected function getAccessChanges() { return [ '-blogpost' => [], // Remove useRoles from blogpost template '+basic-page' => [ // Add useRoles to basic-page 'subscriber' => ['+view'], 'editor' => ['+view', '+edit'], 'admin' => ['+view', '+edit', '+create'] // +create does require +edit ], 'tag' => [ 'subscriber' => ['-edit'], 'admin' => ['+full'] // view, edit, create and add ] ]; } } Edit: Had to remove the automatic +edit for +create, otherwise it's not clear to what to revert on rollbacks.1 point
-
A repeater will work, but it's sort of overkill for repeating just a single line of text. Behind the scenes it is creating a page for every line. This is probably the way to go. If you wanted your own custom markup you could explode and foreach a textarea. $lines = explode("\n", $page->my_textarea); if(count($lines)) { echo "<ul class='my-special-list'>"; foreach($lines as $line) { echo "<li class='my-list-item'>$line</li>"; } echo "</ul>"; } For sure, and for more complex repeatable items see the Table fieldtype.1 point
-
I just created a module that might be useful to those interested in this topic: https://processwire.com/talk/topic/9496-restrict-tab-view/1 point
-
This might be obvious since MariaDB should be totally compatible with mySQL, but just wanted to say that everything seems to be working fine with running pw with it after doing some selector tests.1 point
-
Hi, This is a complete server configuration block for NGiNX communicating with php-fpm. There are a few things that will require customisation: server_name root access_log and error_log fastcgi_pass - socket or TCP specification configuration blocks relating to 40x and 50x error handling Note the use of fastcgi_param HTTP_MOD_REWRITE On; which quiets an installer error about requiring mod_rewrite. You might also want to copy htaccess.txt to .htaccess in the ProcessWire top-level directory. server { listen 80 default_server; server_name localhost localhost.localdomain; index index.php index.html; root /var/www/html; access_log /var/log/nginx/access.log main; error_log /var/log/nginx/error.log notice; default_type application/x-php; ### SECURITY - Protect crucial files location ~ /\. { deny all; } location ~ /(COPYRIGHT|LICENSE|README|htaccess)\.txt { deny all; } location ~ ^/site(-[^/]+)?/assets/(.*\.php|backups|cache|config|install|logs|sessions) { deny all; } location ~ ^/site(-[^/]+)?/install { deny all; } location ~ ^/(site(-[^/]+)?|wire)/(config(-dev)?|index\.config)\.php { deny all; } location ~ ^/((site(-[^/]+)?|wire)/modules|wire/core)/.*\.(inc|module|php|tpl) { deny all; } location ~ ^/(site(-[^/]+)?|wire)/templates(-admin)?/.*\.(inc|html?|php|tpl) { deny all; } ### GLOBAL REWRITE location / { try_files $uri $uri/ /index.php?it=$uri&$args; } # pass the PHP scripts to FastCGI server on local socket # location ~ .+\.php((/|\?).*)?$ { fastcgi_pass unix:/run/php-fpm/php-fpm.sock; fastcgi_index index.php; fastcgi_split_path_info ^(.+\.php)(.*)$; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_param HTTP_MOD_REWRITE On; include fastcgi_params; } # redirect server error pages to the static page /40x.html # error_page 404 /404.html; location = /40x.html { root /usr/share/nginx/html; } # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } In the php-fpm configuration you need to specify unix socket or TCP connection parameters and possibly the chdir setting. These are distribution-dependent values and you will need to determine the correct values for your scenario. My configuration is as follows: ; The address on which to accept FastCGI requests. ; Valid syntaxes are: ; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific address on ; a specific port; ; 'port' - to listen on a TCP socket to all addresses on a ; specific port; ; '/path/to/unix/socket' - to listen on a unix socket. ; Note: This value is mandatory. ;listen = 127.0.0.1:9000 listen = /run/php-fpm/php-fpm.sock ; Set permissions for unix socket, if one is used. In Linux, read/write ; permissions must be set in order to allow connections from a web server. Many ; BSD-derived systems allow connections regardless of permissions. ; Default Values: user and group are set as the running user ; mode is set to 0666 listen.owner = nginx listen.group = nginx listen.mode = 0660 ; Chdir to this directory at the start. This value must be an absolute path. ; Default Value: current directory or / when chroot chdir = /var/www/html Please note that I researched these configurations and the preceeding security configuration from original documentation. I did not rely on howtos available on the Internet. Each has been carefully implemented and undergone significant testing before going into production. Regards, Neil Darlow1 point