Jump to content


Popular Content

Showing content with the highest reputation since 04/08/2015 in all areas

  1. 45 points
    https://www.baumrock.com/portfolio/individuelles-crm-und-controlling-tool/ I'm happy to share my biggest and most interesting ProcessWire project so far with you It's a 100% custom office-management solution that helps my client to keep track of all their contacts, projects and finance/controlling stuff. Conception was done back in 2016 and the software is productive since begin of this year. My client is very happy with the result and so am I. Some technical insights: Everything is done inside the PW Admin. I'm using the Reno Theme with some custom colors. In the beginning I was not sure if I should stay with the pw admin or build my own admin-framework but now I'm VERY happy that I went with PW Almost all of my custom Process Pages use my RockDatatables module - there are still some limitations but without it, this project would not have been possible For the charts I used Google Charts and chartjs - both play well together with the datatables and make it possible to display filtered data instantly: also my handsontable module was created for this project to have a nice and quick option for matrix data inputs: Lister and ListerPro were no options as i needed much more flexibility regarding data presentation (like colorization, filtering and building sums of selected rows): invoices are highly customisable as well and easy to create. PDFs are created by php and mPDF by the way: all data is dummy data populated via my Module RockDummyData have a nice weekend everybody
  2. 31 points
    In this tutorial I will cover how to use clsource's REST Helper classes to create a RESTful API endpoint within a PW-powered site and how to connect to it from the outside world with a REST client. This is a quite lengthy tutorial. If you follow all along and make it through to the end, you should get both, a working REST API with ProcessWire and hopefully some more basic understanding how these APIs work. As always with PW, there are many ways you could do this. My way of implementing it and the code examples are loosely based on a real world project that I am working on. Also, this is the first tutorial I am writing, so please bear with me if my instructions and examples are not that clear to understand. And please let me know if something is missing or could be made more clear. The steps covered: create templates and pages in the PW backend to get an API endpoint (an URL where the API can be accessed at) copy and save the REST Helper classes to your site create a template file and write some logic to receive and process data through our endpoint and send data back to the REST client test the whole setup with a Browser REST Client Addon I will not go into fundamentals and technical details on how RESTful APis are supposed to work. I assume that you have already read up on that and have a basic understanding of the principles behind that technology. Some helpful resources to brush up your knowledge: https://en.wikipedia.org/wiki/Representational_state_transfer http://www.restapitutorial.com/lessons/whatisrest.html The complete pages.php template is attached to this post for copy/paste. Lets get started. 1. create templates and pages in the PW backend to get an API endpoint (an URL where the API can be accessed) First we need to create some templates and pages in the PW backend to make our REST API accessible from the outside world through an URL (API endpoint). In my example this URL will be: https://mysite.dev/api/pages/ Note the "https" part. While this is not mandatory, I strongly recommend having your API endpoint use the https protocol, for security reasons. Further down in step 3 we will use this URL to create new pages / update and get data of existing pages. Go to your PW test site admin and: create 2 new templates: one is called "api", the other one "pages". For a start, they both have only a title field assigned. Just create the templates. We will create the corresponding files later, when we need them. enable "allow URL segments" for the "pages" template. We will need this later to access data sent by the requests from the client. in the Files tab of the "pages" template check "Disable automatic append of file: _main.php" create a new page under the home page with title, name and template "api" and set it to hidden create a child page for the "api" page with title, name and template "pages" The pagetree should look somewhat like this: Ok, now we're all set up for creating our API endpoint. If you browse to https://mysite.dev/api/pages/ you will most likely get a 404 error or will be redirected to your home page as we do not have a template file yet for the "pages" template. We will add that later in step 3. 2. copy and save the REST Helper classes to your site I have the REST Helper class sitting in site/templates/inc/Rest.php and include it from there. You could save it in any other location within your site/templates folder. I forked clsource's original code to add basic HTTP authentication support. Click here to open my raw gist, copy the contents and save them to /site/templates/inc/Rest.php In the next step we will include this file to make the classes "Rest" and "Request" available to our template file. 3. create a template file and write some logic to receive and process data through our endpoint and send data back to the client This will be the longest and most complex part of the tutorial. But I will try to present it in small, easy to follow chunks. Since we access our API at https://mysite.dev/api/pages/, we need to create a template file called "pages.php" for our "pages" template and save it to /site/templates/pages.php. Go ahead and create this file (if you're lazy, copy the attached file). Now right at the top of pages.php, we start with <?php require_once "./inc/Rest.php"; to include the REST Helper classes. Next, we initialize some variables that we will keep using later on // set vars with the default output $statuscode = 200; $response = []; $header = Rest\Header::mimeType('json'); 3.1 retrieve data with a GET request Now that we have the basics set up, we will next create the code for handling the easiest request type, a GET request. With the GET request we will ask the API to return data for an existing page. To let the API know which page it should return data for, we need to send the page id along with our request. I am attaching the page id as an url segment to the API endpoint. So the URL that the client will use to retrieve data for a page will look like: https://mysite.dev/api/pages/1234 where 1234 is the unique page id. Add following code to pages.php // if we have an urlsegment and it is a numeric string we get data from or update an existing page: handle GET and PUT requests if($input->urlSegment1 && is_numeric($input->urlSegment1)) { $pageId = $input->urlSegment1; // GET request: get data from existing page if(Rest\Request::is('get')) { // get the page for given Id $p = $pages->get($pageId); if($p->id) { $pdata = ["id" => $pageId]; // array for storing page data with added page id $p->of(false); // set output formatting to false before retrieving page data // loop through the page fields and add their names and values to $pdata array foreach($p->template->fieldgroup as $field) { if($field->type instanceof FieldtypeFieldsetOpen) continue; $value = $p->get($field->name); $pdata[$field->name] = $field->type->sleepValue($p, $field, $value); } $response = $pdata; } else { //page does not exist $response["error"] = "The page does not exist"; $statuscode = 404; // Not Found (see /site/templates/inc/Rest.php) } } } else { // no url segment: handle POST requests } // render the response and body http_response_code($statuscode); header($header); echo json_encode($response); Lets brake this down: First we check for a numeric url segment which is our $pageId. Then the Rest Request class comes into play and checks what type of request is coming in from the client. For the GET request, we want to return all data that is stored for a page plus the page id. This is all good old PW API code. I am using the $pdata array to store all page data. Then I am handing this array over to the $response variable. This will be used further down to render the JSON response body. If the page does not exist, I am setting an error message for the $response and a status code 404 so the client will know what went wrong. The else statement will later hold our POST request handling. The last 3 lines of code are setting the header and status code for the response and print out the response body that is sent back to the client. You can now browse to https://mysite.dev/api/pages/1 where you should see a JSON string with field names and values of your home page. If you enter a page id which does not exist you should see a JSON string with the error message. Lets move on to updating pages through a PUT request 3.2 update pages with a PUT request Since our API needs to know the id of the page we want to update, we again need to append an id to our endpoint url. In this example we will update the title and name of our homepage. So the request url will be: https://mysite.dev/api/pages/1. For the GET request above, anyone can connect to our API to retrieve page data. For the PUT request this is not a good idea. Thus we will add basic authentication so that only authorized clients can make updates. I use basic HTTP authentication with username and password. In combination with the https protocol this should be fairly safe. To set this up, we need an API key for the password and a username of our choosing. We add the API key in the PW backend: add a new text field "key" and assign it to the "api" template. edit the "api" page, enter your key and save. (I am using 123456 as key for this tutorial) Now add following code right after the if(Rest\Request::is('get')) {...} statement: // PUT request: update data of existing page if(Rest\Request::is('put')) { // get data that was sent from the client in the request body + username and pass for authentication $params = Rest\Request::params(); // verify that this is an authorized request (kept very basic) $apiKey = $pages->get("template=api")->key; $apiUser = "myapiuser"; if($params["uname"] != $apiUser || $params["upass"] != $apiKey) { // unauthorized request $response["error"] = "Authorization failed"; $statuscode = 401; // Unauthorized (see /site/templates/inc/Rest.php) } else { // authorized request // get the page for given Id $p = $pages->get($pageId); if($p->id) { $p->of(false); $p->title = $sanitizer->text($params["title"]); $p->name = $sanitizer->pageName($params["name"]); $p->save(); $response["success"] = "Page updated successfully"; } else { // page does not exist $response["error"] = "The page does not exist"; $statuscode = 404; // Not Found (see /site/templates/inc/Rest.php) } } } Breakdown: We check if the request from the client is a put request. All data that was sent by the client is available through the $params array. The $params array also includes $params["uname"] and $params["upass"] which hold our API user and key. We set API key and user and check if they match with the values that were sent by the client. If they don't match we store an error message to the response body and set the appropriate status code. If authentication went through ok, we get the page via PW API and update the values that were sent in the request body by the client. Then we put out a success message in the response body. If the page does not exist, we send the appropriate response message and status code, just like in the GET request example above. Now you might wonder how the client sends API user/key and new data for updating title and name. This is covered further down in step 4. If you want to test the PUT request right now, head down there and follow the instructions. If not, continue reading on how to setup a POST request for creating new pages. 3.2 create new pages with a POST request Final part of the coding part is creating new pages through our API. For this to work we need to implement a POST request that sends all the data that we need for page creation. We will do this at our endpoint: https://mysite.dev/api/pages/ Paste following code within the else statement that has the comment "// no url segment: handle POST requests": // POST request: create new page if(Rest\Request::is('post')) { // get data that was sent from the client in the request body + username and pass for authentication $params = Rest\Request::params(); // verify that this is an authorized request (kept very basic) $apiKey = $pages->get("template=api")->key; $apiUser = "myapiuser"; if($params["uname"] != $apiUser || $params["upass"] != $apiKey) { // unauthorized request $response["error"] = "Authorization failed"; $statuscode = 401; // Unauthorized (see /site/templates/inc/Rest.php) } else { // authorized request // create the new page $p = new Page(); $p->template = $sanitizer->text($params["template"]); $p->parent = $pages->get($sanitizer->text($params["parent"])); $p->name = $sanitizer->pageName($params["name"]); $p->title = $sanitizer->text($params["title"]); $p->save(); if($p->id) { $response["success"] = "Page created successfully"; $response["url"] = "https://mysite.dev/api/pages/{$p->id}"; } else { // page does not exist $response["error"] = "Something went wrong"; $statuscode = 404; // just as a dummy. Real error code depends on the type of error. } } } You already know what most of this code is doing (checking authorisation etc.). Here's what is new: We create a page through the PW API and assign it a template, a parent and basic content that was sent by the client. We check if the page has been saved and update our response body array with a success message and the URL that this page will be accessible at through the API for future requests. The client can store this URL for making GET or PUT requests to this page. If you're still reading, you have made it through the hard part of this tutorial. Congratulations. Having our code for reading, updating and creating pages, we now need a way to test the whole scenario. Read on to find out how this can be done. 4. test the whole setup with a Browser REST Client Addon The link in the heading will take you to a place from which you can install the very useful RESTClient addon to your favorite browser. I am using it with Firefox which is still the dev browser of my choice. Open a RESTClient session by clicking the little red square icon in the browsers addon bar. The UI is pretty straightforward and intuitive to use. 4.1 test the GET request Choose Method GET and fill in the URL to our endpoint. If you do not have a SSL setup for testing, just use http://yourrealtestdomain.dev/api/pages/1. If you happen to have a SSL test site with a self signed certificate, you need to point your browser to the URL https://yourrealtestdomain.dev/api/pages/ first in your test browser and add the security exception permanently. Otherwise RESTClient addon won't be able to retrieve data. If you have a test site with a 'real' SSL certificate, everything should be fine with using the https://... URL Hit send. In the Response Headers tab you should see a Status Code 200 and in the Response Body tabs a JSON string with data of your page. now change the 1 i the URL to some id that does not exist in your site and hit send again. You should get a 404 Status Code in the Response Headers tab and an error message "{"error":"The page does not exist"}" in the Response Body (Raw) tab. If you get these results, congrats! The GET request is working. For further testing you can save this request through the top menu Favorite Requests->Save Current Request. 4.1 test the PUT request Choose Method PUT and fill in the URL to our endpoint ending with 1 (http://yourrealtestdomain.dev/api/pages/1). In the top left click Headers->Content-Type: application/json to add the right content type to our request. If you miss this step, the request will not work. You will now see a "Headers" panel with all your headers for this request Click on Authentication->Basic Authentication. In the modal window that pops up, fill in user (myapiuser) and password (your API key). Check "Remember me" and hit Okay. You now should see Content-Type and Authorization headers in the "Headers" panel. Next, we need to send some data in the request body for updating our page title and name. Since we're using JSON, we need to create a JSON string that contains the data that we want to send. As I will update the home page for this example, my JSON reads { "title" : "Newhome", "name" : "newhome" } Be careful that you have a well formed string here. Otherwise you will get errors. Paste this into the "Body" panel of the REST Client addon. Hit send. In the Response Headers tab you should see a Status Code 200 and in the Response Body tabs a JSON string "{"success":"Page updated successfully"}". Now go to the PW backend and check if title and name of your page have been updated. If yes, congrats again. 4.2 test the POST request Choose Method POST and fill in the URL to our endpoint without any page id (http://yourrealtestdomain.dev/api/pages/). In the top left click Headers->Content-Type: application/json to add the right content type to our request. If you miss this step, the request will not work. You will now see a "Headers" panel with all your headers for this request Click on Authentication->Basic Authentication. In the modal window that pops up, fill in user (myapiuser) and password (your API key). Check "Remenber me" and hit Okay. You now should see Content-Type and Authorization headers in the "Headers" panel. Next, we need to send some data in the request body for updating our page title and name. Since we're using JSON, we need to create a JSON string that contains the data that we want to send. I will create a new page with template basic-page and parent /about/ for this example, my JSON reads { "template" : "basic-page", "parent" : "/about/", "title" : "New Page created through api", "name" : "newapipage" } Be careful that you have a well formed string here. Otherwise you will get errors. Paste this into the "Body" panel of the REST Client addon. Hit send. In the Response Headers tab you should see a Status Code 200 and in the Response Body tabs a JSON string "{"success":"Page created successfully","url":"https:\/\/mysite.dev\/api\/pages\/1019"}". Now go to the PW backend and check if title and name of your page have been updated. If yes, you're awesome! Summary By now you have learned how to build a simple REST API with ProcessWire for exchanging data with mobile devices or other websites. Notes I tested this on a fresh PW 2.7.2 stable install with the minimal site profile and can confirm the code is working. If you experience any difficulties in getting this to work for you, let me know and I will try to help. There purposely is quite a lot of repetion in the example code to make it easier to digest. In real life code you might not want to use procedural coding style but rather separate repeating logic out into classes/methods. Also, in life applications you should do more sanity checks for the authentication of clients with the API / for the data that is delivered by the client requests and more solid error handling. I skipped these to make the code shorter. RESTful services are by definition stateless (sessionless). My implementation within PW still opens a new session for each request and I haven't found a way around that yet. If anyone can help out this would be much appreciated. And finally big thanks to clsource for putting the Rest.php classes together. pages.php.zip
  3. 30 points
    We recently rebuilt the Architekturführer Köln (architectural guide Cologne) as a mobile-first JavaScript web app, powered by VueJS in the frontend and ProcessWire in the backend. Concept, design and implementation by schwarzdesign! The Architekturführer Köln is a guidebook and now a web application about architectural highlights in Cologne, Germany. It contains detailled information about around 100 objects (architectural landmarks) in Cologne. The web app offers multiple ways to search through all available objects, including: An interactive live map A list of object near the user's location Filtering based on architect, district and category Favourites saved by the user The frontend is written entirely in JavaScript, with the data coming from a ProcessWire-powered API-first backend. Frontend The app is built with the Vue framework and compiled with Webpack 4. As a learning exercise and for greater customizability we opted to not use Vue CLI, and instead wrote our own Webpack config with individually defined dependencies. The site is a SPA (Single Page Application), which means all internal links are intercepted by the Vue app and the corresponding routes (pages) are generated by the framework directly in the browser, using data retrieved from the API. It's also a PWA (Progressive Web App), the main feature of which is that you can install it to your home screen on your phone and launch it from there like a regular app. It also includes a service worker which catches requests to the API and returns cached responses when the network is not available. The Architekturführer is supposed to be taken with you on a walk through the city, and will keep working even if you are completely offline. Notable mentions from the tech stack: Vue Vue Router for the SPA functionality VueX for state management and storage / caching of the data returned through the API Leaflet (with Mapbox tiles) for the interactive maps Webpack 4 for compilation of the app into a single distributable Babel for transpilation of ES6+ SASS & PostCSS with Autoprefixer as a convenience for SASS in SFCs Google Workbox to generate the service worker instead of writing lots of boilerplate code Bootstrap 4 is barely used here, but we still included it's reboot and grid system Backend The ProcessWire backend is API-only, there are no server-side rendered templates, which means the only PHP template is the one used for the API. For this API, we used a single content type (template) with a couple of pre-defined endpoints (url segments); most importantly we built entdpoints to get a list of all objects (either including the full data, or only the data necessary to show teaser tiles), as well as individual objects and taxonomies. The API template which acts as a controller contains all the necessary switches and selectors to serve the correct response in <100 lines of code. Since we wanted some flexibility regarding the format in which different fields were transmitted over the api, we wrote a function to extract arbitrary page fields from ProcessWire pages and return them as serializable standard objects. There's also a function that takes a Pageimage object, creates multiple variants in different sizes and returns an object containing their base path and an array of variants (identified by their basename and width). We use that one to generate responsive images in the frontend. Check out the code for both functions in this gist. We used native ProcessWire data wherever possible, so as to not duplicate that work in the frontend app. For example: Page names from the backend translate to URLs in the frontend in the form of route parameters for the Vue Router Page IDs from ProcessWire are included in the API responses, we use those to identify objects across the app, for example to store the user's favourites, and as render keys for object lists Taxonomies have their own API endpoints, and objects contain their taxonomies only as IDs (in the same way ProcessWire uses Page References) Finally, the raw JSON data is cached using the cache API and this handy trick by @LostKobrakai to store raw JSON strings over the cache API. Screenshots
  4. 24 points
    Thanks to all that have been helping us to wrap up issue reports on GitHub in preparation for the next master version of the ProcessWire core. Unless any major issues surface, most likely 3.0.95 (next week) will be the next master version. Like last week's version 3.0.93, this week's version 3.0.94 continues along the same path in clearing up new and existing issue reports, and fine tuning little details for the master branch. If you come across any new significant issues please submit them in the issues repository. Or if you opened an issue awhile ago and we haven't fixed it yet, please check that the issue is still applicable and reply to let us know. No blog post this week because I don't have anything else new or interesting to write about other than this. But I am definitely getting excited about having a new master version ready. There's no doubt, this is a really nice upgrade relative to the current master 3.0.62. Have a great weekend!
  5. 24 points
    Client-side image resizing has been on our roadmap for awhile, and this week we've got it ready on the dev branch in version 3.0.63. People expressed interest in this feature in the comments to last week's post, and I promised to give it a closer look sooner rather than later. After getting that closer look, and doing some research, I realized we could get in this week's version. After using it now for a couple of days this week, I think people are really going to like this feature, and it works a lot better than I had originally guessed it could. https://processwire.com/blog/posts/processwire-3.0.63-adds-client-side-image-resizing/
  6. 23 points
    MediaLibrary Update: MediaLibrary can now be found in the official module list. Out of necessity, I've started to implement a simple media library module. The basic mechanism is that it adds a MediaLibrary template with file and image fields. Pages of this type can be added anywhere in the page tree. The link and image pickers in CKEditor are extended to allow quick selection of library pages from dropdowns. In the link picker this happens in the MediaLibrary tab, where you can also see a preview of the selected image. In the image picker, simply select a library from the dropdown at the top, everything else is handled by standard functionality. I've put the code onto github. This module is compatible with ProcessWire 3. Steps to usage: Download the module's zip from github (switch to the pw3 branche beforehand if you want to test on PW 3.x) and unpack it into site/modules Click "Modules" -> "Refresh" in the admin Click "Install" for MediaLibrary For testing, create a page with the MediaLibrary template under home (give it an expressive title like 'Global Media') and add some images and files Edit a differnt page with a CKEditor field and add a link and an image to see the MediaLibrary features in action (see the screencap for details) Optionally, go into the module settings for MediaLibrary Note: this module is far from being as elaborate as Kongondo's Media Manager (and doesn't plan to be). If you need a feature-rich solution for integrated media management, give it a look. Feel free to change the settings for MediaFiles and MediaImages fields, just keep the type as multiple. There are some not-so-pretty hacks for creating and inserting the correct markup, which could probably be changed to use standard input fields, though I'm a bit at a loss right now how to get it to work. I've also still got to take a look at error handling before I can call it fit for production. All feedback and pointers are appreciated (that's also why I post this in the development section). Edit 09.03.2016 / version 0.0.4: there's now also a "Media" admin page with a shortcut to quickly add a new library. Edit 01.05.2016: Version 0.0.8: - The module now supports nested media libraries (all descendants of eligible media libraries are also selectable in link/image picker). - There's a MediaLibrary::getPageMediaLibraries method you can hook after to modify the array of available libraries. - You can switch between (default) select dropdowns or radio boxes in the module configuration of MediaLIbrary to choose libraries. Edit 10.10.2018: Version 0.1.3: - Dropped compatibility for ProcessWire legacy versions by adding namespaces - Allow deletion of libraries from the Media overview admin page - Added an option to hide media libraries from the page tree (optionally also for superusers)
  7. 23 points
    This latest version of ProcessWire on the dev branch adds a new Inputfield module called “Toggle” that is an alternative to the existing Checkbox Inputfield. It also adds a nicer way to make column width adjustments to your fields when editing a template. This post covers all the details with screenshots and a short video: https://processwire.com/blog/posts/pw-3.0.139/
  8. 21 points
    This week we’ve started developing a new admin theme for ProcessWire that aims to be a community collaboration. We’ve now got some good momentum with lots to share in this post. We also get into some technical details and have screenshots as well. https://processwire.com/blog/posts/working-towards-a-new-admin-theme/
  9. 20 points
    In this tutorial I want to write about handling special cases and change requests by clients gracefully without introducing code bloat or degrading code quality and maintainability. I'll use a site's navigation menu as an example, as it's relatable and pretty much every site has one. I'll give some examples of real situations and change requests I encountered during projects, and describe multiple approaches to handling them. However, this post is also about the general mindset I find useful for ProcessWire development, which is more about how to handle special cases and still keep your code clean by making the special case a normal one. The problem: Special cases everywhere Since ProcessWire has a hierarchical page tree by default, as a developer you'll usually write a function or loop that iterates over all children of the homepage and displays a list of titles with links. If the site is a bit more complex, maybe you additionally loop over all grandchildren and display those in drop-down menus as well, or you even use a recursive function to iterate over an arbitrary amount of nested child pages. Something like this: function buildRecursiveMenu(Page $root): string { $markup = ['<ul class="navigation">']; foreach ($root->children() as $child) { $link = '<a class="navigation__link" href="' . $child->url() . '">' . $child->title . '</a>'; $children = $child->hasChildren() ? buildRecursiveMenu($child) : ''; $markup[] = "<li class="navigation__item">{$link}{$children}</li>"; } $markup[] = '</ul>'; return implode(PHP_EOL, $markup); } But then the requests for special cases come rolling in. For example, those are some of the requests I've gotten from clients on my projects (by the way, I'm not saying the clients were wrong or unreasonable in any of those cases - it's simply something I needed to handle in a sensible way): The homepage has the company's name as it's title, but the menu link in the navigation should just say "Home". The first page in a drop-down menu should be the top-level page containing the drop-down menu. This was requested because the first click on the top-level item opens the sub-navigation instead of navigating to that page (espcially on touch devices, such as iPads, where you don't have a hover state!), so some visitors might not realize it's a page itself. Some top-level pages should be displayed in a drop-down menu of another top-level page, but the position in the page tree can't change because of the template family settings. The menu needs to contain some special links to external URLs. For one especially long drop-down menu, the items should be sorted into categories with subheadings based on a taxonomy field. In general, my solutions to those requests fall into three categories, which I'll try to elaborate on, including their respective benefits and downsides: Checking for the special case / condition in the code and changing the output accordingly (usually with hard-coded values). Separating the navigation menu from the page tree completely and building a custom solution. Utilizing the Content Management Framework by adding fields, templates and pages that represent special states or settings. Handling it in the code This is the simplest solution, and often the first thing that comes to mind. For example, the first request (listing the homepage as "Home" instead of it's title in the navigation) can be solved by simply checking the template or ID of the current page inside the menu builder function, and changing the output accordingly: // ... $title = $child->template->name === 'home' ? 'Home' : $child->title; $link = '<a class="navigation__link" href="' . $child->url() . '">' . $title . '</a>'; // ... This is definitely the fastest solution. However, there are multiple downsides. Most notably, it's harder to maintain, as each of those special cases increases the complexity of the menu builder function, and makes it harder to change. As you add more special conditions, it becomes exponentially harder to keep changing it. This is the breeding ground for bugs. And it's much harder to read, so it takes longer for another developer to pick up where you left (or, as is often cited, for yourself in six months). Also, now we have a hard-coded value inside the template, that only someone with access to and knowledge of the template files can change. If the client want's the link to say "Homepage" instead of "Home" at some point, they won't be able to change it without the developer. Also, each special case that is hidden in the code makes it harder for the client to understand what's going on in terms of template logic - thus increasing your workload in editorial support. That said, there are definitely some times where I would go with this approach. Specifically: For smaller projects that you know won't need to scale or be maintained long-term. If you are the only developer, and/or only developers will edit the site, with no "non-technical" folk involved. For rapid prototyping ("We'll change it later") Building a custom solution My initial assumption was that the main navigation is generated based on the page tree inside ProcessWire. But of course this isn't set in stone. You can just as easily forgo using the page tree hierarchy at all, and instead build a custom menu system. For example, you could add a nested repeater where you can add pages or links on a general settings page, and generate the menu based on that. There are also modules for this approach, such as the Menu Builder by @kongondo. This approach is not the quickest, but gives the most power to the editors of your site. They have full control over which pages to show and where. However, with great power comes great responsibility, as now each change to the menu must be performed manually. For example, when a new page is added, it won't be visible in the menu automatically. This is very likely to create a disconnect between the page tree and the menu (which may be what you want, after all). You may get ghost pages that are not accessible from the homepage at all, or the client may forgot to unpublish pages they don't want to have any more after they've removed them from the menu. I would only go with this approach if there are so many special cases that there hardly is a "normal case". However, even then it might not be the best solution. The direct relationship between the page tree, the menu structure and page paths are one of the strongest features of ProcessWire in my opinion. If many pages need to be placed in special locations without much structure in regards to what templates go where, maybe you only need to loosen up the template family settings. I have built one site without any template family restrictions at all - any page of any template can go anywhere. It's definitely a different mindset, but in this case it worked well, because it allowed the client to build custom sections with different page types grouped together. It's a trade-off, as it is so often, between flexibility and workload. Weigh those options carefully before you choose this solution! Utilizing the CMF This is the middle ground between the two options above. Instead of building a completely custom solution, you keep with the basic idea of generating a hierarchical menu based on the page tree, but add fields and templates that allow the editor to adjust how and where individual pages are displayed, or to add custom content to the menu. of course, you will still write some additional code, but instead of having hard-coded values or conditions in the template, you expose those to the client, thereby making the special case one of the normal cases. The resulting code is often more resilient to changing requirements, as it can not one handle that specific case that the client requested, but also every future change request of the same type. The key is to add fields that enable the client to overwrite the default behaviour, while still having sensible defaults that don't require special attention from the editor in most cases. I'll give some more examples for this one, as I think it's usually the best option. Example 1: Menu display options This is probably the first thing you thought of for the very first change request I mentioned (displaying the homepage with a different title). Instead of hard-coding the title "Home" in the template, you add a field menu_title that will overwrite the normal title, if set. This is definitely cleaner than the hard-coded value, since it allows the client to overwrite the title of any page in the menu. I'll only say this much in terms of downsides: Maybe the menu title isn't really what the client wanted - instead, perhaps they feel limited because the title is also displayed as the headline (h1) of the page. In this case, the sensible solution would be an additional headline field that will overwrite the h1, instead of the menu_title field. Which fields are really needed is an important consideration, because you don't want to end up with too many. If each page has fields for the title, a headline, a menu title and an SEO-title, it's much more complicated than it needs to be, and you will have a hard time explaining to the client what each field is used for. Another example in this category would be an option to "Hide this page in the menu". This could be accomplished by hiding the page using the inbuilt "hidden" status as well, but if it's hidden it won't show up in other listings as well, so separating the menu display from the hidden status might be a good idea if your site has lots of page listings. Example 2: "Menu link" template One solution that is quite flexible in allowing for custom links to pages or external URLs is creating a menu-link template that can be placed anywhere in the page tree. This templates can have fields for the menu title, target page and/or external target URL. This way, you can link to another top-level page or an external service inside a drop-down menu, by placing a Menu Link page at the appropriate position. This is also a clean solution, because the navigation menu will still reflect the page tree, making the custom links visible and easily editable by the editors. A minor downside is that those templates are non-semantical in the sense that they aren't pages with content of their own. You'll need to make sure not to display them in listings or in other places, as they aren't viewable. It may also require loosening up strict family rules - for example, allowing for Menu Link pages to be placed below the news index page, which normally can only hold news pages. Example 3: Drop-down menu override This one is a more radical solution to override drop-down menus. You add a repeater field to top-level pages, similar to the one mentioned as a custom solution, where you can add multiple links to internal pages or URLs. If the repeater is empty, the drop-down menu is generated normally, based on the sub-pages in the page tree. But if the repeater contains some links, it completely overrides the drop-down menu. It's similar to the fully custom solution in that as soon as you override a sub-menu for a top-level page, you have to manually manage it in case the page structure changes. But you can make that decision for each top-level page individually, so you can leave some of them as is and only have to worry about the ones that you have overwritten. Again, this offers sensible defaults with good customizability. A downside is that the mixed approach may confuse the client, if some changes to the page tree are reflected in the drop-down menu directly, while others don't seem to have any effect (especially if you have multiple editors working on a site). Finding the right solution So how do you choose between the approaches? It depends on the client, the requirements, and on what special cases you expect and want to handle. Sometimes, a special request can be turned down by explaining how it would complicate editorial workflows or have a negative impact on SEO (for example, if you risk having some pages not accessible from the homepage at all). Also, make sure you understand the actual reason behind a change request, instead of just blindly implementing the suggestion by the client. Often, clients will suggest solutions without telling you what the actual problem is they're trying to solve. For example: In one case, I implemented the drop-down override mentioned in example three. However, what the client really wanted was to have the top-level page as the first item in the drop-down menu (see the example requests mentioned above). So they ended up overwriting every single drop-down menu, making the menu harder to maintain. In this case, it would have been better to go with a more specialized solution, such as adding a checkbox option, or even handling it in the code, since it would have been consistent throughout the menu. Another example was mentioned above: If the client requests an additional "Menu title" field, maybe what they really need is a "Headline" field. I recommend reading Articulating Design Decisions by Tom Greever; it includes some chapters on listening to the client, finding out the real reason behind a change request, and responding appropriately. It's written from a design perspective, but is applicable to development as well, and since UX becomes more important by the day, the lines between the disciplines are blurred anyway. Conclusion I realize now this reads more like a podcast (or worse, a rant) than an actual tutorial, but hopefully I got my point across. ProcessWire is at is greatest if you utilize it as a Content Management Framework, creating options and interfaces that allow for customizability while retaining usability for the client / editor. I usually try to hit a sweet spot where the editors have maximum control over the relevant aspects of their site, while requiring minimal work on their part by providing sensible defaults. Above, I listed some examples of requests I've gotten and different solutions I came up with to handle those with custom fields or templates. Though in some cases the requirements call for a custom solution or a quick hack in the template code as well! What are some of the special requests you got? How did you solve them? I'd love to get some insights and examples from you. Thanks for reading!
  10. 20 points
    I've been in Minnesota all week for a family reunion and only a few minutes at the computer every day, so I don't have a core update or anything worthwhile to write about this week. But now I'm headed back home so will be back to a regular schedule next week. Hope that you have a great weekend!
  11. 20 points
    This week we’ve got another update to the new Uikit 3 based admin theme, AdminThemeUikit. It now has the flavor of Tom Reno’s fantastic AdminThemeReno, and now it's starting to feel like home. In this post we take a closer look with screenshots and more. https://processwire.com/blog/posts/uikit-3-admin-theme-updates/
  12. 19 points
    The Page Hit Counter module for ProcessWire implements a simple page view counter in backend. Page views of visitors are automatically tracked on defined templates, with monitoring of multiple page views. This gives you a quick overview of how many visitors have read a news or a blog post, for example, without first having to open complex tools such as Google Analytics. This module quickly provides simple information, e.g. for editors. Or, for example, to sort certain news by most page views. For example for "Trending Topics". Works with ProCache and AdBlockers. With a lightweight tracking code of only ~320 bytes (gzipped). And no code changes necessary! In addition GDPR compliant, since no personal data or IP addresses are stored. Only session cookies are stored without information. In addition, there are some options, for example filtering IP addresses (for CronJobs) and filtering bots, spiders and crawlers. You can also configure the lifetime of the session cookies. Repeated page views are not counted during this period. It is also possible to exclude certain roles from tracking. For example, logged in editors who work on a page are not counted as page views. Sort by hits and access page views (hit value) Each trackable template has an additional field called phits. For example, you want to output all news sorted by the number of page views. // It is assumed that the template, e.g. with the name "news", has been configured for tracking. $news = $pages->find("template=news, sort=-phits"); To output the page views of a tracked page, use: echo $page->phits; Example: Tracking a page hit via API and jQuery If you want to track a template that does not represent a full page to automatically inject a tracking script, you can define allowed API templates in the module that you can track. Below is an example of how you can track a click on news tag using jQuery. This will allow you to find out which keywords are clicked the most. For example, you can sort and display a tag cloud by the number of hits. Suppose your keywords have the template "news_tag". The template "news_tag" was also configured in the Page Hit Counter Module as a trackable API template. Example PHP output of keywords / tags: // Required: the data attribute "data-pid" with the ID of the template to be tracked. echo $pages->find("template=news_tag, sort=-phits")->each("<a href='{url}' class='news_tag' data-pid='{id}'>{title}</a>"); Example Tracking Script with jQuery: /** * Required: Data attribute "data-pid" with the ID of the news tag template * Required: Send the POST request to the URL "location.pathname.replace(/\/?$/, '/') + 'phcv1'" * Required: The POST parameter "pid" with the ID of the template */ $(function(){ if($('a.news_tag').length > 0) { $('a.news_tag').each(function(){ var tPID = $(this).data("pid"); if(tPID) { $(this).on("click", function(){ $.post(location.pathname.replace(/\/?$/, '/') + 'phcv1', {pid: tPID}); }); } }); } }); So simply every click on a tag is counted. Including all checks as for automatic tracking. Like Bot Filtering, Session Lifetime, etc. Notice: Tracking with URL segments If the option "Allow URL Segments" is activated on a template, the hits are only counted if the base URL of the page is called. If you want the hit to be counted even when a segment is requested, you MUST configure the segments in the template configuration. How to do this can be found here. If you use dynamic segments, configure them as RegEx. There is currently no other option. The problem is that the Page Hit Counter hooked into the PageNotFound process. If URL segments are allowed but not defined, a 404 is never triggered. This means that the Page Hit Counter cannot be called. _______________________________________________________ Background: This module is the result of a customer requirement, where the editors are overwhelmed with analytics or no tracking tools were allowed to be used. However, a way had to be found to at least count page views in a simple form for evaluations. Furthermore, by using ProCache, a way had to be found to count views of a page without clearing the cache. _______________________________________________________ Pros Automatic Page View Tracking Lightweight tracking code, only ~320 bytes (gzipped) No code or frontend changes necessary Works with ProCache! Even if no PHP is executed on the cached page, the tracking works Works with browser AdBlockers No cache triggers (for example, ProCache) are triggered. The cache remains persistent GDPR compliant, session-based cookie only, no personal information Filtering of IPs and bots possible Exclude certain roles from tracking Ability to reset Page Views Works with all admin themes Counter database is created as write-optimized InnoDB API to track events for templates that are not viewable No dependencies on libraries, pure VanillaJS (Automatic tracking script) Works in all modern browsers Pages are sortable by hits Cons Only for ProcessWire version 3.0.80 or higher (Requires wireCount()) Only for PHP version 5.6.x or higher No support for Internet Explorer <= version 9 (Because of XMLHttpRequest()) No historical data, just simple summation (Because of GDPR) Segment URLs can only be counted if the segments are defined Planned Features / ToDos API access to hit values Since version 1.2.1 Possibility to sort the pages by hits (Request by @Zeka) Since version 1.2.0 Don't track logged in users with certain roles (Request by @wbmnfktr) Since version 1.1.0 Possibility to reset the counter for certain pages or templates (Request by @wbmnfktr) Since version 1.1.0 Better bot filter Since version 1.1.0 Disable session lifetime, don't store cookies to track every page view (Request by @matjazp) Since version 1.2.1 Option to hide the counter in the page tree (Request by @matjazp) Since version 1.2.1 Option to hide the counter in the page tree on certain templates Since version 1.2.1 API to track events for templates that are not viewable Since version 1.2.2 Changelog 1.2.5 Bug-Fix: When counting 404 hits, cookies are no longer set. The session lifetime is deactivated for the 404 page Enhancement: Documentation improvement regarding URL segments 1.2.4 Bug-Fix: Resetting the counters on system pages (e.g. 404) does not work (Reported by wbmnfktr) Bug-Fix: Tracking endpoint is logged as 404 if module "Jumplinks" is installed (Reported by wbmnfktr) Enhancement: Corrected few typos (Merged from Sergio #6 – THX!) 1.2.3 Bug-Fix: Tracking script triggers 404 if pages are configured without slash (#3) Reported by @maxf5 Enhancement: Reduction of the tracking script size if it's gzipped (~320 bytes) Enhancement: Documentation improvement Enhancement: Corrected few typos 1.2.2 New feature: API to track events for templates that are not viewable Enhancement: Documentation improvement 1.2.1 API access to hit values Use $page->phits Bug-Fix: No tracking on welcomepage (Reported by wbmnfktr; Thx to matjazp) Bug-Fix: Tracking script path on subfolders (Reported by matjazp) Bug-Fix: Tracking on pages with status "hidden" Enhancement: Change database engine to InnoDB for phits field Enhancement: Option to disable session lifetime set session lifetime to 0, no cookies Enhancement: Better installation check Enhancement: AJAX Request asyncron Enhancement: Reduction of the tracking script size by ~20% Enhancement: Option to hide the counter in the page tree You can output the counter with the field name "phits" Enhancement: Option to hide the counter in the page tree on certain templates Enhancement: Option for activate general IP validation Enhancement: Reduction of tracking overhead up to ~30ms Enhancement: Better bot list for detection 1.2.0 New feature: Sort pages by hits – New field phits Migrate old counter data to new field 1.1.0 New feature: Exclude tracking of certain roles New feature: Reset Page Views Better bot filter and detection 1.0.0 Initial release Notes By default, the page views are stored as INT in the database. This allows a maximum counter value of 4.2 billion views (4,294,967,295) per page. If you need more, change the type to BIGINT directly in the database. But I recommend to use Google Analytics or similar tools if you have such a large number of users. _______________________________________________________ Download GitHub: ProcessWire Page Hit Counter (Version 1.2.5) PW Module Directory: ProcessWire Page Hit Counter (Version 1.2.5) Install via ProcessWire (Classname): PageHitCounter _______________________________________________________ Update information If you have used version 1.2.1 from the DEV branch, please replace it completely with the new master version.
  13. 19 points
    MarkupMenu is a markup module for generating menu trees. When provided a root page as a starting point, it generates a navigation tree (by default as a HTML "<ul>" element wrapped by a "<nav>" element) from that point onwards. If you've also provided it with current (active) page, the menu will be rendered accordingly, with current item highlighted and items rendered up to that item and its children (unless you disable the "collapsed" option, in which case the full page tree will be rendered instead). Modules directory: https://modules.processwire.com/modules/markup-menu/ GitHub repository: https://github.com/teppokoivula/MarkupMenu Usage As a markup module, MarkupMenu is intended for front-end use, but you can of course use it in a module as well. Typically you'll only need the render() method, which takes an array of options as its only argument: echo $modules->get('MarkupMenu')->render([ 'root_page' => $pages->get(1), 'current_page' => $page, ]); Note: if you omit root_page, site root page is used by default. If you omit current_page, the menu will be rendered, but current (active) page won't be highlighted etc. A slightly more complex example, based on what I'm using on one of my own sites to render a (single-level) top menu: echo $modules->get('MarkupMenu')->render([ 'current_page' => $page, 'templates' => [ 'nav' => '<nav class="{classes} menu--{menu_class_modifier}" aria-label="{aria_label}">%s</nav>', 'item_current' => '<a class="menu__item menu__item--current" href="{item.url}" tabindex="0" aria-label="Current page: {item.title}">{item.title}</a>', ], 'placeholders' => [ 'menu_class_modifier' => 'top', 'aria_label' => 'Main navigation', ], 'include' => [ 'root_page' => true, ], 'exclude' => [ 'level_greater_than' => 1, ], ]); Note: some things you see above may not be entirely sensible, such as the use of {menu_class_modifier} and {aria_label} placeholders. On the actual site the "nav" template is defined in site config, so I can define just these parts on a case-by-case basis while actual nav markup is maintained in one place. Please check out the README file for available render options. I'd very much prefer not to keep this list up to date in multiple places. Basically there are settings for defining "templates" for different parts of the menu (list, item, etc.), include array for defining rules for including in the menu and exclude array for the opposite effect, classes and placeholders arrays for overriding default classes and injecting custom placeholders, etc. 🙂 MarkupMenu vs. MarkupSimpleNavigation TL;DR: this is another take on the same concept. There are many similarities, but also some differences – especially when it comes to the supported options and syntax. If you're currently using MarkupSimpleNavigation then there's probably no reason to switch over. I'd be surprised if someone didn't draw lines between this module and Soma's awesome MarkupSimpleNavigation. Simply put I've been using MSN (...) for years, and it's been great – but there are some issues with it, particularly in the markup generation area, and it also does some things in a way that doesn't quite work for me – the xtemplates thing being one of these. In some ways less about features, and more about style, I guess 🙂 Anyhow, in MarkupMenu I've tried to correct those little hiccups, modernise the default markup, and allow for more flexibility with placeholder variables and additional / different options. MarkupMenu was built for ProcessWire 3.0.112+ and with PHP 7.1+ in mind, it's installable with Composer, and I have a few additional ideas (such as conditional placeholders) still on my todo list. One more small(ish) difference is that MarkupMenu supports overriding default options via $config->MarkupMenu. I find myself redefining the default markup for every site, which until now meant that each site had a wrapper function for MarkupSimpleNavigation (to avoid code / config repetition), and this way I've been able to leave that out 🙂 Requirements ProcessWire >= 3.0.112 PHP >= 7.1.0 If you're working on an earlier version of ProcessWire or PHP, use MarkupSimpleNavigation instead.
  14. 19 points
    hi everybody, this is a preview of a module that i'm working on for quite a long time. I needed it for an intranet office management application that i'm still working on. It makes it very easy to create very customized Datatables using the awesome jquery datatables plugin (www.datatables.net) Download - sorry, removed as it changes too frequently; closed alpha - contact me if you think you can contribute Remarks: The module is intended to be used on the backend. Loading styles is at the moment only done via adding files to the $config->styles/scripts arrays. Also the communicaton to javascript is done via the $config->js() method that is built into the admin and would have to be implemented manually on frontend use. But it would not be difficult of course Installation: Nothing special here, just download + install edit: removed some parts, because i made a complete rewrite of the module (and may not have been the last one)! [...] removed Why i created this module: of course i know @Soma s module but i needed a lot more features and the newer datatables version. also i like to define all the columns as objects and have everything on one place. lister & markupadmindatatable: nice for basic tables but lacks of features to modify the appearance of the cell values (like rendering icons, background colors and so on) datatables provides a great frontend API for filtering, showing/hiding columns, getting data, modifying it... it also plays well together with frontend charts like google chart api in this case: todo / roadmap: reload only one row add filters to all columns (in future also dropdowns, smaller than, bigger than, Regex, ...) make it possible to add table on frontend pages make buttons look like pw buttons make it possible to set settings globally and only for one table provide easy way of colorbars (percentage, red/green), maybe at different positions (left, top, right, bottom) provide easy way of adding action items (edit, show, link etc - visible or onhover) make own layout for tables (topleft, topright, bottom etc to make it easy to create extensions and show messages) privide way of simple javascript plugins (like I already have for row sums etc) provide easy way of handling actions for all selected items (delete selected, update selected...) provide easy way of reloading data (--> easy when using ajax source) easy way of showing/hiding columns excel/csv/clipboard export GUI for table setup processmodule to show different tables (lister replacement)
  15. 19 points
    The focus this week was on covering the queue of issue reports, and a whole lot of progress was made. Plus some other useful additions can be found ProcessWire 3.0.113. This post covers all the details— https://processwire.com/blog/posts/processwire-3.0.113-core-updates/
  16. 19 points
    More updates! Here is what was added since my last post: FieldtypeDatetime now supports format argument. Which allows you to pass PHP date format string and retrieve your datetime values formatted the way you want. Besides FieldtypeDatetime fields, the built-in fields created and modified are also support new format argument. FieldtypeOptions is now supported. first and last fields on PageArray types are now supported. As per @eelkenet's request. See above post for details on those. Finally, now there is a way to add support for any fieldtype you want via third-party module. What you need to do is create a module with the name exactly as the fieldtype you want to add support for with "GraphQL" prefix. So for FieldtypeMapMarker it would be GraphQLFieldtypeMapMarker. Then you need to add three required methods and install it. This will automatically add GraphQL support for your desired fieldtype and it will be available in your GraphQL api. Checkout the documentation and an example module for reference.
  17. 19 points
    Hi everyone, This is the new official thread for the module that was previewed some time ago here: https://processwire.com/talk/topic/14117-module-settings-import-export/ Big thanks to @Robin S for help testing and feature suggestions! http://modules.processwire.com/modules/module-settings-import-export/ https://github.com/adrianbj/ModuleSettingsImportExport Module features Ability to copy and paste settings from one PW install to another Optional automatic backup of module settings on uninstall and ability to restore settings if you reinstall the module Backup current settings at any time Restore backed up settings at any time Import option checks module name and version number and warns if importing settings from a different version As always, let me know if you find any problems or have any suggestions!
  18. 19 points
    This week has been busy! We've got updates to our demo site, a new functions API, and other core additions that you may find useful in template files. https://processwire.com/blog/posts/processwire-3.0.39-core-updates/
  19. 18 points
    Between a lot of big, not-web-related projects at work, I could squeeze in having a little a fun with ProcessWire too. What was needed was a shop-like solution where employees can order lunch snacks from our local butcher. Previously, this was handled with hand-written lists in each production hall that would be forwarded over fax to the butchers, leading to all kinds of transliteration and pricing errors. Our Central Services department looked at different software packages for that, but none of them did exactly what they needed. ProcessWire to the rescue. In little more than two days, I got a fitting solution up and running: PW 3.0.52 The only two non-standard modules used are @adrian's AdminActions and my own DatetimeAdvanced Role based template access UIKit 3 with modals, flex layout, mobile-ready with hamburger menu on smaller devices Everything is a page Synchronisation of employee accounts from time management software (> 600) into PW (120 lines, runs twice a day) Mixed authentication (Windows Active Directory if user is available there, local password if not) Account balance system with manual deposits and automated checking / detuction / refunding for orders, complete transaction history visible for the user Article categories (page reference), product images (not used yet), simple product variants (1 level, text + price) Ordering contents of a shopping cart for a single day or multiple days ahead of time within (admin configurable) time constraints (order window for following day ends X hours before, holidays excluded, etc.) Delivery locations (buildings) changeable by users themselves Integrated favorites system Management area in frontend for balance bookings, managing articles, previewing orders Daily collected orders for the butchery are printed into PDF through fpdf with a tiny wrapper Product images option with thumbnails (currently not used) Small additions like price validity dates or saving and re-using shopping carts will be added over the next months The shop landing page with all relevant information: Articles are grouped by category and have dropdowns for variations (with optional price addition/discount): Editable shopping cart with option to order same selection for multiple days: Minimallistic product item editor:
  20. 17 points
    Simple Contact Form Using Google Recaptcha & Valitron validation library I just finished creating a simple Contact-Form for a client's website so i thought it would be really helpfull to share it with the community. The contact form uses: Google recaptcha for validating the user is not a robot (https://github.com/google/recaptcha) Valitron validation library for validating the form fields (https://github.com/vlucas/valitron) Twitter Bootstrap 3.0.0 for form HTML The contact-form is located inside a contact-page, so the bare minimum you need in order to setup your own is: contact.php (template file used by your contact-page) _contact-controller.php (file used as a controller for your contact-form functionality like send email, validate fields etc) 2 extra lines inside your composer.json file So, let's start: First you need to update your composer.json file adding 2 lines inside the require object: "vlucas/valitron": "^1.2", "google/recaptcha": "~1.1" Here is a sample composer.json file: { "name": "processwire/processwire", "type": "library", "description": "ProcessWire CMS/CMF", "keywords": [ "cms","cmf", "content management system" ], "homepage": "https://processwire.com", "authors": [ { "name": "Ryan Cramer", "email": "ryan@processwire.com", "homepage": "https://processwire.com", "role": "Developer" } ], "require": { "php": ">=5.3.8", "ext-gd": "*", "vlucas/valitron": "^1.2", "google/recaptcha": "~1.1" }, "autoload": { "files": [ "wire/core/ProcessWire.php" ] }, "minimum-stability": "dev" } open console and navigate to processwire root folder (where composer.json file is) on this step i assume you have already setup composer for your project, otherwise google it run the following command: composer update this will create a vendor folder (if it does not already exist) and download valitron and google recaptcha libraries. Then open your contact-page template file(usually named contact.php inside your templates directory) and add the following: * Note: The form below uses bootstrap 3.0.0 css, so if you are using something else you need to make the appropriate changes. <?php namespace ProcessWire; include('_contact-controller.php') ?> <div class="container"> <div class="row"> <div class=" col-md-4"> <h2>Contact Form</h2> <?php if($session->flashMessage):?> <div class="alert <?=!$session->sent && (!$v->validate() || !$resp->isSuccess()) ? 'alert-danger' : 'alert-success'?>" role="alert"> <?php echo $session->flashMessage;?> </div> <?php endif;?> <form id="contact-form" method="post"> <div class="form-group <?=$v->errors('name') ? 'has-error' : ''?>"> <label for="name">Name</label> <input class="form-control" name="name" id="name" type="text" value="<?=$sanitizer->text($input->post->name)?>"> </div> <div class="form-group <?=$v->errors('email') ? 'has-error' : ''?>"> <label for="email">Email</label> <input class="form-control" name="email" id="email" type="text" value="<?=$sanitizer->text($input->post->email)?>"> </div> <div class="form-group <?=$v->errors('message') ? 'has-error' : ''?>"> <label for="message">Message</label> <textarea class="form-control" name="message" id="message"><?=$sanitizer->text($input->post->message)?></textarea> </div> <div class="form-group"> <!-- Google Recaptcha code START --> <div class="g-recaptcha" data-sitekey="<?=$googleSiteKey?>"></div> <script type="text/javascript" src="https://www.google.com/recaptcha/api.js"> </script> <!-- Google Recaptcha code END --> </div> <button type="submit" class="btn btn-primary">SEND</button> </form> </div> </div> </div> <?php //here we remove the flash-message because it is already shown above the form. $session->remove('flashMessage'); //reset 'sent' variable for future submit $session->sent = false; ?> Next create a file inside you templates directory with name: _contact-controller.php: and set the required variables($googleSiteKey, $contactFormRecipient, $contactPageID) <?php namespace ProcessWire; /** * here we include Valitron & Google recaptcha libraries * make sure the path is correct in your template */ include(dirname(__FILE__) . "/../../vendor/vlucas/valitron/src/Valitron/Validator.php"); include(dirname(__FILE__) . '/../../vendor/google/recaptcha/src/ReCaptcha/ReCaptcha.php'); /** * here we add the form field values to Valitron */ $v = new \Valitron\Validator(array( 'name' => $sanitizer->text($input->post->name), 'email' => $sanitizer->email($input->post->email), 'message' => $sanitizer->text($input->post->message), ) ); /** * validation rules set for each form field * For more details on Valitron/Validator usage visit: * https://github.com/vlucas/valitron */ $v->rule('required', ['name', 'email', 'message']); $v->rule('lengthMin', 'name', 5); $v->rule('email', 'email'); /** * set Google recaptcha site-key & secret-key * create a new key from: https://www.google.com/recaptcha/admin */ $googleSiteKey = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'; $googleSecretKey = 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY'; /** * set the email of the contact form recipient(usually the website owner) */ $contactFormRecipient = 'your@company.com'; /** * set the id of contact-page in order to redirect there when the form is sent */ $contactPageID = '1045'; //here we check whether the 'name' field exists inside post variables (which means the form is posted) if ($input->post->name) { //if fields validation passes if ($v->validate()) { $reCaptcha = new \ReCaptcha\ReCaptcha($googleSecretKey); $resp = $reCaptcha->verify($input->post->{'g-recaptcha-response'}, $_SERVER["REMOTE_ADDR"]); //if google-recaptcha validation passes if ($resp->isSuccess()) { //This is the HTML message $message = ' <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Contact Form | ' . $input->post->name . '</title> </head> <body> <p>' . $input->post->message . '</p> </body> </html>'; //here we send the form to $contactFormRecipient wireMail($contactFormRecipient, $input->post->email, "Contact Form | " . $input->post->name, $message); //here we set a flash-message to notify the user that the form was successfully sent $session->flashMessage = 'Thank you for your message! We will get in touch with you shortly.'; //save in session that the form is sent $session->sent = true; //finally redirect user to contact-page $session->redirect($pages->get($contactPageID)->url); } else { //self explain $session->flashMessage = 'Error while validating you are not a robot!'; } } } ?> Thats all! You now have a simple contact-form working with captcha and field validation! I would be more than happy to help anyone having problems on the setup.
  21. 17 points
    Version 3.0.135 of ProcessWire on the dev branch focuses on adding additional layers of security via .htaccess updates, adds clarity to debug mode, and improves upon the installer— https://processwire.com/blog/posts/pw-3.0.135/
  22. 17 points
    RedBeanPHP is a simple and easy-to-use ORM, and this module is a lightweight ProcessWire wrapper and/or loader for it. The main task of the module is loading and setting up RedBeanPHP with database credentials from $config. There are some config settings for defining how RedBeanPHP should behave, and the module also exposes some often-used methods, but that's just about it. For more details (including a rant about why one might prefer separate ORM in some cases), take a look at the README file. Please note that, for almost all use cases, the data modeling features of ProcessWire are much better choice than a separate database structure of your own, but in those rare cases where that's not the situation, it's good to have options. This module was a side product of one of my own projects, and I thought I might as well share it with you folks. You can grab the module from GitHub: https://github.com/teppokoivula/RedBeanPHP.
  23. 17 points
    This latest version on the dev branch adds a new site profile to the core, adds useful new functions to our $mail API variable, and makes significant enhancements to our $sanitizer API variable: https://processwire.com/blog/posts/processwire-3.0.105-core-updates/
  24. 17 points
    This week we have a new core version on the development branch and some nice updates to our Uikit admin theme in development. This post covers them in detail, includes a screencast and links to a live demo of the updates. https://processwire.com/blog/posts/processwire-3.0.56-and-uikit-admin-theme-updates/
  25. 17 points
    I am glad that this project is helping ProcessWire getting more devs on board :). I just want to say that I wouldn't have been able to finish ProcessVue if it wasn't for the amazing ProcessWire community. I believe that the community truly is the biggest selling point for new users (like me). Before trying ProcessWire I used OctoberCMS for a while but when I was stuck I got 0 support from the forums, so...althought the CMS is based on the amazing Laravel framework, I just left! I think that ProcessWire is extremely powerful and flexible and with time will become the tool of choice for frontend developers, the new GraphQL module will also help on this direction. Droves of frontend developers are looking for a CMS like this, they just don't know it exists! The usual keywords they use when looking for a SPAs CMS is "Decoupled CMS" or "Headless CMS", and I believe that that's exactly what ProcessWire is for! Some frontend developers prefer to use NodeJS, but the learning curve is huge if you need it for a non trivial project, and the worst thing of all is that after two weeks ANY js tool you may have used is outdated. See for example how Angular has been replaced with React or Vue, and Gulp with Webpack. That doesn't mean that I am against improvements in this regard, I just feel that it's just too much for us poor frontend devs to cope with! ProcessWire is stable, easy to use and won't change API every week. BTW, after that I migrate ProcessVue to GraphQL I am also planning to add Auth0 login integration with JWT, as I think that login/signup is a common feature in SPAs. I am sure I'll have to annoy @Nurguly Ashyrov and the rest of ProcessWire community for getting it in sync with ProcessWire users, but the result should be quite useful
  26. 17 points
    Hi everyone I´m creating an AdminTheme based on Semantic UI framework. Here is the beta version. I 'm fixing bugs. Github here Changelog: 0.0.1 --- Fixed background color / image.
  27. 17 points
    We've got some great updates for you this week, especially for those still running ProcessWire 2.x. ProcessWire 3.x also got a lot more stable this week. In fact, this week ProcessWire 2.x and 3.x got a lot closer! https://processwire.com/blog/posts/processwire-3.0.21-and-2.8.21/
  28. 17 points
    I got a client request to make Word template so she can create an image of the weekly menu of a restaurant to post on Facebook. I usually get sick when I have to deal with M$ Word - it's frustration and it's not meant to be used things like that. So I figured out a better system: PW + html2canvas.js. It took more time to finish but it was worth the trouble. The main issues were html2canvas versus some CSS issues, plus switching to different languages in PW on each line. Also, JavaScript cannot offer named downloads, only "download.png", so I had to use HTML5 attribute "download" on links to be able to add custom download filename. I used ProField modules Table and Matrix Repeater which allow very pleasant editing of menu items, even in multilanguage context. Front-end editing is created using the built-in front-end editor. I was about to achieve inline editing of menu items but it soon turned out that repeaters and tables can't be edited this way. Anyway, it's still better compared to my module FEEL because sections are reloaded using Ajax and the admin lightbox shows only the fields I choose. Here's a quick screencap - when the gray background turns to dark gray, then the menu is not HTML but canvas, that can be downloaded.
  29. 16 points
    This module lets you restrict users to a certain branch of the page tree - it can limit editing permissions, as well as the page list view to this branch. http://modules.processwire.com/modules/admin-restrict-branch/ https://github.com/adrianbj/AdminRestrictBranch Restricted View Non-restricted View Note that this module does not add permissions (unlike how PageEditPerUser and PageEditPerRole work), so the user must have template level permissions to edit the pages in the restricted branch. What this does allow though is giving all users/roles editing access for the home template and allowing that to inherit all the way through the page tree and let this module restrict to specific branches. As you can see from the screenshots you can specify how to determine the branch to restrict the user to - either via a matched role name, or via a dedicated page select field on the user's profile. The match role name works like this - if you have a series of branches called: Branch One, Branch Two, etc, users with a role named: branch-one will only have access to Branch One. You can also decide whether to restrict page tree viewing as well and editing privileges (default) or just editing privileges. The Branch Exclusions option is important for things like external PageTable parent branches etc. Main module config settings User specific branch setting on user profile page This module came out of my personal needs as well as this discussion: https://processwire.com/talk/topic/11428-project-design-main-shop-hundreds-of-affiliates/ As always, feedback is very welcome.
  30. 16 points
    Hi all, I am new to ProcessWire module development. Just recently getting back to using PW again after a couple of years away from it. I was looking for a SASS/SCSS/Compass pre-processor module for PW and couldn't find any. The closest I could find was AllInOneMinify but it doesn't do SASS/SCSS. I decided to make this module (with heavy inspiration from AOIM). This is a pretty basic module that takes one or more sass/scss/compass file(s) and compile them automatically in your template. Github link https://github.com/lesaff/ProcessWire-Sassify I really appreciate any comments, suggestions, bug fixes, etc. Hopefully this is the first of many modules. Thanks Rudy Note: Added to Modules directory, under http://modules.processwire.com/modules/sassify/
  31. 16 points
    Excellent write-up Ryan! Here's a quick tip. If you are processing large amounts of data, you should always use PW's field joining options to achieve maximum performance. Even if you only use a single field, you should get around 50% faster execution when the field is joined straight away. With two fields joined, the execution time will be cut into half (i.e. 100% increase in speed). Let's say you need to export the e-mail addresses of thousands of customers. Here's a simplified example using "on-demand" join // Prepare a pointer for writing the output $fp = fopen('customers.csv', 'w') or die('Failed opening the file for writing'); // Ask PW to join two fields (the regular find() also supports this). $options = ['loadOptions' => ['joinFields' => ['customer_email', 'customer_number']]]; // Searching for imaginary customers $selector = 'template=customer'; // Find the pages using the new method foreach($pages->findMany($selector, $options) as $page) { // Write the data in CSV format fputcsv($fp, [$page->customer_number, $page->customer_email]); } // Free the pointer fclose($fp); As a reminder, you can also force some fields to be always joined (through PW admin). @Joer: That is pretty much what the implementation of findMany() does behind the scenes. However splitting the job into smaller batches still makes sense if you use multiple workers to process the data (e.g. for MapReduce or whatever) or if the execution time of your script is limited.
  32. 16 points
    This week we're going to look at our path for growth in 2016, which is already off to a nice start! This includes where we've been, where we're going, and how we will get there–with your help. We actually do have several core updates prepared too, but mostly smaller things that will fit better in next week's post. This post originally started as a section of our 2016 roadmap, but I decided it really deserved its own post, so saved it for this week so that we could cover it more in-depth. However, you might consider this part of, or a continuation of, our 2016 roadmap. But rather than looking internally, much of this post looks externally, outside of our software and community, and into the bigger picture of ProcessWire in the web development/design world. https://processwire.com/blog/posts/growing-processwire-in-2016/
  33. 16 points
    This is a reworked website of Imre Baksa, a Hungarian actor and director. His former (static) website was also made by me back in around 2009. He asked me to do a redesign but I decided to involve ProcessWire to make content management easier. http://baksaimre.hu/ Lesson 1: templates - no thanks It is tempting to use ready-made templates for a project because most of the work is done, there's some tweaking here and there and mission completed. Okay, this is the theory. I have to admit that I have never found a suitable template/theme for my projects, but being a web designer this is the way it should be, I guess. This time I found one that looked fine: Landed from HTML5 UP. In fact there was a self-made design for the site and this template looked 90% similar, so it really seemed to be an easy task to bring it into ProcessWire. Soon turned out that some of the fancy features of the template required advanced JavaScript knowledge. That means, I had to dive into it and learn how it worked. I did so and I was able to tweak things then, but I wasn't satisfied with the outcome. So I went back to the drawing board and dropped the template. This experience reassured that starting from scratch would have been a better choice on the long run. The good thing is that I learned some new things that will come in handy in the future. Lesson 2: template engines - Latte is still my best friend I was using Nette's templating engine, Latte in previous projects and I liked it a lot. It is similar to Twig, which is more widespread, so I decided to try that for learning purposes. This sounded like an easy journey because ProcessWire supports Twig through modules. However, at the end of the day I sort of regretted that. Latte has some small helper features that makes templating easier, and these differences made up a huge difference. Maybe I'm getting old but I felt Twig more of a nuissance after Latte. Finally I got rid of Twig and I guess I will never look back I will keep using Latte in the future even if I have to bootstrap it manually. Lesson 3: frontend development is hard Having ProcessWire at hand, backend is the minor part in web development, at least in smaller projects. I've used only a few jQuery plugins but making them work together nicely was a real challenge. At the end I had to make a compromise by removing SmoothScroll because it didn't work well with JScrollPane. Even so, I had tough times to eliminate content "jumps" when a lightbox is opened, and to make JScrollPane work the way I wanted to work. Making the whole thing responsive added another level of complexity as features needed to be destroyed/reinitialized on different screen sizes. I like to polish things as much as possible but the current state of the web makes it almost impossible to reach certain level of perfection. Moduling up This was the project where I felt that a link checker button would be helpful so started to develop InputfieldURLChecker. I use this in a few projects and it does the job nicely. My other pet PW module FEEL also got some polish during site development. For site-wide settings I used MultiValueTextformatter so I didn't need to create a field for every setting, just one for all. Other modules used: SEO: must have! Admin Template Columns for better admin layout Page List Image Label: to add featured image to the list of pages in the admin Pageimage Remove Variations: remove unused image variations Forms: one step closer to simpler form processing For the contact form I used Nette Forms, because it is so easy to add forms to the site with it, with frontend & backend validation in one go. Processing them still needs some sweat, but this time I finally put together a class to make this easier. Surely it still needs some iterations but it's a huge help: it takes only a few parameters to save submissions as Pages or send email to the admin or the visitor. For email templates I also used Latte (what else? ). I also created a "json_storage" field to store submission. The beauty is that while it's only one field (a textarea), in the admin it is displayed as formatted key-value pairs using a hook. Summary The site looks fresh and the year knob on the left makes it fun to use. Content management is easy as 1-2-3 thanks to ProcessWire, even if it's mostly me who will deal with the updates. While it's not visible on the site, in the background there are many improvements to my development tools and workflow that will be of huge help in the future.
  34. 16 points
    The Background In April 2015, we were asked by CSDC Systems, to revamp their website. CSDC Systems is a Toronto-based company selling cloud-powered, process improvement technology to local governments, regulatory institutions, courts, universities, and granting agencies all over the world. CSDC Systems had a website that was built on Drupal 7.x, was difficult to update internally by their staff, had maintenance issues apart from being a very design dated website. The Objectives >> Create a modern, world-class website that sells CSDC's Solutions >> Highlight the benefits and advantages of the solutions and the company >> Make the site responsive and browsable across devices >> Give them a system that could be updated by non-technical users in their company. The Challenges >> Re-architect the entire website to make it usable by b2b clients >> Rewrite the copy with pain/gain/ benefits orientation >> Redesign to make it more visually appealing despite long copy >> Get results Re-imagining the CSDC Systems Website >> The old site had 350 pages of information with the bulk of it being blogs/ news. Blogs and News were ported and reformatted to make it visually appealing. >> 120 pages of copy to do with Solutions, Services, Platform, Case Studies and Whitepapers were rewritten and visualized ground up >> The persuasion structure with social proof - testimonials, Case Studies, Why CSDC was re-imagined and then reconstructed The Technology >> This was more a fairly large, prestigious, communications-driven website without any major technology challenges. >> The site uses Processwire CMS with Foundation 5 CSS framework >> The front-end page structure and design were mirrored using repeaters and fields at the backend. >> Forms, Profields, Autolinks, Tags, SEO are some of the plugins used >> The site uses ProCache. The Result The tutorial for the client over Skype lasted one hour and the happy part is that they are up and way confidently after that. The client's response: I was surprised that the site administration could be so intuitive and manageable by anyone with little technical knowledge. Coming from an old Drupal website, we shall take that as a true compliment for Processwire. The site is blazing fast. Records A: 94% on Google Page Speed and A: 92% on YSlow at GTMetrix. A nifty 1.6s load time with proCache turned on. You can view the new site at: http://www.csdcsystems.com/
  35. 16 points
    Today we've relaunched the website of one of the most beautiful castles in Germany: "Castle Marienburg". The Castle was built as a birthday present of King George V of Hanover (1819-1878) to his wife Queen Marie (1818-1907). Located right next to neuwaerts hometown Hanover and being one of the greatest Tourist attractions in our region it was a real pleasure to be allowed to bring the feeling of this venerable building to the digital age. // Some parts of the website are not yet translated. We know about this I'll add some details and technical notes next week (if anyone is interested).
  36. 15 points
    This module is (or will be) the successor of RockGrid. It uses http://tabulator.info/ as grid library that is completely open sourced (MIT, no commercial extensions) and as far as I've seen so far at least as powerful as ag-grid, which RockGrid is using. GOALS: Support various data sources (RockFinder, JS, PHP, SQL) Supported only in PW Backend + Uikit Easy setup of new fields INSTALLATION: Just install the main module. RockMarkup is a required dependency. Please also have a look at the RockMarkup examples before jumping into RockTabulator! DEMO: STATUS: The module is early alpha and can be downloaded from github: https://github.com/BernhardBaumrock/RockTabulator It took me two weeks of rewriting RockMarkup/RockTabulator to get where I am now and I think I'm quite happy with the setup. There might be some breaking changes though in the near future. At the moment the main features that should be working properly are: Data sources: SQL, PHP, JS Sandbox for easy setup Multilang This means basically everything that is possible with Tabulator is also possible with RockTabulator. Though, there are no custom helpers/plugins yet. That's the next step and whoever is willing to help, please give me a shout 🙂 If you have any questions or ideas please let me know 🙂
  37. 15 points
    This week we've got a lot of updates on our core dev branch, including new features, issue resolutions and more. For starters, we've added support for making the Trash and Restore features available to non-superusers, along with related improvements. Plus we've got several new useful and interesting page traversal methods and properties added to our $page API. https://processwire.com/blog/posts/processwire-3.0.107-core-updates/
  38. 15 points
    Just wanted to share what I recently used to create forms in modules and in frontend using the API and Inputfield modules PW provides and uses on its own. I think many newcomers or also advanced user aren't aware what is already possible in templates with some simple and flexible code. Learning this can greatly help in any aspect when you develop with PW. It's not as easy and powerful as FormBuilder but a great example of what can be archieved within PW. Really? Tell me more The output markup generated with something like echo $form->render(); will be a like the one you get with FormBuilder or admin forms in backend. It's what PW is made of. Now since 2.2.5~ somewhere, the "required" option is possible for all fields (previous not) and that makes it easier a lot for validation and also it renders inline errors already nicely (due to Ryan FormBuilder yah!). For example the Password inputfield already provides two field to confirm the password and will validate it. De- and encryption method also exists. Or you can also use columns width setting for a field, which was added not so long ago. Some fields like Asm MultiSelect would require to also include their css and js to work but haven't tried. Also file uploads isn't there, but maybe at some point there will be more options. It would be still possible to code your own uploader when the form is submitted. Validation? If you understand a little more how PW works with forms and inputfields you can simply add you own validation, do hooks and lots of magic with very easy code to read and maintain. You can also use the processInput($input->post) method of a form that PW uses itself to validate a form. So getting to see if there was any errors is simply checking for $form->getErrors();. Also the $form->processInput($input->post) will prevent CSRF attacks and the form will append a hidden field automaticly. It's also worth noting that processInput() will work also with an array (key=>value) of data it doesn't have to be the one from $input->post. Styling? It works well if you take your own CSS or just pick the inputfields.css from the templates-admin folder as a start. Also the CSS file from the wire/modules/InputfieldRadios module can be helpful to add. And that's it. It's not very hard to get it display nicely. Here an code example of a simple form. <?php $out = ''; // create a new form field (also field wrapper) $form = $modules->get("InputfieldForm"); $form->action = "./"; $form->method = "post"; $form->attr("id+name",'subscribe-form'); // create a text input $field = $modules->get("InputfieldText"); $field->label = "Name"; $field->attr('id+name','name'); $field->required = 1; $form->append($field); // append the field to the form // create email field $field = $modules->get("InputfieldEmail"); $field->label = "E-Mail"; $field->attr('id+name','email'); $field->required = 1; $form->append($field); // append the field // you get the idea $field = $modules->get("InputfieldPassword"); $field->label = "Passwort"; $field->attr("id+name","pass"); $field->required = 1; $form->append($field); // oh a submit button! $submit = $modules->get("InputfieldSubmit"); $submit->attr("value","Subscribe"); $submit->attr("id+name","submit"); $form->append($submit); // form was submitted so we process the form if($input->post->submit) { // user submitted the form, process it and check for errors $form->processInput($input->post); // here is a good point for extra/custom validation and manipulate fields $email = $form->get("email"); if($email && (strpos($email->value,'@hotmail') !== FALSE)){ // attach an error to the field // and it will get displayed along the field $email->error("Sorry we don't accept hotmail addresses for now."); } if($form->getErrors()) { // the form is processed and populated // but contains errors $out .= $form->render(); } else { // do with the form what you like, create and save it as page // or send emails. to get the values you can use // $email = $form->get("email")->value; // $name = $form->get("name")->value; // $pass = $form->get("pass")->value; // // to sanitize input // $name = $sanitizer->text($input->post->name); // $email = $sanitizer->email($form->get("email")->value); $out .= "<p>Thanks! Your submission was successful."; } } else { // render out form without processing $out .= $form->render(); } include("./head.inc"); echo $out; include("./foot.inc"); Here the code snippet as gist github: https://gist.github.com/4027908 Maybe there's something I'm not aware of yet, so if there something to still care about just let me know. Maybe some example of hooks could be appended here too. Thanks Edit March 2017: This code still works in PW2.8 and PW3.
  39. 15 points
    This is updated/version of the AdminDocsTab module as was posted here: https://processwire.com/talk/topic/11803-admindocstab/ (that one is now obsolete) https://github.com/outflux3/AdminHelp AdminHelp module for ProcessWire Processwire helper modules for managing site documentation. Currently alpha state - please use with caution and report all errors. Instructions There are 3 modules included with this: the master module holds the settings for the other 2. This is a helper module which expects you to have already setup a hidden part of your page tree* to establish your help documentation, using any template (e.g. 'help-doc') and a body field (ckeditor) (*or you can run the setup and it will create these items). The help-doc template does not need to have an output template, as the module will only echo the body field inside the admin. In addition this 'help-doc' template requires "template select" field (separate module) which should be named template_select. (if you run setup it will create this). To have a global help page, which renders all of the help docs in an accordion view, you can install the ProcessAdminHelp module, which will setup a page to view the help docs, under setup. Help Setup Module (AdminHelp) This is how the setup module looks before setup is completed: If you use the automated setup, it will create the field, templates and pages, and will auto-set the module configuration: Example Help Tab (when using AdminHelpTab) Admin Help page (ProcessAdminHelp) This shows all help docs in accordion. (it is capable of displaying child pages also but this is not currently implemented in the module due to the family settings of the default templates). Accordion opened: Features Summary: Manages settings for the help templates (help-index, and help-doc), which enabled users to add new help docs where applicable; these can already exist and be named whatever, you just specify them in the setting. Getting the help/docs tab to show up in the right place (based on user preference template selected on the help doc). Has it's own scoped CSS styling that makes the documentation readable and engaging; has some @import fonts, and also rules to make text layout look correct in PW admin (paragraphs, lists, headings, blockquotes etc.) Makes it easy for site editors to add their notes, mods/edits/enhancements to the docs (edit button - currently only enabled for Superadmin - this can be made a setting based on user feedback). Using the secondary process module will create a 'Help Docs' page under Setup where you can view all of the docs in 1 place as an accordion. (could be moved somewhere else) There is also an automated setup that can run, where it will create the field, templates, and pages for you to get started. This module is probably optimized to handle no more than 10-15 or so help pages; if you needed more than that, the Process module may need to be changed to work differently. Most sites I do need around 4-5 help pages. *If you don't want to load those extra google fonts in your admin you can modify the CSS to your needs, e.g. remove the @import and then change the few lines of css that reference those; Once there have been a few testers, I can see about adding this to the modules directory in a week or so.
  40. 15 points
    Presentation Originaly developped by Jeff Starr, Blackhole is a security plugin which trap bad bots, crawlers and spiders in a virtual black hole. Once the bots (or any virtual user!) visit the black hole page, they are blocked and denied access for your entire site. This helps to keep nonsense spammers, scrapers, scanners, and other malicious hacking tools away from your site, so you can save precious server resources and bandwith for your good visitors. How It Works You add a rule to your robots.txt that instructs bots to stay away. Good bots will obey the rule, but bad bots will ignore it and follow the link... right into the black hole trap. Once trapped, bad bots are blocked and denied access to your entire site. The main benefits of Blackhole include: Bots have one chance to obey your site’s robots.txt rules. Failure to comply results in immediate banishment. Features Disable Blackhole for logged in users Optionally redirect all logged-in users Send alert email message Customize email message Choose a custom warning message for bad bots Show a WHOIS Lookup informations Choose a custom blocked message for bad bots Choose a custom HTTP Status Code for blocked bots Choose which bots are whitelisted or not Instructions Install the module Create a new page and assign to this page the template "blackhole" Create a new template file "blackhole.php" and call the module $modules->get('Blackhole')->blackhole(); Add the rule to your robot.txt Call the module from your home.php template $modules->get('Blackhole')->blackhole(); Bye bye bad bots! Downloads https://github.com/flydev-fr/Blackhole http://modules.processwire.com/modules/blackhole/ Screen Enjoy
  41. 15 points
    I've played around a bit with setting up a blog in PW using @kongondo's blog module and, since I'm anything but not a designer, incorporated the free "Striped" template from HTML5UP. The result is Striped Travel Blog Template (Responsive). Update: link now points to the official module repository. The layout is classical (some might say "very nineties" ) but I do like sidebars on the left and content starting close to the top. Features are: You can the sort order for lists that are sorted by date to ascending or descending. This is the "killer feature" I'm missing everywhere else, and as an avid reader of hiking blogs (mostly long distance, so long series of blog entries) I always hate when "next" buttons take me backwards (in time) and lists have to be read top down. Post lists are paginated Since it uses the blog module, comments and email notifications for them are a given Contact form using @justb3a's Simple Contact Form module Auto-generated sitemap.xml using @Pete's XML Sitemap module The usual stuff like recent comments, recent posts, recent tweets Calendar for the current month with links to dates with pages 3-column home layout with posts, configurable number of posts shown there "Sticky" option for posts to glue them to home RSS feed Already added all templates with translatable strings to site translation files Todos: Add post overview by year and month Allow paging through the calendar widget by month I'm a bit of two minds whether to show the full posts in lists or just summaries and still have to decide on that As written in the title, it is still in development, and I'm not completely happy with the lack of integration of my additional site-wide settings with the blog module's settings widget. I just thought I'd share it. Of course, I'd be happy about any feedback. Screenshots:
  42. 15 points
    Hello, in one of my current projects, we have a test and production stage with their own ProcessWire installations, and we regularly want to migrate template/field configuration to the next stage without migrating content as well. ProcessWire supports this via the import/export functionality in the admin GUI. However we (and some others) would like to do this as part of an automated process. There seem to have been some discussion on this topic and two existing modules that get pretty near our requirements: https://github.com/mindplay-dk/SystemMigrations/blob/master/SystemMigrations.module https://github.com/LostKobrakai/Migrations However, they try to solve more then we need and for mindplay-dk's module, development seems to have discontinued. At that point I decided to build a more simple module with reduced scope that basically just makes ProcessWire's import/export automation-ready. Thanks in advance for trying this out and any feedback! About this Module CAUTION: This module is incompatible with repeater fields and ProFields. The module enables you to transfer template and field configuration from one processwire installation to another in an automated way. Changes to templates or fields are exported to the file system immediately (so they can be added to source control together with changes in the template files) The import script is designed to run from the command line (so it can be invoked from your build/deployment tool). On invocation in import mode, a DB backup is created template/field changes from the persist directory are imported into the DB In restore mode, the import script can restore any of the DB backups previously saved How to Use Make sure the module is installed in the source and destination ProcessWire environments. After installation of the module, you should check if the module's default persistDirectory configuration setting fits your requirements. The module will automatically export all fields, fieldgroups, and templates to JSON files in the directory specified by that setting. Note that the same setting is used by the import script as well, so if you change it, make sure you change it in all affected ProcessWire environments. The JSON files can be transferred to the destination ProcessWire environment. Running the import script from the command line will import template and field data in the destination ProcessWire environment. Manual Installation Caution: Beta software - do not use in a production environment without prior testing and regular back-ups. Caution: This module is incompatible with repeater fields and ProFields. In case automatic installation does not work, get the code at https://github.com/jaromic/pw-autoexport-tf/ and follow the instructions in README.md for manual installation. Manual Uninstall Delete the following files and directories from your module directory: AutoExportTemplatesAndFields/ AutoExportTemplatesAndFields.module Download Install from the module directory, clone the repository via GitHub, or download ZIP.
  43. 15 points
    This week’s core version on the dev branch includes a lot of small fixes and improvements. We’ve also got a new multi-language URL field available this week as well. https://processwire.com/blog/posts/processwire-3.0.65-core-updates/
  44. 15 points
    Hi, I'm not sure if this can be useful or not. Check by your self. I have created a little module around a set of 42 svg icons. (nearly a copy from Windows 10 iconset). It is very rudimentary at the moment. One can define settings for colors and size of the svg variations. Variations will be created on demand in a central directory (assets/svgicons/) and cached for later usage. The settings can be passed as selectorstrings. You can use a template variable $icon. To output markup for a plain svg icon in an image tag, you need to call the name: echo $icon->img("name=lock"); echo $icon->img("name=unlock"); If you want use it as a rollover with hover state, you need to call the function markup: echo $icon->markup("name=attention"); This gives you markup with default settings. If you need a clickable link, just define it in the selector string: echo $icon->markup("name=attention, link=/about/"); You can set / change the default values from template like: $icon->setColors(array(0, 111, 187, 0.2), array(219, 17, 116, 1.0)); $icon->setSize(180); and / or you can use individual settings for each icon: echo $icon->markup("name=document, size=32, color1=#775599, color2=blue, link=/about/"); // HEX colors or named colors echo $icon->markup("name=idea, size=120, color1=255-255-0-0.3, color2=255-255-0-1, link=/about/"); // RGBA colors echo $icon->markup("name=home, size=20, color1=230-0-0, color2=230-0-0, link=/about/"); // RGB colors . For the rollover / hover markup there is a piece of css needed what can be outputted (only once). It can be fetched with or without the style-tags: echo $icon->css(); // with style tags echo $icon->css(true); // suppress the style tags . The functions that does not output markup return the module itself (the $icon object). So you can use them chained: // all functions that can be chained: $icon->emptyCache()->setSize(100)->setColors(array(0, 111, 187, 0.25), array(243, 79, 159)); // after such a (re)set call, you only need to pass a name to each icon call to get consistent results: echo $icon->markup("name=lock, link=/logout/"); echo $icon->markup("name=unlock, link=/login/"); // you can override single settings, ->mu() is a shortcut for ->markup() echo $icon->mu("name=trash, size=200"); echo $icon->mu("name=key, color2=red"); // ->presentation() print the whole collection with optionally individually specified size and colors: echo $icon->presentation(100, array(0, 111, 187, 0.25), array(243, 79, 159)); . . . The code is here in a gist: https://gist.github.com/horst-n/f6922f6fa228991fd686 . Download it as ZIP! . . A live demo is here: http://images.pw.nogajski.de/svg-icons/ . .
  45. 14 points
    WHY? This module was built to fill the gap between simple $pages->find() operations and complex SQL queries. The problem with $pages->find() is that it loads all pages into memory and that can be a problem when querying multiple thousands of pages. Even $pages->findMany() loads all pages into memory and therefore is a lot slower than regular SQL. The problem with SQL on the other hand is, that the queries are quite complex to build. All fields are separate tables, some repeatable fields use multiple rows for their content that belong to only one single page, you always need to check for the page status (which is not necessary on regular find() operations and therefore nobody is used to that). In short: It is far too much work to efficiently and easily get an array of data based on PW pages and fields and I need that a lot for my RockGrid module to build all kinds of tabular data. Basic Usage Docs & Download https://modules.processwire.com/modules/rock-finder/ https://github.com/BernhardBaumrock/RockFinder Changelog 180817, v1.0.6, support for joining multiple finders 180810, v1.0.5, basic support for options fields 180528, v1.0.4, add custom select statement option 180516, change sql query method, bump version to 1.0.0 180515, multilang bugfix 180513, beta release <180513, preview/discussion took place here: https://processwire.com/talk/topic/18983-rocksqlfinder-highly-efficient-and-flexible-sql-finder-module/
  46. 14 points
    Thanks to @Macrura for the idea behind this module. Page Field Info Adds information about options in Page Reference fields. Supports InputfieldSelect and inputfields that extend InputfieldSelect: InputfieldSelect InputfieldRadios InputfieldSelectMultiple InputfieldCheckboxes InputfieldAsmSelect Requires ProcessWire >= 3.0.61 and AdminThemeUikit. Screenshots Field config Example of changes to inputfield Example of info field filled out in Page Edit Installation Install the Page Field Info module. Configuration In the Input tab of the settings for a Page Reference field... Tick the "Add info tooltips to options" checkbox to enable tooltips for the options in the field. Tooltips are not possible for Select or AsmSelect inputfield types so for those types you would want to tick the next option. Tick the "Append info about selected options" checkbox to append information about the selected options to the bottom of the inputfield. If the Page Reference field is a "multiple pages" field then the info for each selected option will be prefixed with the option label (so the user will know what option each line of info relates to). In the "Info field" dropdown select a text field that will contain information about the page, to be used in the tooltips and appended info. Of course this field should be in the template(s) of the selectable pages for the Page Reference field. Hook In most cases the "Info field" will supply the text for the tooltips and appended info, but for advanced usages you can hookPageFieldInfo::getPageInfo() to return the text. For example: $wire->addHookAfter('PageFieldInfo::getPageInfo', function(HookEvent $event) { $page = $event->arguments(0); // The page $inputfield = $event->arguments(1); // InputfieldPage $field = $event->arguments(2); // The Page Reference field $info = $event->return; // Text from the info field, if any // Set some custom text as the $event->return... }); https://github.com/Toutouwai/PageFieldInfo https://modules.processwire.com/modules/page-field-info/
  47. 14 points
    Pages At Bottom Keeps selected pages at the bottom of their siblings. A "bottom page" will stay at the bottom even if it is drag-sorted to a different location or another page is drag-sorted below it (after Page List is refreshed the bottom page will still be at the bottom). Newly added sibling pages will not appear below a bottom page. The module also prevents the API methods $pages->sort() and $pages->insertAfter() from affecting the position of bottom pages. Note: the module only works when the sort setting for children on the parent page/template is "Manual drag-n-drop". Why? Because you want some pages to always be at the bottom of their siblings for one reason or another. And someone requested it. 🙂 Usage Install the Pages At Bottom module. Select one or more pages to keep at the bottom of their siblings. If you select more than one bottom page per parent then their sort order in the page list will be the same as the sort order in the module config. https://github.com/Toutouwai/PagesAtBottom https://modules.processwire.com/modules/pages-at-bottom/
  48. 14 points
    hello! I created a new site for my podcast Machines-ethics.net and thought I should create a version for anyone to use. With PW-podcast profile you can create one or more lists of pages with content and create one or more itunes RSS feeds to syndicate your podcast (as well as listening online). There is also a blog section and basic pages for anyother page you need. Check out the example site here: http://pw-podcast.nicegrp.com/ and the github here: https://github.com/benbyford/pw-podcast
  49. 14 points
    This module allows the default admin - and other users you specify - to login as any other user as defined in the module config. This could be useful for reproducing errors your users can see on websites or during testing, but I would suggest it is used wisely as there are some obvious snooping abilities in using this. Once installed, you can configure the module to be usable by various members of staff other than the default admin account, as well as define which roles or individual users you will be able to login as. After saving the config settings, you will find a page under the Setup menu in the admin called "Switch User". Selecting a user from the list checks via AJAX whether the user has admin access (this is a basic check for the page-edit permission as by default the admin homepage requires page-edit to load the page tree) and will either allow you to redirect to the homepage or back to the admin depending on that check. Download Here Version History 0.0.6 - Initial release
  50. 14 points
    This week we've got major upgrades to ProcessWire's selector engine, a great new version of Form Builder, and a few other core updates as well! https://processwire.com/blog/posts/processwire-3.0.13-selector-upgrades-and-new-form-builder-version/
  • Create New...