Jump to content

Leaderboard

Popular Content

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

  1. This week's version of ProcessWire adds several new panels that provide simpler and more direct access to the page tree, page view and debug tools. New page view options also include multi-language selection and configuration support. And if that's not enough, we've also got install-time utf8mb4 support and more! https://processwire.com/blog/posts/pw-3.0.15/
    16 points
  2. Hey guys. I'm submitting my latest project to the directory. http://www.sagatex.pt Sagatex represents fashion and sportswear brands in Portugal, such as Fred Perry, Diadora, Dr. Martens, etc. This site features a product catalogue and a store finder. I like keeping my ingredients to a minimum. Here, I used: Reno theme InputfieldRepeater InputfieldMapMarker MarkupSEO ImportPagesCSV Enjoy
    6 points
  3. Hi @wsjmdavies, welcome to the forums. This seems to be not exactly determined what you've explained. But no problem, we can go together step by step to inspect what happens. Regarding uploaded (original) images and image variations: If you upload new images to an images field, the original uploaded files never get altered, with one exception. (I come to this later *) So, the original (uploaded) file regularly is unaltered, means has the exact filesize and contents as your local copy. Only the filename will get altered to match the criterias for asset filenames in PW. (all lowercase, 0-9, a-z, _-, no dots, etc) Image variations derives from the original uploaded source and are altered according to your request. (different dimensiones, optionally cropped, etc). A few part is reflected in a suffix that is appended to all image variations filename. (basename.jpg becomes basename.0x100.jpg or that like) --- How have you determined that the original file gots altered? What are your settings in the images field? Do you have any setting there in the fields for max-width or max-height? (* this would be the only exception where uploaded files can get altered!) You have uploaded an image that contains compressed image (bitmap) data. Photoshop is able to apply a (hopefully) lossless compression to the bitmap data before storing it into the image file. ImageRenderingEngines in webenvironments often have not included equal compression algorithm. SO they uncompress the bitmap data, alters it, and stores it uncompressed, or with much lower compression rate into a new file. (What explains why a new (variation) file has higher filesize) BTW: using compressed images as original sources for variations is a bad idea. It is not much relevant with PNGs, but highly with JPEGs: mostly you will not serve the originals, (or you shouldn't do), but generate and serve variations. to generate one, you need to open (and uncompress compressed bitmap data) of the original image (more CPU usage each time) with JPEGs, what do not have lossless compression, you multiply building of compression artefacts with each saving / compression of bitmap data Best practice is: upload (original) images with 100% quality, and / or without compression. Do not serve those originals directly! If you need to serve images with the full dimensions, create a variation with 100% width/height, but lesser quality (e.g. 80% - 90%) $original = $page->images->first(); $options = array('quality' => 80, 'forceNew' => true); // forceNew is recommended for testing purposes only!! $variation = $original->width($original->width, $options); // create an identical variation with lesser quality and filesize
    6 points
  4. ClearCacheAdmin Module Since there's so many asking how to clear cache. Here comes a helper module that let's you clear caches directly from the admin menu. Additionally it has its own admin page that's a collection of the various caches available in ProcessWire in one place. See some infos and clear them. WireCache (using $cache) caches in the DB and there no way to clear it from the admin. Now you can delete the entries individually or all in one go. Supports following cache types: - Template "Page" Cache (disk file cache) - MarkupCache module (file cache) - WireCache (DB) - Other files and directories found in assets/cache/ path Wanted to do this for a long time. It was also great fun to do it and give something to the community to also learn something. For now it's on github only. Requires ProcessWire 2.6+. https://github.com/somatonic/ClearCacheAdmin
    5 points
  5. Hi all, here's my first official module. I created it for a project I'm working on, so I thought I'd release it in case anyone else finds it useful. If you have any suggestions or find any issues with it etc, please post them here. ​ NOTE: I have only tested it on PW 3.0.3, so you might want to try it on a test installation first if you decide to use it. ​Lumberjack Logger Module A simple module for ProcessWire CMS that logs the IP / User Agent details of the user when the page is saved. Installation Copy the Lumberjack folder to /site/modules and then refresh the modules from Admin->Modules. Lumberjack can then be installed. Usage Two new fields will be created when Lumberjack is installed. lumb_ip_log - This field is used to store the IP lumb_ua_log - This field is used to store the User Agent String You can add both fields or just one to the required templates. Pages using those templates will then automatically store the IP and User Agent of the user when the page is saved. Settings Lumberjack can be disabled by unchecking the Enabled option on the settings page. ​ Screenshot ​ Download ​ Link https://github.com/GuruMeditation02/Lumberjack Updates Version 0.1.1 - Added User Agent String santization
    5 points
  6. This module is deprecated in favor of RockMigrations. It'll continue to work and I might fix some smaller incompatibilities if they're reported, but no major development will happen on this anymore. There where various threads about a how to reasonably handle multiple dev/staging and live environments with ProcessWire and at best handle it as automatically as possible. A git based workflow makes it easy to handle files, but the pain point of migrating db changes has even lead to multiple requests of not handling template/field storage in the db at all. I've gone ahead and used for my own projects the concept of database migrations, which most frameworks are using to handle database changes – but the ProcessWire flavored way. ___ ___ ___ ___ ___ ___ ___ /\__\ /\ \ /\ \ /\ \ /\ \ /\ \ /\ \ /::L_L_ _\:\ \ /::\ \ /::\ \ /::\ \ \:\ \ /::\ \ /:/L:\__\ /\/::\__\ /:/\:\__\ /::\:\__\ /::\:\__\ /::\__\ /::\:\__\ \/_/:/ / \::/\/__/ \:\:\/__/ \;:::/ / \/\::/ / /:/\/__/ \:\:\/ / /:/ / \:\__\ \::/ / |:\/__/ /:/ / \/__/ \:\/ / \/__/ \/__/ \/__/ \|__| \/__/ \/__/ Beta This module does help you managing migration files, where any database changes can be stored in php files using just the simple ProcessWire api at your disposal. It's not as nice as using the admin UI, but certainly better than trying to migrate changes manually and possibly weeks after adding the changes. Also there's always the option to create helper modules, which can export changes made in the admin UI to something usable in those migration files. For example I'm using an internal process module, which does let me export changes to template access rules as json strings, which I then use in my migrations to actually apply the changes. Now on to the meat – How to use the module: Read the following guide on creating own migrations Maybe use the CLI tool to speed up your workflow (and possibly automate stuff) It is generally recommended, but not enforced, that migrations are run/rolled back in order. When doing migrations or rollbacks, without specifying a migration, this module will stick to the order. Creating Migrations Your migrations will probably hold lot's of code, which does delete data. By now this module does not have any security measurements to prevent that. Be sure to test your migrations locally and possibly keep a database backup before running them. There are currently four types of migrations: default (Migration) Default migrations are the most free form migrations. There's just a description and two functions – update() and downgrade(). What you're doing in those functions is totally up to you, but it's recommended to try the best to keep changes as reversible as possible. Meaning that running update() and downgrade() once should have as less effect on the installation as possible. The ProcessWire API is available exactly like in modules using the $this->pages, $this->config, … syntax. FieldMigration TemplateMigration ModuleMigration All of those are there to make your life easier. They all have different but similar functions – which you can find in migrations created by this module – which ease the creation and removal of fields, templates or modules. All the boilerplate is handled by the base classes these migrations do extend, so you don't even need to think about update() and downgrade(). You can rather just describe the item you want to handle and the creation / removal process is taken care of. These are by now not highly tested, so please again be sure to test migrations before running them on important content. Command-Line Interface The module does include a cli interface, which does allow the migrations to be run automatically by CI or deployment scripts or just by you if you like cli's. The cli script is located in the bin directory inside the module's folder. It does however require a composer package to work, which you can simply add by running composer require league/climate in your site directory (or the root directory for pw 3.0). Make sure to require composers autoload.php in your config.php for 2.x installations. The CLI does have a quite handy help page, which you get by running php migrate -h so I'm just adding the important bits of that here: > php migrate -h […] Usage: migrate [-h, --help] [-i info, --info info] [-m migrate, --migrate migrate] [-n new, --new new] [-nf newField, --newField newField] [-nm newModule, --newModule newModule] [-nt newTemplate, --newTemplate newTemplate] [-r rollback, --rollback rollback] Optional Arguments: -m migrate, --migrate migrate Run a specific migration or all new* ones if none given. * From latest migrated to newest. -r rollback, --rollback rollback Undo a specific migration or the latest one if none given. -n new, --new new Bootstrap a new migrations file. Optionally you can already supply a description. -nt newTemplate, --newTemplate newTemplate Bootstrap a new template migrations file. Optionally you can already supply a description. -nm newModule, --newModule newModule Bootstrap a new module migrations file. Optionally you can already supply a description. -nf newField, --newField newField Bootstrap a new field migrations file. Optionally you can already supply a description. -i info, --info info Get detailed info about a migration. -h, --help Show all commands of the cli tool. Link the migrations cli update save to ProcessWire's root: https://processwire.com/talk/topic/13045-migrations/?p=118329 Helper Functions There are already a handful of helper function included in the Migration base class, which tackle things I found to need way to much boilerplate for kinda simple changes, but you can also add own custom helper functions via hooks. /** * This does use @diogo's while loop technique to loop over all pages * without getting memory exhaustion. */ $this->eachPageUncache("template=toBeHidden", function($p){ $p->setAndSave('status', Page::statusHidden); }); /** * $template, $field, $reference = null, $after = true * The below function reads like this: * In the template … add the field … relative to the field … in the position after/before */ $this->insertIntoTemplate('basic-page', 'images', 'body', false); /** * Edit field settings in context of a template */ $this->editInTemplateContext('basic-page', 'title', function($f, $template){ $f->label = 'Headline'; }); And a simple example of adding a custom helper as a hook. // in ready.php $wire->addHook('Migration::renameHome', function(HookEvent $event){ $name = $event->arguments(0); wire('pages')->get('/')->setAndSave('title', $name); }); // in the migration $this->renameHome('Root'); Snippets Still not sure how all this works in practice? Or you want to share a maybe more complex migration? Just head over to the Snippets Repo at Github. https://github.com/LostKobrakai/MigrationSnippets There are also less specific examples in the modules repository: https://github.com/LostKobrakai/Migrations/tree/master/migrations Appendix As long as the module is in Beta the helper functions in the Migration.php might be object to change/removal, so be aware of that. Download http://mods.pw/Bm https://github.com/LostKobrakai/Migrations
    3 points
  7. This might come in handy, it's still hot: https://processwire.com/talk/topic/13042-clearcacheadmin/
    3 points
  8. $sanitizer->int() or php natives (int) or intval() will do. Looking up in tools like API-gen, maintained (with everytime the newest codebase) by Kongondo, is much more faster than asking (such simply code/method related things) in the forums. I highly suggest to use this. It will save you a lot of time, that you otherwise have to wait until someone other answers your questions. --- EDIT: Oh damn, - @arjen beats me, while I made the screenshot!
    3 points
  9. How about: $pageID = $sanitizer->int($id); or $pageID = (int) $id;
    3 points
  10. Does TextFormatterHannaCode need to be so large? The bundled version of Ace contains all 4 versions of the Javascript files, but in ProcessHannaCode.module I can only see the src-min version being called in. Removing these and the demo directory would reduce the size of the module by around 38MB. Are there licensing issues which require that these files must be present?
    3 points
  11. Sure. I just wanted to get it out, but this can certainly being added. We're using Cronjob DB Backup on our project so it's not been so much a necessity for me. Also it wouldn't even need to check for anything as the backup functionality is part of the core. Only the admin UI must be installed.
    2 points
  12. Not at all. Yeah why not. But I originally just wanted to be able to trigger the various caches by simply using their own API. I wasn't doing any real operations or deletions just delegation. I now added support for "Other files and directories in the site/assets/cache/ dir". For now they will now show up in menu and on admin page and you can delete them recursively. Version 0.0.2 is commited.
    2 points
  13. Horst, thank you for explanation. One question: if you don't use $options array then $config->imageSizerOptions from /wire/config.php is used? So when you use just $img->width(xxx) then default quality of 90 will be used? And what options are used when you resize/crop the image in admin interface (in image UI)? For thumb generation, $config->adminThumbOptions is used, right?
    2 points
  14. It's not an issue in terms of security, but rather a (probably small) performance consideration. It just prevents an unnecessary mysql query in case a negative int is supplied.
    2 points
  15. what? please try: foreach(array(-5, -1, 0, 1, 5) as $id) { var_dump((bool)$id); }
    2 points
  16. I think currently this module is one option: https://processwire.com/talk/topic/7857-module-fieldtypetime-inputfieldtime/ or you just use a plain text field but init the field for the timepicker as described above by renobird; you can init the field using ACF and also specify to load the assets in the ACF settings...
    2 points
  17. Which, in plain vanilla PHP is $pageID = abs(intval($id));
    2 points
  18. Even better is $sanitizer->intUnsigned() as id's cannot be negative.
    2 points
  19. 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
    1 point
  20. Where do i get? From here: https://github.com/IDT-media/IntegerAutoIncrement What does this do? Adds additional option to Fieldtype Integer to make it auto increment. How to use? Simply install this module, Create / Modify Field that is Integer, set auto increment on/off. Enjoy!
    1 point
  21. Looks great, Benjamin, thank you! I have not tried this yet, but look forward to doing so. Could your module check if ProcessDatabaseBackups is installed? If it is perhaps it could take care of either backing up the DB or at least reminding the user to do so, before it performs a migration.
    1 point
  22. Whoah, this is going to be really useful, especially that wirecache cleaner(!)
    1 point
  23. Thanks a lot! Just one question: how about cache/FileCompiler too? Or better yet, any number of custom paths added by the superuser? Maybe I'm asking too much.
    1 point
  24. @GuruMeditation Thank you for the module - and welcome to the wonderful world of "official" module authorship!
    1 point
  25. The module core -> PageRender does have a button.
    1 point
  26. PHP 6 was never released as stable php version (and discontinued years ago) and 5.3.8 is also not the most recent version to run. I'd really doublecheck if that's the hoster to go.
    1 point
  27. Hmmm, PHP 6? Really? AFAIK not an officially supported version. (Google for more info.) You would probably be better served with 5.6 or maybe even 7. Anyhow, GD support is pretty much a requirement for most hosting. PW uses it to resize/crop images. The combination of PHP 6 and no GD throws real doubt on the competence of your hoster,
    1 point
  28. Ah yes right, negative evals to true. So then correcting mine: if($id > 0){...} But don't see why that would be an issue to have a negative page id it won't find it anyway.
    1 point
  29. Hi Horst, Wow that makes perfect sense to me! Quite new to process wire, so wasn't 100% sure how the image uploads worked....but now I will start uploading originals and creating variations as you mentioned. Thank you so much for you reply, much appreciated.
    1 point
  30. Usually like this. $id = (int) $input->post->id; $p = $pages->get("id=$id, template=dings"); if($p->id){ // valid } else { // not valid }
    1 point
  31. Hi adrianmark, would you be so kind to post your solution here for further reference?
    1 point
  32. The /src-min/snippets/ folder could also be slimmed down There's scripts in there for languages like ruby, python, etc.
    1 point
  33. To add to my post above. You'd need to use "sort=parent", but if that's the case it should only render the restaurant name once and all it's dishes below it. If that's not what you want then I don't understand your request.
    1 point
  34. $dishes = $pages->find("…, sort=parent, sort=XX"); $parentID = null; foreach($dishes as $dish) { if($parentID === null || $dish->parent->id !== $parentID){ $parentID = $dish->parent->id; // render restaurant headline } // render dish list item }
    1 point
  35. https://processwire.com/talk/topic/9400-how-to-get-all-unique-values-for-a-field/ Good discussion by LostKorakai.
    1 point
  36. I think the mysql variant would certainly be the most performant. Otherwise you'd need to load all pages holding that field into memory, which is probably not your intend.
    1 point
  37. If you don't need the $page / $field info I'd rather just use the format(&$str) function. Also I don't really see the need for preg_replace_callback(). preg_replace should be enough as it's already replacing all occurrences, as long as you don't tell it to do otherwise.
    1 point
  38. Two very good windows alternatives for quicktime: 1. smplayer http://smplayer.sourceforge.net/ 2. vlcplayer http://www.videolan.org/vlc/
    1 point
  39. Works like a charme. I have used expandable textareas on my frontend forms, but the possibility to use it also in the backend is very usefull!!!
    1 point
  40. hi cb, i think addhookafter "saveReady" should be fine. and just set the values, the save will take place afterwards: // change this $page->setAndSave("title", $page->page_quote->title); // to that $page->title = $page->page_quote->title;
    1 point
  41. At X-com we build and maintain ticketingsoftware, named Itix. About 20 Dutch Theatres run our software and since we've dropped maintentance on our own CMS, we're slightly migrating more and more of those theatres towards a ProcessWire powered website. Using various API's and synchronisation tools, all needed information and actions are provided to the ProcessWire frontend, resulting in a cool e-commerce solution which is flexible towards the client and scalable for us. Another one was released this week, go see the result at http://www.maaspoort.nl The design was done by http://www.dejongensvanboven.nl The technical implementation by us at http://www.x-com.nl Other Itix theatres running on ProcessWire include: http://www.schouwburgvenray.nl/ http://www.dnk.nl/ http://www.rabotheater.nl/ http://www.deleest.nl/ http://www.hof88.nl/ http://www.demeenthe.nl/ http://www.muziekgebouweindhoven.nl/
    1 point
  42. It seems that opengraph tags require "property" instead of "name" in tags: http://ogp.me/ Here is a quick fix (around line 330, added dynamic $attributeName): // add "render" $rendered = ''; foreach($pageData as $name => $content) { // set "property" as meta attribute name instead of "name" if it's an opengraph tag $attributeName = strpos($name, 'og:') !== false ? 'property' : 'name'; switch($name) { case 'custom': break; case 'title': if($this->titleFormat == '') break; $rendered .= '<title>'.$this->parseTitle($page, $content).'</title>'.PHP_EOL; break; case 'canonical': $rendered .= '<link rel="canonical" href="'.$content.'" />'.PHP_EOL; break; default: $rendered .= '<meta ' . $attributeName . '="'.$name.'" content="'.$content.'" />'.PHP_EOL; break; } } Also, it's a good practice to add og:image:width and og:image:height, this ensures the image is loaded on first facebook share (otherwise it's empty). Maybe the module could automatically add these too.
    1 point
  43. Hey, steady on, tiger, let's give it a try. I just installed the module on a fresh 2.7.2 install (Basic profile) and followed the instructions in the repo and everything installed beautifully. I also created rough root, forum and topic pages, just for testing. The first thing puzzling me is that I also added a new PW role 'member', expecting to give that role 'create topic' and suchlike privileges, but, to me, the config options in the backend aren't coming up where I'd expect them (at subforum level), but at topic level. And there's no sign of my 'member' role being offered the chance to do anything. Is there something I have done wrong? I would like to spend some time giving whatever help I can to help make this work.
    1 point
  44. @Pete, seeing some problems when visiting this module's settings page under PHP7. I had to tweak the code a little to get it working again. Here's what I did... // Now use $data and $fieldsModel loop to create all checkboxes foreach ($fieldsModel as $f=>$fM){ $creator = $fM['type']; $fields->add( self::$creator($f, $fM['label'], $data[$f], $fM['desc']) ); }
    1 point
  45. The idea is to have no dependencies, so a fresh install of PW would provide everything needed. Replies are stored as pages under a sub-page called "Comments". I've followed along the route of topics and comments so that comments (or replies) can also be used for other pages throughout a site. This image explains it more: The module installs all the required fields and templates expected for a basic forum, it's then up to the user in regards to the markup and how to sanitize the data etc. It doesn't really do much more than that, other than to obviously save the data through custom methods. I have some code as stated previously, but I'm afraid people will laugh at it, as I know a lot of people on here could easily do a better job.
    1 point
  46. I can definitely see the need for just being able to show the time picker. I have a custom inputfield for entering lists of hours that does this. The JS in the module customizes the picker to only show date — even though it's using the core datetimepicker.js $(document).on("focus", ".InputfieldHours .datepicker", function() { $(this).timepicker({ timeFormat: 'h:mm tt', ampm: true }); }); bah! Just noticed some alignment issues on my time picker in safari. :/
    1 point
  47. I prefer to bake them with integral flour and yellow sugar. My favorites have butter, almond or hazelnut, oat, cinnamon, and sometimes a bit of black pepper. I let them bake a lot until they get really crunchy. Hm, not sure if I ever tried those ingredients... are they tasty?
    1 point
  48. Joss actually emailed me a similar question and I'll duplicate my reply here since it seems relevant: Performance as it relates to database is really not an issue that one needs to consider much (or at all) when it comes to creating their fields. Most field data in ProcessWire is loaded on-demand. It loads data selectively and only when it needs it. This enables it to be highly memory efficient with large quantities of pages in memory at once. When you have a $page, behind the scenes, none of the page data is actually loaded until you access it. For instance, if you access $page->body, then it goes and retrieves it at that moment (if it hasn't already retrieved it before). MySQL is extremely fast with simple primary key, non-joined selects, and we take advantage of that. What I'm trying to get across is that quantity of fields does not translate to an increase in joins or other factors that would slow the system down. Where ProcessWire does join data automatically is at page load time is when you check the "autojoin" box on a Field's "advanced" tab. Some fields you know will always be needed with every $page instance, and that's what autojoin is for. Typically, I make my "title" field autojoin, as it is already by default. I've hidden that autojoin option under the Advanced tab simply because most people never need to consider it. The original intentions behind autojoin have become less applicable than I originally thought [with regards to performance], so it's not something that comes up that often. ProcessWire also uses joins when it performs queries for $pages->find("selector"), and related DB-querying selector functions. It joins all the tables for fields that you query. So if you perform a find for "date>2012-12-19, body*=holidays" then it's going to join the field_date and field_body tables when a value matches. Though it doesn't do this for the purpose of loading the data, only for matching the data. Technically this type of query could be potentially faster if all those fields were in one table. But that doesn't translate to results that matter for us, and doesn't affect the way that you should use ProcessWire. The benefits of our one-table-per-field architecture far outweigh any drawbacks. I put a lot of time into finding the right architecture and balance here when coding ProcessWire 2. Incidentally, ProcessWire 1 did use the one-table approach (all the field data was stored with the page rather than in separate tables) and it was far less efficient with memory, and about the same in terms of performance. It's better to re-use something like "body" (when possible) rather than create "article_maintext" or other template-coupled variations like that. The reasons for that are for your own benefit. It is less to keep track of, and tends to foster better consistency. It also results in more reusable code and broadens the potential of where the data can be used. Take the example of an on-site search engine, like you see in the upper right corner of processwire.com. If we know that the main text field(s) of most templates has some consistency in their field names (like title and body), then we can write code that doesn't need to know whether something is an article, a press release or a product. We can find all pages that match "holidays" in the text just by doing this: $pages->find("title|body*=holidays"); But if created a separate textarea field for every template, then any code that queries those fields needs to know a lot more about them: $pages->find("title|article_maintext|pr_maintext|product_maintext*=holidays"); While that still works, it doesn't scale nearly as well. This also translates to the code you use to output the results. If you want to show a snippet of the matching text with the search results, you are going to have a lot more fields to consider than just "body". Now if each of your templates genuinely needs very different settings for each of their main text fields, then of course it's fine to create them as you need them. But in the real world, I think you draw more benefit by planning for reusability when possible. The benefits are for you (the developer), as it doesn't matter much to ProcessWire. Reuse fields where it's obvious that the name of the field makes sense in the context of multiple templates. If template "employee" needs a date_of_birth field and template "press_release" needs a date_publish field then just create one field called date and use it on both templates. On the other hand, if you need multiple date fields on the same template (like date_unpublish) then more specific field names start to make sense. In that case, I would usually use my date field for publish date, and create a separate date_unpublish field for my unpublished date field. Though some may prefer to actually have separate date_publish and date_unpublish fields because they are obviously related by name. Ultimately, use what works best for you, but always keep an eye out for obvious reusability potential with fields. I think that most people naturally arrive at the right balance for their needs after some experimentation. What is a best practice for one need might not necessarily be for another. So these are mostly general purpose guidelines and people should find what makes the most sense in their context. For the majority of cases, I think avoiding tightly coupled template and field names is a better strategy. TL;DR: It doesn't matter to ProcessWire what you do. Aim to reuse fields when you can and when it makes sense, for your benefit.
    1 point
  49. This is the way to create template and fields with API: // new fieldgroup $fg = new Fieldgroup(); $fg->name = 'new-template'; $fg->add($this->fields->get('title')); // needed title field $fg->save(); // new template using the fieldgroup $t = new Template(); $t->name = 'new-template'; $t->fieldgroup = $fg; // add the fieldgroup $t->noChildren = 1; $t->save(); // add one more field, attributes depending on fieldtype $f = new Field(); // create new field object $f->type = $this->modules->get("FieldtypeFloat"); // get a field type $f->name = 'price'; $f->precision = 2; $f->label = 'Price of the product'; $f->save(); // save the field $fg->add($f); // add field to fieldgroup $fg->save(); // save fieldgroup All pretty much standard OO one can figure out looking at core and PW modules. But not someone unexperienced would figure out by themself. I think at some point we need to cover these in a documentation.
    1 point
×
×
  • Create New...