Leaderboard
Popular Content
Showing content with the highest reputation on 01/19/2016 in all areas
-
my beginners guide is now live on Tuts+: How to install and setup PW CMS http://webdesign.tutsplus.com/tutorials/how-to-install-and-setup-processwire-cms--cms-25509 I also saw that Francesco Schwarz post went live today: 4 Reasons to Choose ProcessWire as Your Next CMS http://code.tutsplus.com/articles/4-reasons-to-choose-processwire-as-your-next-cms--cms-2506212 points
-
http://code.tutsplus.com/articles/4-reasons-to-choose-processwire-as-your-next-cms--cms-25062 and http://webdesign.tutsplus.com/tutorials/how-to-install-and-setup-processwire-cms--cms-25509 Copied from tuts topic. Great day for pw exposure. Please share these on Twitter, Facebook etc!10 points
-
Just launched MWest Holdings: http://mwestholdings.com/ Designed by Durre Design. PW, Bootstrap, Sage Frontend Approach, Slick Carousel... Lots and lots of carousels! The main "collections" on the site include: Properties Case Studies Staff News Articles Press Releases Blog Articles Going to /properties/ defaults you to /properties/residential/ca The two segments after /properties/ are URL segments which are used to bring up the appropriate properties. The primary navigation is hardcoded given the different types of links (some are regular links, some are anchors, etc.). I find myself hardcoding most navigation menus these days given the specificity and unique needs of each site. The navigation rarely changes however so that hasn't been a problem in terms of ability for a client to edit it. More details on my personal website: https://jonathanlahijani.com/projects/mwest-holdings/ Jonathan8 points
-
I have always seen processwire terminology like the OOP way Template = Class Field = Property Page = Instance Also everything is a page is a wonderful concept that saves me a lot of engineering efforts when creating websites8 points
-
Hi iNoize, Checkout this example used by Ryan to view tags in his blog profile: <?php /** * Generate a list of tags A-Z * * Used by the /site/templats/tags.php template file. * * VARIABLES: * ========== * $tags PageArray of tags * * Each of the tags has a numPosts property containing the number of posts used by the tag. * */ $lastLetter = ''; $out = ''; $letters = array(); foreach($tags as $tag) { $letter = strtoupper(substr($tag->title, 0, 1)); if($letter != $lastLetter) { if($lastLetter) $out .= "</ul>"; $out .= "<h3 id='letter_$letter'>$letter</h3>"; $out .= "<ul class='tags posts-group'>"; $letters[] = $letter; } $lastLetter = $letter; $numPosts = sprintf(_n('%d post', '%d posts', $tag->numPosts), $tag->numPosts); $out .= "<li><a href='{$tag->url}'>{$tag->title}</a> <span class='num-posts'>$numPosts</span></li>"; } $out .= "</ul>"; echo "<p class='jumplinks'>"; foreach($letters as $letter) { echo "<a href='./#letter_$letter'>$letter</a> "; } echo "</p>"; echo $out;5 points
-
Update: So I've been using Pete's dashboard module instead of the technique described above; it's quicker to setup, and easier to clone to other installs, and no need to have code in various places like the themes folder and in ready.php The hope/plan is to turn it into a full widget enabled dashboard module that i can install on various installations a) without having to do any hardcoding b) enable control over which widgets are shown on the dashboard (by user/role/permission) c) allow for easy configuration of colors, icons, columns, etc. To achieve this I setup some templates for different widget types, one is a shortcuts widget, another is a page lister widget; these each have pages and fields to configure them; i can set them to appear by user/role. in the future i can add different types of widgets as needed and then include them in the needed user's dashboard. This is using a combination of a free (MIT licensed) admin theme for bootstrap, but instead of using bootstrap as the css layout, it uses a tiny grid called rwdgrid which i s 2KB; it uses the box, and other widget classes from the admin theme.5 points
-
Have a look here. include=hidden $vorstand = $page->children("vorstand=1,include=hidden,sort=sort");4 points
-
Ok, so there's a problem with the selector. Standard kind of debugging process - try this: $vorstand = $page->children(); then $vorstand = $page->children("vorstand=1"); (You might not need sort=sort) Hang on, just thought of something! Take the space out of your selector. $vorstand = $page->children("vorstand=1,sort=sort"); You never know.4 points
-
I actually think “page” is a stroke of genius. It keeps the learning curve low for beginners by giving them a word they can immediately understand and work with. You can either keep working under the assumption that a page represents an HTML document, or dig deeper little by little. You’ll install a module like Language Support, and notice that – what do you know – languages are pages, until you arrive at “everything is a page”.4 points
-
Also take a look at http://oembed.com/ and http://modules.processwire.com/modules/textformatter-oembed/3 points
-
I was wondering about that actually - how the id of the page could match the category id Good question - it is possible, but you need to do things a little differently. Try the following in the custom selector option - not the custom PHP option: parent=page.category3 points
-
Processwire playing Aces in cms land I just noticed that with version 8 Drupal is waking up to decoupled. http://buytaert.net/the-future-of-decoupled-drupal Processwire has been playing it's decoupled front-end Ace since day 1. My opinion is that Processwire has a second decoupled Ace to play that needs more campaign: "everything is a page" To illustrate better what I mean is that my impression is that many processwire beginners pick up it's api and the decoupled front-end way of working but not the potential of "everything is page". An easy explanation for this is of course that many still associate a page with a webpage this established habit simply hinders to pick up the "everything is a page" potential of processwire. I have come across many examples of the "everything is a page" in the forum, here are two I could still recall, but there are many more amazing examples to find: 1. Multiple content areas with pages https://processwire.com/talk/topic/4487-homepage-with-multiple-content-areas/#entry44125 2. Making menus with pages https://processwire.com/talk/topic/11748-menu-building/#entry109226 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - In the Processwire about page (middle of the page) a general reference is made to "everything is a page" http://processwire.com/about/what/ But this general reference never made me pick it up until lately, and I am sure I am not the only one. The more I start working with this concept, the more I find it underestimated as a playing Ace for Processwire in cms land. We should collect all examples in the forum and select the best ones as a source for writing a new special article about the "everything is a page" and how to use it's potential in making your websites with processwire.3 points
-
Next and prev return a single Page, not a PageArray, so you can’t count them. See the docs for $page->next: https://processwire.com/api/variables/page/ »This page's next sibling page, or NullPage if it is the last sibling.« This is the same for all properties and methods that return only a single Page, like $pages->get(), $page->parent, $page->rootParent, etc. If nothing is found, they will give you a NullPage, which always has the id 0. So you can test for it using one of these conditions: if ($page->next->id === 0) { /*$page is the last of its siblings*/ } or if ($page->next instanceof NullPage) { /*same here*/ } Since 0 evaluates to false you can also go if (!$page->next->id) { /*see above*/ } if you’re into the whole brevity thing. Sorry, can’t format on mobile.3 points
-
Sometimes you have clients who will login to the admin, and they perhaps only need to access a few areas of the site, such as: a product or blog list (ListerPro) site settings formbuilder entries specific front end pages documentation helpdesk backup interface Server health or server status (for example site5 has a page for every server to see it's status) Link to a bookmark (page tree bookmark for example) - this is awesome by the way Run a special action like clear a wirecache or other custom caching Add a billing or late payment notice Add an alert about upcoming server maintenance The Problem: How can you collate all of these diverse links and messages into 1 page, as fast and easy as possible, make it hassle-free to add stuff to it, maybe some messages, change the order of the links etc. In some systems this is called the Dashboard. You can make one in a few minutes using: 1.) Admin Custom Pages 2.) ready.php in your site folder Steps: Install ACP create a file inside templates for your dashboard (e.g. _ac_dashboard.php). Create your dashboard page below the admin branch of the page tree, assign it to ACP, and set the template to use to the one you created. This example will display a table of quicklinks. Contents of the dasboard file: <?php wire('modules')->get('MarkupAdminDataTable'); ?> <h3>Title of site here</h3> <div id='ProcessFieldList'> <table id='AdminDataTable1' class='AdminDataTable AdminDataList AdminDataTableResponsive AdminDataTableSortable'> <thead> <tr> <th>Item</th> <th>Comment</th> </tr> </thead> <tbody> <tr> <td><a href='<?php echo $config->urls->admin?>blog/'>Blog</a></td> <td>Filterable/Searchable listing of blog posts.</td> </tr> <tr> <td><a href='<?php echo $config->urls->admin?>news/'>News</a></td> <td>Filterable/Searchable listing of news items.</td> </tr> <tr> <td><a href='<?php echo $config->urls->admin?>projects/'>Projects</a></td> <td>Filterable/Searchable listing of projects.</td> </tr> <tr> <td><a href='<?php echo $config->urls->admin?>page/'>Page Tree</a></td> <td>A hierarchical listing of the pages in your site.</td> </tr> <tr> <td><a href='<?php echo $config->urls->admin?>settings/'>Site Settings</a></td> <td>Global site settings</td> </tr> </tbody> </table> <script>AdminDataTable.initTable($('#AdminDataTable1'));</script> </div><!--/#ProcessFieldList--> You only need this if you want to redirect logins to the dashboard: add to ready.php (where 1234 is the ID of your dashboard page): if($page->template=="admin" && $page->id == 2) $session->redirect($pages->get(1234)->url);2 points
-
hi christophe, maybe this helps: put this in your /site/ready.php $wire->addHookAfter("ProcessPageEdit::buildForm", function(HookEvent $event){ $process = $event->object; $page = $process->getPage(); $user = wire('user'); // example: limit code to one template if($page->template != 'basic-page') return; // example: limit it to user role if(!$user->hasRole("membres")) return; $config = wire('config'); $config->scripts->append($config->urls->templates . 'scripts/yourscript.js'); $config->styles->append($config->urls->templates . 'styles/yourstyle.css'); }); of course you will have to put your script or style-files in the appropriate folders (in the example above it would be the folder /site/templates/scripts or /site/templates/styles2 points
-
2 points
-
Hmm, I think I tried this before … but don't know for sure. Anyway: $vorstand = $page->children(); echo $vorstand->count(); Doesn't change anything. Echoes "0". Ehm … the subpages are all hidden. Could this be the reason? Edit: YES! That IS the reason. Is there a way to list hidden pages? Edit 2: Yes, there is: use "include=hidden" as a selector.2 points
-
There is an article about the role of Page in the old Wiki. It is still a good starting point, even though it is a bit hard to understand this article without some actual experience with Pages: http://wiki.processwire.com/index.php/Pages_Overview What I would find useful is the addition of schematic diagrams that help beginners picture the concept. Trying to explain it by using only words resulted in this sort of convoluted article. A picture is worth a thousands words Not to mention lots of simple examples, that help explain different scenarios in which Page is used.2 points
-
2 points
-
Actually I hadn't noticed that - thanks for pointing it out. Sorry it's taken so long to get to, but the latest version of the module now has a new config setting to optionally exclude pages outside the restricted branch from the search results of pages. Please test and let me know if you find any problems.2 points
-
Hi folks So I actually just added this as a pull request on Github before figuring out it's already possible in the system. What I wanted was an ASMSelect field (can be a normal select or other Page field) where the list was ordered in a certain way and pulled from two sets of pages with different templates. The trick was that I wanted the labels for one set of pages to include the parent page name in the label so they're sorted by parent title, then title, but the other set of pages was just to be added to the end in alphabetical order. I've anonymised the example somewhat, but I have a medical blog where I wanted to categorise posts against medical procedures listed on the site, as well as other blog-specific, general categories. To achieve what I wanted, I selected ASMSelect as the Page Inputfield type, then used the "Custom PHP code to find selectable pages" option and did this: $options = new PageArray(); foreach ($pages->find("template=surgery, sort=parent.name, sort=name") as $category) { $category->title = $category->parent->title . ' / ' . $category->title; $options->add($category); } $options->add($pages->find("template=blog-category, sort=name")); return $options; This results in a nice, alphabetised list such as: Surgery Category A / Procedure 1 Surgery Category A / Procedure 2 Surgery Category A / Procedure 3 Surgery Category B / Procedure 4 Surgery Category C / Procedure 5 Surgery Category C / Procedure 6 General Category A General Category B General Category C and so on, so the surgery pages are in the format {parent.title} / {title} in the Select list, and the blog-category pages are just the normal {title}, all ordered alphabetically by parent (where necessary) and page title. Nice and easy1 point
-
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.zip1 point
-
Hello and happy new year to everybody This little module adds an unselect button to PageListSelect. You can define to which PageLists the button is added via module config (CSS selector). it solves this issue: https://processwire.com/talk/topic/9677-unselectclear-button-for-pagelistselect/ https://github.com/BernhardBaumrock/PageListSelectUnselectButton Changelog: v 2.0.0 - added support for ajax loaded fields including repeaters - added sanitization for the jquery selector1 point
-
Hello, I have been playing with PW for last 2 months. So far everything was really intuitive and straightforward. However, I get into some strange behaviour yesterday and I would like to let you know about it. I build website with 2 languages - default and english. All pages exist for default language, but only some of them are active for english. Let's say this is the structure: Home + About (default + english language) + Contact (default language only) Now I execute the following code: $user->language = $languages->get('default'); $children = $homepage->children; As you would expect, variable $children contains pages [about, contact]. Also if I execute... $user->language = $languages->get('english'); $children = $homepage->children; ...variable $children contains pages [about]. But here comes the trick. Let's execute this: $user->language = $languages->get('default'); $defaultChildren = $homepage->children; $user->language = $languages->get('english'); $englishChildren = $homepage->children; You would expect, that $defaultChildren == [about, contact] and $englishChildren == [about]. But that is NOT true. Both of them contain [about, contact]. The problem probably is, that first call to $homepage->children is cached somehow. If you call it second time, language change is ignored and the same output is given again. Workaround for me is to make second call little different, but semanticaly the same. For example: $user->language = $languages->get('default'); $defaultChildren = $homepage->children; $user->language = $languages->get('english'); $englishChildren = $homepage->children('parent=' . $homepage); And that is all Have a nice day. Martin1 point
-
In some cases it doesn't matter, and in some cases it does It depends on the scope and class context as to whether $this->user->name will work or not, but seems to have been common practice in various modules. With PW 3.x ramping up I would suggest reading this: https://processwire.com/blog/posts/processwire-2.6.21-upgrades-comments-more-on-pw-3.x/#more-updates-on-processwire-3.0 - scroll down a little to the "Multi-instance with 3rd party modules and PW 3.x" section where it explains what works where. Actually this is a good reminder for me to update my modules to use $this->wire()1 point
-
Yep, you could do. But I would recommend to use Javascript for that instead of CSS. When you check the javascript config checkbox, you can access the roles in your javascript file with: config.AdminCustomFiles.user.roles1 point
-
Depending on how you have the page field configured remember that you might need first(): $page->sideBarURL1->first()->url;1 point
-
It hasn't been updated for a while, but the author, felix, is still around. <edit> And just thinking out loud, it wouldn't be hard to make a text formatter to cURL a link and parse for Open Graph tags or Twitter Cards, which a lot of sites include.</edit>1 point
-
The hoster I use is 'Neostrada', a local hosting company. I've got wordpress, MODX, and codeigniter sites running. All without any installation problems. Running php version 5.6 Only caveat I can think of is the fact I don't have a domainname linked yet. So currently I'm working with server-ip-address/~user . Can this cause any problems? I am using the master.zip straight from the pw website, so yes the install.php files resides in the root dir. Also, the db exists and username/password are correct. EDIT: wow such response, much quick and thank you guys for being so welcoming1 point
-
Hi @Snd, Welcome to PW and the forums. Can you confirm you have install.php on your server? (assuming you FTP'ed new files for a new installation)...Otherwise, what @rick said... Edit: Beaten be @rick1 point
-
@Pete - thanks for noticing.. yeah there isn't really a 'blog' icon in fontawesome, so i just used that one..., and i also have some questions/comments about the dashboard module, but i'll do those over on the module thread.. @Mike - yeah, i should probably change to a rocket, but the idea of 're-routing' something seemed also to communicate what that does... any sort of looped arrow i guess would work...1 point
-
First, welcome to the forum. There are many ways to accomplish goals in ProcessWire, so I'm sure others will submit additional info, or a completely different solution. Probably the simplest method to start with would be to create a text field to hold the desired URL, and reference that page field in your code as the usage section describes on the github preview site. $preview_url = new LinkPreview( $your_page->your_field ); $parsed_url = $preview_url->getParsed(); etc... Also, that solution on that site is not the only solution available, as this functionality has been a fairly common request for a number of years. So you might want to search around for an option (e.g., jquery) that may better fit your needs.1 point
-
Firstly, try adding echo $vorstand->count(); after your first line. That will tell you if there is anything to loop through with your foreach().1 point
-
1 point
-
Hi @Asmordean, Welcome to the forums. Until yesterday, not it was not possible. Given your question (and previous requests), I have gone ahead and added an option to enable CKEditor in Quick Post (see demo below). This is currently in the dev branch only (version 2.3.6). To enable, follow these steps: Update Blog to version 2.3.6 from Dev branch (either download the zip or use url to pass to PW to upgrade the module) Scroll to the bottom of ProcessBlog's module configurations (via /modules/). Check the box to enable use of RTE in Quick Post. NOTE: This setting (unlike the others) is available both before and after installing Blog). I decided to keep it here so that only Superusers can control it Head over to /blog/posts/ and reload the page. You should see CKEditor loaded Note: If you are on an older ProcessWire install without CKEditor, you will not see the RTE, of course. You cannot insert images in your post using this option. To do that, edit your post in normal 'page-edit' mode Please test and let me know how it goes.1 point
-
Also comment this line: this.$timeObj[0].setSelectionRange(sPos, ePos); in jquery-ui-timepicker-addon.js to avoid focus bug (#1528).1 point
-
Nice looking site. Only critique is the load times for the front page movie and all the header and staff images. Maybe default the movie to a screenshot, and play it only on user request, rather than running it initially. I like the way the staff bios are presented.1 point
-
It for uikit lightbox component, resizing image and adding lightbox link automatically with overlay effect. If you don't use uikit you can remove these codes from function or you can customize them for your needs. I dind't modify my code, directly shared them with community and you can take it as example and you can modify codes or you can create a function as your needs. // Without lightbox $img = image($page->image->first(), $config->respImgOptions['villa']['list']); // With lightbox, its enought to send "true" or "overlay" class, after set responsive image settings $img = image($page->image->first(), $config->respImgOptions['villa']['list'], true); Lightbox example output : <figure class='uk-overlay uk-overlay-hover'> <picture> <!--[if IE 9]> <video style='display: none;'> <![endif]--> <source srcset='data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' data-srcset='/site/assets/files/1058/terra-nova-003.344x212.jpg' media='(max-width: 479px)' /> <source srcset='data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' data-srcset='/site/assets/files/1058/terra-nova-003.309x190.jpg' media='(max-width: 767px)' /> <source srcset='data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' data-srcset='/site/assets/files/1058/terra-nova-003.265x164.jpg' media='(max-width: 959px)' /> <!--[if IE 9]> </video> <![endif]--> <img src='data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' data-srcset='/site/assets/files/1058/terra-nova-003.290x180.jpg' alt='terra-nova-003' class="lazyload uk-responsive-width uk-overlay-spin" /> </picture> <div class='uk-overlay-panel uk-overlay-background uk-overlay-fade'></div> <div class='uk-overlay-panel uk-overlay-icon uk-overlay-fade'></div> <a class='uk-position-cover' href='/site/assets/files/1058/terra-nova-003.jpg' title='Terra Nova' data-uk-lightbox="{group:'terra-nova'}"> <span class='uk-hidden'>Terra Nova</span> </a> </figure>1 point
-
On my side i prepared a function for srcset and <picture> tag creation. Function output look like : <picture> <!--[if IE 9]> <video style='display: none;'> <![endif]--> <source srcset='data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' data-srcset='/site/assets/files/1058/terra-nova-001.404x270.jpg' media='(max-width: 479px)' /> <source srcset='data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' data-srcset='/site/assets/files/1058/terra-nova-001.692x462.jpg' media='(max-width: 767px)' /> <source srcset='data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' data-srcset='/site/assets/files/1058/terra-nova-001.427x285.jpg' media='(max-width: 959px)' /> <!--[if IE 9]> </video> <![endif]--> <img src='data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' data-srcset='/site/assets/files/1058/terra-nova-001.630x421.jpg' alt='terra-nova-001' class="lazyload uk-width-1-1" /> </picture> Here are my functions : /** /** * Generate Attributes Array or String * * @param $item * @return string */ function buildAttrs($item) { $attributes = ""; // echo "<pre>Key : " . print_r($item, true) . "</pre>"; if(array_key_exists('attributes', $item) && is_array($item['attributes'])) { foreach($item['attributes'] as $key => $attr) { $attributes .= ' ' . $key . '="' . $attr . '"'; } } return $attributes; } /** * * @param $image * @param $key * @param $options * @return string */ function buildSrcAndSrcset($image, $key, $options) { $return = ''; // echo "<pre>Key : {$key}, " . print_r($options, true) . "</pre>"; if(!empty($options[$key]) && !empty($options[$key]['sets']) && is_array($options[$key]['sets'])) { $x=1; $countSets = count($options[$key]['sets']); $sets = ""; foreach($options[$key]['sets'] as $k => $rules) { $y=$x++; $separator = ($y != $countSets) ? ", " : ""; if(!is_array($rules) && $rules == 'original') { $static_url = static_url($image->url); $sets .= "{$static_url} {$key}w{$separator}"; } elseif(isset($rules['width']) || isset($rules['height'])) { $size = imageResize($image, $rules); if(!is_null($size)) { $sets .= "{$size['url']} {$size['width']}w{$separator}"; } } } if($sets != "") $return = " {$key}='{$sets}'"; } elseif(!empty($options[$key]) && !empty($options[$key]['method']) && is_array($options[$key]['method'])) { $img = imageResize($image, $options[$key]['method']); if(!is_null($img)) $return = " {$key}='{$img['url']}'"; } elseif(!empty($options[$key]) && is_string($options[$key])) { $return = " {$key}='{$options[$key]}'"; } return $return; } /** * Image : Create <img /> tag with attributes and responsive lazyload option * * @param $image * @param array $options * @return string */ function image($image, $options = array(), $lightbox=FALSE) { $return = ""; // Alt attribute $alt = ($image->description != '') ? $image->description : pathinfo($image->filename, PATHINFO_FILENAME); $alt = " alt='{$alt}'"; if(array_key_exists('picture', $options) && is_array($options['picture'])) { $picture = $options['picture']; // Set Attributes $attributes = ""; if(array_key_exists('attributes', $picture)) { $attributes = buildAttributes($picture['attributes']); } $return .= "\n<picture{$attributes}>"; if(array_key_exists('source', $options['picture']) && is_array($options['picture']['source'])) { $return .= "\n\t<!--[if IE 9]><video style='display: none;'><![endif]-->"; $sources = $options['picture']['source']; foreach($sources as $key => $source) { $attrSrc = buildSrcAndSrcset($image, 'src', $source); $attrSrcset = buildSrcAndSrcset($image, 'srcset', $source); $attrDataSrcset = buildSrcAndSrcset($image, 'data-srcset', $source); $attributes = buildAttrs($source); $attrMedia = " media='{$key}'"; $return .= "\n\t\t<source{$attrSrc}{$attrSrcset}{$attrDataSrcset}{$attributes}{$attrMedia} />"; } $return .= "\n\t<!--[if IE 9]></video><![endif]-->"; } if(array_key_exists('img', $options['picture']) && is_array($options['picture']['img'])) { $img = $options['picture']['img']; $attrSrc = buildSrcAndSrcset($image, 'src', $img); $attrSrcset = buildSrcAndSrcset($image, 'srcset', $img); $attrDataSrcset = buildSrcAndSrcset($image, 'data-srcset', $img); $attributes = buildAttrs($img); $return .= "\n\t<img{$attrSrc}{$attrSrcset}{$attrDataSrcset}{$alt}{$attributes} />"; } $return .= "\n</picture>"; } elseif(array_key_exists('img', $options) && is_array($options['img'])) { $img = $options['img']; $attrSrc = buildSrcAndSrcset($image, 'src', $img); $attrSrcset = buildSrcAndSrcset($image, 'srcset', $img); $attrDataSrcset = buildSrcAndSrcset($image, 'data-srcset', $img); $attributes = buildAttrs($img); $return .= "\n<img{$attrSrc}{$attrSrcset}{$attrDataSrcset}{$alt}{$attributes} />"; } else { $src = " src='" . static_url($image->url) . "'"; $width = " width='{$image->width}'"; $height = " height='{$image->height}'"; // Set Attributes $attributes = ""; if(array_key_exists('attributes', $options)) { $attributes = buildAttributes($options['attributes']); } $return .= "\n<img{$src}{$width}{$height}{$alt}{$attributes} />"; } if(isset($lightbox) && $lightbox != FALSE) { $page = wire('page'); $title = ($image->description != "") ? $image->description : $page->title; $overlayEffect = (!is_bool($lightbox) && $lightbox != '') ? " " . $lightbox : " uk-overlay-fade"; $return = "\n<figure class='uk-overlay uk-overlay-hover'> \n\t{$return} \n\t<div class='uk-overlay-panel uk-overlay-background{$overlayEffect}'></div> \n\t<div class='uk-overlay-panel uk-overlay-icon{$overlayEffect}'></div> \n\t<a class='uk-position-cover' href='{$image->url}' title='{$title}' data-uk-lightbox=\"{group:'{$page->name}'}\"><span class='uk-hidden'>{$title}</span></a> \n</figure>"; } return $return; } /** * Resize Image * * @param $image * @param array $method * @return array|null */ function imageResize($image, $method=array()) { $alt = ($image->description != '') ? $image->description : pathinfo($image->filename, PATHINFO_FILENAME); if(isset($method['type']) && isset($method['width']) || isset($method['height'])) { // Set Resize Options if(isset($method['options'])) $options = $method['options']; else $options = wire('config')->imageSizerOptions; if($method['type'] === 'size' && isset($method['width']) && isset($method['height'])) { $size = $image->size($method['width'], $method['height'], $options); } elseif($method['type'] === 'width' && isset($method['width'])) { $size = $image->width($method['width'], $options); } elseif($method['type'] === 'height' && isset($method['height'])) { $size = $image->height($method['height'], $options); } else { $size = null; } if(!is_null($size)) { return array( 'url' => static_url($size->url), 'width' => $size->width, 'height' => $size->height, 'alt' => $alt ); } } elseif(is_null($method)) { return array( 'url' => static_url($image->url), 'width' => $image->width, 'height' => $image->height, 'alt' => $alt ); } return null; } /** * Return url with static url * * @param string $url * @return string */ function static_url($url="") { $config = wire('config'); if($config->debug === false && isset($config->static_url) && $config->static_url != "") { $static_url = $config->static_url; } else { $static_url = ""; } return $static_url . $url; } I set responsive sizes on my config file : /** * Responsive Image Options */ $responsiveSizes = array( 'small' => '480', 'medium' => '768', 'large' => '960', 'xlarge' => '1220' ); $config->resSmall = "(max-width: " . ($responsiveSizes['small']-1) . "px)"; $config->resMedium = "(max-width: " . ($responsiveSizes['medium']-1) . "px)"; $config->resLarge = "(max-width: " . ($responsiveSizes['large']-1) . "px)"; $config->resXlarge = "(min-width: {$responsiveSizes['xlarge']}px)"; And I created a file (including this file inside my init.php file) that file have responsive image sizes like : /** * Template --Villa List-- */ $respImgOptions['villa']['list'] = array( 'picture' => array( 'source' => array( $config->resSmall => array( 'srcset' => 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==', 'data-srcset' => array( 'method' => array( 'type' => 'size', 'width' => 404, 'height' => 270 ) ) ), $config->resMedium => array( 'srcset' => 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==', 'data-srcset' => array( 'method' => array( 'type' => 'size', 'width' => 692, 'height' => 462 ) ) ), $config->resLarge => array( 'srcset' => 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==', 'data-srcset' => array( 'method' => array( 'type' => 'size', 'width' => 427, 'height' => 285 ) ) ) ), 'img' => array( 'src' => 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==', 'attributes' => array( 'class' => 'lazyload uk-width-1-1' ), 'data-srcset' => array( 'method' => array( 'type' => 'size', 'width' => 630, 'height' => 421 ) ) ) ) ); And after all done calling images from page like : // on here you need to send single PageImage ! $img = image($page->image->first(), $config->respImgOptions['villa']['list']); Like this usage if you have https://github.com/aFarkas/lazysizes and https://github.com/aFarkas/lazysizes/tree/gh-pages/plugins/respimg every thing will work well !1 point
-
I made a module for this sometime ago https://github.com/ocorreiododiogo/ProcessHomeAdmin Should still work.1 point
-
Thanks for catching this. The error there meant you had some menu pages in the trash. On uninstall, the module was trying to delete their template, hence the error. Fixed in the latest version, 0.1.3.1 point
-
Thats true... as it should. when using: if ($childpages) you ask: If PageArray... which is always true. You should check if it contains pages with count for example. if (!count($childpages)) { // The pageArray doesn't contain pages // If not count pages, thus 0 } if (count($childpages)) { // yay we have at least 1 page } // or if ($childpages->count()) { // yay we have at least 1 page }1 point
-
Video clip showing latest development...(note: previews of other types other than images are still a work in progress...)1 point
-
1 point
-
Hey guys, this upgrade now appears on PW 3.0 (devns) branch. If you are using the ~= operator in a search, like "title~=disability act" it now asks MySQL for the ft_min_word_len (per @LostKobrakai's suggestion above). If any word length falls under that threshold, it delegates that word to a separate REGEXP command in MySQL that matches whole words. This is actually an improvement over the code I mentioned earlier because the previous code that converts it to use a "%=" for short words could match things like "disability fact" or "react to this disability" etc. Whereas the new core addition will only match whole words in the same way that the MySQL index would, meaning the field would only match if it contained the exact words "disability" AND "act" somewhere in the field, and not some word that happens to contain the partial word "act". To take it further, it also now uses this technique to match stopwords that MySQL doesn't full-text index. Previously it would just exclude them from the search since the presence of a stop word would prevent a search from matching. Now you can perform searches that include those stop words and match them.1 point
-
For fields you can get stuff like names, id, labels and such but not content values. For that you need to get a page by id, path or selector. Something like this: echo $pages->get('/')->home_tekst_8;1 point
-
Lately there have been lots of people that are not enjoying the default admin theme, so we've been working on making something new. Not trying to solve all issues or add every new feature we'd like, but just trying to come up with something to have as an interim replacement for the default admin theme until we can afford the time to do something broader in scope (like Phillip Reiner's great admin theme design for example). So this theme doesn't necessarily break a lot of new ground, but hopefully has some of the improvements that people are looking for. Visually, the goal here was to find a lighter, more modern look and reduce the boxes-in-boxes feel of the current admin theme. I've opted to commit it to the dev branch because it requires the latest version of ProcessWire on the dev branch, and likely will continue to with updates. Meaning, I can't distribute this one as a 3rd party theme very easily. This is because I'm making core updates related to the admin theme at the same time. So if you want to help test this new theme, you'll need to grab the dev branch of ProcessWire. The new admin theme is in /site-default/templates-admin/. That means you'll see it as the default theme if you are installing a new copy of PW. But if upgrading an existing copy, you'll continue to get the old theme. If you want the new theme, then just copy the /site-default/templates-admin/ directory to your /site/templates-admin/ directory on your existing PW install. This would be in addition to replacing your /wire/ directory with the latest one from dev, as usual for PW upgrades. The existing default admin theme also remains in place in /wire/templates-admin/. So if you want to stick with the existing/stable admin theme, then just make sure you don't have a /site/templates-admin/ dir in place (unless you are using a 3rd party admin theme). This admin theme is probably not production ready, as it's not been tested in many browsers yet. I personally haven't yet tested it in anything but Chrome and Firefox in OS X. Please let me know if you experience issues in other browsers. I fully expect things won't be pretty in IE... but they never are. To start, this comes with 3 color schemes (though we'll be adding more too): Warm: Modern (similar to processwire.com site colors): Classic (similar to existing admin theme colors): To switch to one color theme or the other, specify a GET variable of 'colors' in any URL you are accessing in the admin: /processwire/?colors=warm /processwire/?colors=modern /processwire/?colors=classic To specify a default, edit your /site/config.php and set to one of the following: $config->adminThemeColors = 'warm'; $config->adminThemeColors = 'modern'; $config->adminThemeColors = 'classic'; We'll probably make this switchable in the user profile at some point, but that comes later. This theme also comes with some new features (most of which have been copied/inspired from the work of others here, whether in other admin themes, modules or designs): It now uses Font-Awesome icons rather than jQuery UI icons. We now only use jQuery UI icons for actual jQuery UI widgets. Note that asmSelect and our file/image inputfields are built as jQuery UI widgets, but I don't think anything else is. Basically, the majority of icons in the system are now Font-Awesome icons. You can associate Font Awesome icons with templates. When associated with a template, the icons will appear in the page list, in front of the page title. To use this, edit any template and go to the Advanced tab. In the "List of fields to show in admin page list", you can type in any Font Awesome icon name (in addition to the field names you could before). For example, in the Page List screenshots above, I have my "search" template configured with the value: "icon-search title". You can associate Font Awesome icons with fields. When associated with a field, the icon will appear at the beginning of the input. For example, I associated a coffee icon with my "title" field in the screenshot below. To do this, edit the field, click on the Advanced tab, and enter the icon name in the new field provided for it. The top navigation now supports simple dropdowns. A new "user" dropdown has also been added that contains profile and logout links. The main Pages screen contains an "add new..." button, that is itself a dropdown to a list of templates that are configured adequately for us to know where they would be added. To use this, you have to configure your template "family" settings. The search box is now ajax powered, though this was introduced a couple weeks ago in the existing admin theme too. The theme is responsive too, of course. This is all kind of preliminary and open to changes. I'm trying to keep the scope of the project fairly small since I don't have much time to work on it. But just wanted something to quiet the haters a bit for a little while, so that we can take our time on the bigger admin theme project for longer term. We'd appreciate your feedback if anyone has time to help test.1 point
-
I updated the install script. It is now interactive and prompts for which branch and which site profile you want to use. See it in action here: https://vid.me/gDwZ You can download the unified install script on GitHub. https://github.com/plauclair/pwscripts1 point
-
I ended up with this code, extract from the session db handler module, modified excluding guest users and focusing just on count: <? function onlineUsers(){ $mins = 1; $table = SessionHandlerDB::dbTableName; $sql = "SELECT COUNT(*) FROM $table " . "WHERE ts > '" . date('Y-m-d H:i:s', (time() - ($mins * 60))) . "' " . "AND user_id!=40 " . "ORDER BY ts DESC LIMIT 1000"; $result = wire('db')->query($sql); $row = $result->fetch_array(); return $row[0]; }1 point
-
404 pages serve an important purpose from an SEO standpoint. So if this is a public facing site, I'd recommend maintaining a proper 404 page rather than just redirecting to or displaying the homepage. You could always have your 404 page link to your homepage or even meta redirect to it after a few seconds.1 point