Leaderboard
Popular Content
Showing content with the highest reputation on 06/16/2016 in all areas
-
We will be performing a forum upgrade tomorrow if final testing goes well today. Why are we doing this? The current version is over a year old and the developers have since released a new major version - in fact they're now up to 4.1.x whilst we're still on the 3.x branch. They will not be supplying bug fixes or security patches for much longer. There are a lot of nice changes (and some confusing ones - more on that later) such as responsive design leading to a more complete mobile experience. We can also leverage some caching options to make things even faster when browsing the forums. What does this mean for you? Unfortunately there are a number of down sides to this: The forums will have to be taken offline twice for approx 20-30 minutes each time for several hours. During the upgrade process, the post content gets completely rebuilt and whilst this is in progress things look a mess, code samples look broken etc etc. We think it's better to take it offline than show broken code samples! We've decided to leave the forums open as the forums still technically work, new posts aren't affected, and we need the shop to stay online as well. There will be a learning curve with the new layout, but I'll do my best to post a "this is where to find stuff" post right here after the upgrade. Bizarrely the "best answer" feature has been replaced with a Stack Overflow-style voting feature which doesn't work everywhere we currently have the best answer feature turned on, so we will be making adjustments to some forums. You also can't make the first post in a Q&A forum topic the "chosen answer" which is a bit silly, but I'll be looking for a workaround for that. Possibly the biggest annoyance is that all unread posts you may currently have will get marked as read after the upgrade. Since this forum is a complete rewrite they've been unable (or possibly unwilling?) to do a complete migration of all data to the new version. There are plus sides though, right? Yep, there are: The reason the post content gets rebuilt is so that it's all stored as HTML in the database rather than BBCode that has to be parsed and isn't upgrade-proof. They've also finally stopped using their own-brand post editor and gone with CKEditor, so upgrades should be a little less chaotic as custom tags have been replaced with universally-recognised data-attributes. Theoretically this means that I have less of a headache if they choose to do another complete rewrite in 5 years' time. The code-base has been rewritten and they've removed some of the less used features to speed things up, plus it'll be compatible with PHP 7 soon which means that theoretically everything on the main ProcessWire site can be updated to work with PHP 7 soon and benefit from some nice speed increases. We should be good to stay on the 4.x branch for several years. I'm not anticipating the devs rewriting the codebase for at least another 4-5 years as they've decided to future-proof it as much as possible. Did I mention upgrades will be easier for me to manage in future? Wouldn't it be easier to build a ProcessWire forum module with all the same features? I do think this from time to time, but to be perfectly honest it takes a team of people at least a year to come up with everything you see here. I'm sure I could emulate much of it pretty quickly if I was paid to do it full-time for a year (including BETA testing etc) since the ProcessWire API and module structure is much more sane, but I might also go crazy in the process One day maybe... --- So at some point tomorrow, you will see a message here saying we're upgrading and to check back later. I can't give a precise time estimate, but it is a lengthy process - I would expect it to be at least 6 hours starting around 10am GMT - fortunately most of that is waiting for content to automatically rebuild - and then I have to apply some manual tweaks afterwards which will take about 30 minutes. Schedule TBC - hopefully still tomorrow - some of the final preparations are taking a little longer. Downtime will be kept to 2 blocks of 20-30 minutes maximum if all goes well. Existing post content will look quite odd for the duration, but new posts will be absolutely fine and we'll fix up the old ones afterwards.17 points
-
I have started developing a new Fieldtype. This Fieldtype provides a CTA Button for the frontend. The fieldtype stores 3 values for 'label' (multilanguage), 'target', and 'class' (CSS). Output Markup can be edited in the Fieldsettings. Enter Page id or path in the target field to store an internal page. If page exist the Field will show up the path in current user language and Page ID as well. If you type in a numeric string which is not similar to any pages id, the inputfield will throw an error. Any other string will be stored as is. If target is an internal page any page field value or property is accessible. The Inputfield optionally provides an API Description. Please try out: https://github.com/kixe/FieldtypeButton http://modules.processwire.com/modules/fieldtype-button/ Feedback welcome. Screenshots:9 points
-
Bootstrap-4 Minimal site profile for ProcessWire This profile is based on the "minimal site profile (intermediate edition)" and bundled with Boostrap v4.4.1 Features Bootstrap SASS Font-Awesome SASS Render / helper functions for : Simple ul navigation Bootstrap Multi-level navbar Bootstrap Carousel Bootstrap Cards Bootstrap Jumbotron Boostrap Accordion Assets minification, files bundle Dependencies jQuery Popper.js Bootstrap FontAwesome Prequisites You'll want to install the following on your system before proceeding: Yarn / NPM How To Install Download the zip file at Github or clone directly the repo with git clone and skip the step 2. Extract the folder site-pwbs4-master into a fresh ProcessWire installation root folder. During the installation of ProcessWire, choose the profile "ProcessWire Bootstrap 4 profile". After installation You can find the development file (CSS/SCSS/JS) in site/assets/dev/src The profile can be used as is only with $config->debug set to false. To use it in debug mode, you are required to install the dependencies with the package manager. Open a terminal in site/assets/dev and execute the following command-line: yarn Available commands : Rebuild, minify and bundle assets for release : yarn build References Bootstrap v4 documentation ProcessWire documentation ProcessWire Forum: bootstrap tag ProcessWire Forum: bootstrap related posts Credits The ProcessWire staff Inspiration from @gebeer and his Bootstrap 3 profile post Members who contributed in various post about Bootstrap navigation and code (see code-source for refs). Screenshots8 points
-
Thanks Pete for all the hard work! Many of us know how much work (and pain) it requires to upgrade these monoliths, so hats off and I pay for the beer when you visit Finland (it is raining, so you would feel comfortable and just like home here!).7 points
-
Just added a ToDo panel: It reports the following comment types: 'todo', 'fixme', 'pending', 'xxx', 'hack', 'bug' If you have your editor configured, the comment text link opens the file to the line of the comment. The icon reports: the number of items in the template file for the current file / the total number of items across all files. Red: there are items for the current page's template file. Orange: there are items in other files, but none in the current page's template file. Green: no items in any files under /site/templates/ Please let me know if you find any items that aren't being reported, or items repotred that shouldn't be. One other key change in the last commit is to the Console Panel - you can now use CTRL+Enter or CMD+Enter to run the code. This keeps your hands on the keyboard and keeps the code textarea focused making it much easier to quickly make and test changes to your code.6 points
-
InputfieldSelectize A Inputfield to provide a select interface for Processwire CMS FieldtypePage using the (awesome) Selectize.js jQuery plugin, by Brian Reavis. Selectize: https://github.com/selectize/selectize.js Modules directory: http://modules.processwire.com/modules/inputfield-selectize/ Github: https://github.com/outflux3/InputfieldSelectize Features Custom designed options and items for any page select field. Your select options can use any field or subfield on the page, but also sub-subfields, or any data you provide, since you are not limited by tag replacement: you control the precise data supplied to the options using a PHP array that returns data to the module, which is in turn supplied in JSON to the select as adata-dataattribute. The plugin uses the JSON object for each option meaning you can do whatever you want with that data in designing your options/items. Each instance lets you define which fields are searchable for the select Your selects can use display logic based on the value of any field/data item, for example using ternery conditionals you can avoid empty parenthesis. You can design the options and items (what is seen once an option is selected) independently of each other. Therefore you could have special fields on the options for searching, but exclude those on the item. Likewise you can show elements on your item like an edit button which is not needed on the option. Multiselect pages are sortable, and deletable by backspace or optional remove button. When AceExtended editor is installed, the module will use that for the code input fields. Usage Install the Module Edit your pagefield and choose InputfieldSelectize as inputfield. You will see the empty fields that need to be populated to make this work Notes For examples of what you can do (in general) with your selects when using Selectize.js, view the plugin site at http://selectize.github.io/selectize.js/. The plugin theme is selected on the required JquerySelectize module ----- Examples Basic Example PHP (the data array for each item - this must return a plain array): $data = array( 'title' => $page->title, 'company' => $page->company_select ? $page->company_select->title : 'Not set', 'total' => count($page->recipients), 'editUrl' => $page->editUrl ); return $data; Javascript (item and option same) Here, the item.property each refer to the keys of the PHP array that you returned in the above field. This field must be a valid Javascript string with each of the properties you want to show as demonstrated below, and recommended to use the escape(item.property) syntax. These strings are passed to the render functions of the plugin. '<div class="item">' + '<span style="display:block;font-size:14px;font-weight:bold;">' + escape(item.title) + ' (' + escape(item.total) + ')</span>' + '<span>' + escape(item.company) + '</span>' + '</div>' Example screenshot: A more advanced example This example shows how to use conditionals for the PHP and JS to get the select options to look clean and provide the necessary information to assist users in choosing the correct options: PHP $data = array( 'title' => $page->title, 'year' => $page->year ?: $page->year_sort, 'for_inst' => $page->for_inst, 'edit_href' => $page->editUrl ); return $data; Item Javascript: '<div class="item">' + '<div style="color: black; font-size: 14px;"><span style="font-weight:bold;">' + escape(item.title) + ' (' + escape(item.year) + ')</span>' + ' <a class="pw-modal pw-modal-medium" href="' + escape(item.edit_href) + '">Edit <i class="fa fa-edit"></i></a></div>' + (item.for_inst ? '<div style="color:gray;">for ' + escape(item.for_inst) + '</div>' : '') + '</div>' Option Javascript: '<div class="item" style="width:100%;">' + '<div style="color: black; font-size: 14px;"><span style="font-weight:bold;">' + escape(item.title) + ' (' + escape(item.year) + ')</div>' + (item.for_inst ? '<div style="color:gray;">for ' + escape(item.for_inst) + '</div>' : '') + '</div>' Example with images In this example the selects will feature a thumbnail image: You could also set the width of the selected item to 100% depending on where you place the field (e.g. in a column) $image = $page->images->first(); $thumb = $image->size(100,100); $data = array( 'title' => $page->title, 'thumb_src' => $thumb ->url, 'img_dims' => $image->width . 'x' . $image->height, 'img_desc' => $image->description, 'img_size' => $image->filesizeStr, 'edit_src' => $page->editUrl ); return $data; '<div class="item" style="width:100%;">' + '<div class="image-wrapper" style="float:left;"><img src="' + escape(item.thumb_src) + '" alt=""></div>' + '<div class="info-wrapper" style="float:left; padding:5px;">' + '<span style="font-size:14px;font-weight:bold">' + escape(item.title) + '</span><br>' + '<span>Dimensions: ' + escape(item.img_dims) + 'px</span><br>' + '<span>Filesize: ' + escape(item.img_size) + '</span><br>' + '<span>' + escape(item.img_desc) + '</span><br>' + '<a class="pw-modal pw-modal-medium" href="' + escape(item.edit_src) + '">Edit <span class="ui-icon ui-icon-extlink"></span></a></div>' + '</div>' '<div class="item">' + '<div class="image-wrapper" style="float:left;"><img src="' + escape(item.thumb_src) + '" alt=""></div>' + '<div class="info-wrapper" style="float:left; padding:5px;">' + '<span style="font-size:14px;font-weight:bold">' + escape(item.title) + '</span><br>' + '</div>' + '</div>' Current Notes & Issues: Works with 3.0.23 devns Doesn't currently support creating new options (and may exhibit strange behavior if you try and add one not in the list) Doesn't yet support optgroups4 points
-
Tracy Debugger for ProcessWire The ultimate “swiss army knife” debugging and development tool for the ProcessWire CMF/CMS Integrates and extends Nette's Tracy debugging tool and adds 35+ custom tools designed for effective ProcessWire debugging and lightning fast development The most comprehensive set of instructions and examples is available at: https://adrianbj.github.io/TracyDebugger Modules Directory: http://modules.processwire.com/modules/tracy-debugger/ Github: https://github.com/adrianbj/TracyDebugger A big thanks to @tpr for introducing me to Tracy and for the idea for this module and for significant feedback, testing, and feature suggestions.2 points
-
Basic implementation of the Simple MDE as an Inputfield. https://simplemde.com/ Module developed in reply to request from @OrganizedFellow (https://processwire.com/talk/topic/13474-found-a-handy-js-based-markdown-editor/) Modules Directory: http://modules.processwire.com/modules/inputfield-simple-mde/ Github: https://github.com/outflux3/InputfieldSimpleMDE Editor example: Preview mode: Frontend output (using Markdown/Parsedown textformatter and Image Tags) Limitations etc: This has been tested with multiple instances on 1 page and seems to work fine. Toolbar is not configurable, but you can edit the JS file; In the spirit of keeping this simple, there are no module settings. If you want the spellchecker, you can enable it in the JS file. If is seems that there is a need for configurable instances, it could be added, but so far this works fine and can't see any reason to complicate it further.2 points
-
I think part of the problem is it would be a big job for relatively little gain to build a new CMS on top of the core. ProcessWire's admin isn't 100% perfect for every requirement, but it would be easier to build modules or make suggestions to improve things than create another CMS using the framework provided by the API. That said, for very specific jobs you could easily build a front-end control panel providing pretty bespoke functionality without the need to tinker with modules and just not use the admin at all. For example, a real estate directory site where you're inviting many companies to submit their data - you'd only want a simple login area, list and upload form and you might choose to do something like that with frontend templates and the API rather than in the admin (though you could do it with both). I think Soma's post that you referred to is more saying "it's possible to create your own CMS interface on top of the API", not that there's necessarily a reason to do it, or anyone out there with enough time on their hands to recreate all the functionality their own way2 points
-
I'm confused... it comes with a CMS when you install it2 points
-
The problem with your approach is, that it only requests the data once and ProcessWire returns all pages instead of those who match the query string. This could be a problem on very dynamic sites, where the content changes often. Here is my solution which solves this: Modify the standard search.php and add if ($config->ajax) { header("Content-type: application/json"); // Set header to JSON echo $matches->toJSON(); // Output the results as JSON via the toJSON function } so the whole file reads <?php namespace ProcessWire; // look for a GET variable named 'q' and sanitize it $q = $sanitizer->text($input->get->q); // did $q have anything in it? if ($q) { // Send our sanitized query 'q' variable to the whitelist where it will be // picked up and echoed in the search box by _main.php file. Now we could just use // another variable initialized in _init.php for this, but it's a best practice // to use this whitelist since it can be read by other modules. That becomes // valuable when it comes to things like pagination. $input->whitelist('q', $q); // Sanitize for placement within a selector string. This is important for any // values that you plan to bundle in a selector string like we are doing here. $q = $sanitizer->selectorValue($q); // Search the title and body fields for our query text. // Limit the results to 50 pages. $selector = "title|body%=$q, limit=50"; // If user has access to admin pages, lets exclude them from the search results. // Note that 2 is the ID of the admin page, so this excludes all results that have // that page as one of the parents/ancestors. This isn't necessary if the user // doesn't have access to view admin pages. So it's not technically necessary to // have this here, but we thought it might be a good way to introduce has_parent. if ($user->isLoggedin()) $selector .= ", has_parent!=2"; // Find pages that match the selector $matches = $pages->find($selector); $cnt = $matches->count; // did we find any matches? if ($cnt) { // yes we did: output a headline indicating how many were found. // note how we handle singular vs. plural for multi-language, with the _n() function $content = "<h2>" . sprintf(_n('Found %d page', 'Found %d pages', $cnt), $cnt) . "</h2>"; // we'll use our renderNav function (in _func.php) to render the navigation $content .= renderNav($matches); } else { // we didn't find any $content = "<h2>" . __('Sorry, no results were found.') . "</h2>"; } if ($config->ajax) { header("Content-type: application/json"); // Set header to JSON echo $matches->toJSON(); // Output the results as JSON via the toJSON function } } else { // no search terms provided $content = "<h2>" . __('Please enter a search term in the search box (upper right corner)') . "</h2>"; } Then call typeahead with the following options: $.typeahead({ input: '#q', order: 'desc', hint: false, minLength: 3, //cache: false, accent: true, display: ['title'], // Search objects by the title-key backdropOnFocus: true, dynamic: true, backdrop: { "opacity": 1, "background-color": "#fff" }, href: "{{url}}", emptyTemplate: "No results for {{query}}", searchOnFocus: true, cancelButton: false, debug: true, source: { //url: actionURL // Ajax request to get JSON from the action url ajax: { method: "GET", url: actionURL, data: { q: '{{query}}' }, } }, callback: { onHideLayout: function (node, query) { $('#searchform').hide(); console.log('hide search'); } } }); The important parts are "dynamic:true" and the "source" configuration so the query string is beeing sent. Now you have a nice AJAX search. EDIT: If you also want to find the query string in other fields than the title make sure you add filter: false to the config of typeahead.2 points
-
See updated first post. I'll post more when we're closer to starting the process. @Apeisa - just been enjoying some good downpours the last few days, but wouldn't mind the sun making a return2 points
-
I'm using this module recently to put items to the sidebar, and works well. However, when it comes to editors, they don't have access to pages under Admin, so they can't access these sites. Of course I could add them permission but that's risky. So I went with this route: add a Page under Admin, set a template file to it (_sidebar-item.php) create a page under the Home with similar name in _sidebar-item.php get the page under Home: <?php namespace ProcessWire; $p = $this->modules->get('ProcessPageList'); // try to get a similarly named page in root $rootPage = wire('pages')->get('parent=1, name=' . wire('page')->name); if ($rootPage->id) { $p->set('id', $rootPage->id); return $p->execute(); } This way non-superusers can see the sidebar item and have permission to edit (because the loaded page has a non-admin template). Without this they would get "You don't have access to list page /admin/.../".2 points
-
A like is not enough! This is just awesome and opens up so many possibilities for page inputfield UI. :)2 points
-
You can build a lot of very custom stuff without the need to create another CMS on top of the framework. I have about a dozen or so modules that are essentially standalone apps within the PW admin. I know there are lots of others who do the same. Is there something specific you want to build?1 point
-
There is this ancient discussion about using ProcessWire as a framework for Textpattern. Never got any traction, but it would have certainly been possible.1 point
-
The only person I know who has fully done it (like @Pete alluded to) is Ryan ....He built the 'Process' (/wire/modules...i.e. the CMS/Admin) on top of the 'Wire'...the core...Not exactly what you asked for, but this app by @Jonathan might interest you. Am curious, where do you want to go with this? Planning to build something? ...1 point
-
I refering to the answer of Soma in this post where he makes assumptions about creating a own CMS based on pw-core since the core is a Framework. Probably my Question was perhaps not correctly formulated. I wanted to know, if someone had tried to build a own CMS based on the pw-core framework. Sorry for my confusing Question.1 point
-
Ok, thanks. Don't worry about the errors, I use this module only occassionally, and I just took a look on the opcache viewer as there's opcache on the server. As for issue No1, maybe there's an extra "children('include=hidden')" that doesn't needed? // original - throws error $p = $this->pages->get(22)->children('include=hidden')->get('name=' . AdminLinksInFrontend::opcPageName); // works $p = $this->pages->get(22)->get('name=' . AdminLinksInFrontend::opcPageName); // in one go $p = $this->pages->get('parent=22, name=' . AdminLinksInFrontend::opcPageName); Admin->Setup has the id of 22 here too. I forgot to mention that it's PW 3.021 here but it seems you figured this out1 point
-
No problem mate, always happy to help. Be sure to checkout this thread as well with more examples.1 point
-
I wrote something about this on my thread. let me look for it UPDATED: https://processwire.com/talk/topic/8374-how-to-approach-this-feat-in-processwire/page-21 point
-
Hey mr-fan, If I understand your case correctly you could use implode() instead of your if else / foreach so you'll save the a few lines. Makes it more readable too: echo wire('page')->field_name->implode(', ', 'title'); // This will produce title1, title2, title3, lasttitle1 point
-
I know this topic is kinda old... Since this is a othen find on google search on finding/handling first/last entries i will provide a little snippet that get pagefield titles prepared for needed output. Should just stay as very simple example for using first() and last() on this short thread... //get the page field titles right with a separating , $titles = ''; //count if we have more than one page in the page field if (count($page->mypagefield) == 1) { //we have only one page so get the first page title $titles = $page->mypagefield->first()->title; } else { //we have more pages in the pagefield so lets loop foreach ($page->mypagefield as $t) { //we need a , after every entry but not in the last one if ($t === $page->mypagefield->last()) { $titles .= $t->title; } else { $titles .= $t->title.', '; } } } //output will be "title1, title2, title3, lasttitle" best regards mr-fan EDIT: use arjen's example of implode() is in this case much more smarter....but the example above shows how to work with first() and last() so it will stay1 point
-
Not as often as I should. Mostly to sort properties in css style blocks or arrays/objects. I follow googles styleguide https://google.github.io/styleguide/htmlcssguide.html#Declaration_Order1 point
-
My pagination class has no concept of appending or prepending. It's just selector after selector after selector. Just put your selectors in the intended order from the start and you should be fine. If the first selector does not return any items it'll just skip to the next one.1 point
-
I don't know what the context of this search query is, but if it is from a search box, you could consider to use a Google Custom Search Engine. When the search queries become 'to complex' I would go for that option. Google gives back the URL, together with other found data. You could use the URL to get back to your own 'ProcessWire' data and enrich the result as wished. When you are scared having to pay for the searches, you could always store the google results temporary with MarkupCache or WireCache.1 point
-
Sweet site! From https://processwire.com/api/selectors/: ProcessWire 3 contains a workaround for this, but you should be able to build it into your search code by checking for such short words and handling them with a the %= operator.1 point
-
Hi, If you have 13 minutes to kill, you might enjoy this https://www.youtube.com/watch?v=dIjKJjzRX_E It's funny but he makes some interesting points. This guy also has a series on functional programming with javascript. I've only watched the first video so far but I thought it was pretty good. Recommended here https://processwire.com/talk/topic/13494-methods-to-cycle-through-foreachcompare-values-of-children/?p=1217471 point
-
If the order is your issue than use count() with those selectors, the current page num and some math to determine which pages are to be shown on the current page - then load just those. Edit: I've not tested it with real pages, but the test class seems to work correctly: https://github.com/LostKobrakai/Paginator1 point
-
Time ago I needed something similar. I solved it with a small script. You may use it as a starting point: $media = wire('pages')->find('parent=1034,template=medium,include=all'); $media->sort('media_type,status,title'); foreach ($media->getValues() as $key => $medium) { $medium->of(false); $medium->sort = $key; $medium->save(); // var_dump($key.' - '.$medium->sort.' - '.$medium->media_type.' - '.$medium->title); }1 point
-
Here you go: new InstagramFeed version 1.0.0 add required scope public_content to retrieve tagged media with getRecentMediaByTag('tag') (thanks @gebeer) add ProcessWire 3.x compatibility add endpoint to get a list of recent comments on a media object: getRecentComments($media) Example: <?php $feed = $modules->get('InstagramFeed')->getRecentMedia(); ?> <div class="instagram> <?php foreach ($feed as $media): ?> <?php if ($media['type'] === 'image'): ?> <a href="<?=$media['link']; ?>" class="instagram-item"> <img src="<?=$media['images']['thumbnail']['url']; ?>" alt=""> </a> // display comments <?php $comments = $modules->get('InstagramFeed')->getRecentComments($media); ?> <?php if ($comments): ?> <ul> <?php foreach ($comments as $comment): ?> <li><?=$comment['text']?></li> <?php endforeach; ?> </ul> <?php endif; ?> <?php endif; ?> <?php endforeach; ?> </div> A comment ($comment in the example above) contains the following data: "created_time": "1280780324", "text": "Really amazing photo!", "from": { "username": "snoopdogg", "profile_picture": "http://images.instagram.com/profiles/profile_16_75sq_1305612434.jpg", "id": "1574083", "full_name": "Snoop Dogg" }, "id": "420"1 point
-
Coming from a Rails background years ago, I think it's best to stick with web application frameworks for any web applications that will have heavy iterations and multiple team members. With a webapp framework, you get a testing suite, migrations, ORM, REST, MVC, specific deployment tools and many other necessities. You would have to re-do all those with ProcessWire. Furthermore, using pages to act as a router doesn't feel right. "Use the best tool for the job" and all that.1 point
-
What I basically would need is that the ratio is fixed (and there is a minimum of height/width it can't go below). So users can select parts from the image to crop but that crop would always fit 100% and no further cropping would apply when using the image.1 point
-
Successfully tested ALIF with PW 3.0 and set the version to 1.0.0 stable now.1 point
-
Just thought I'd add the solution to this problem, for reference. Turns out there's a file called /site/assets/cache/LazyCron.lock (or something along those lines) preventing the cron functionality from working when there are PHP errors in my cron logic, which can happen during development of course. Fixing the errors and deleting the lock file restores functionality.1 point
-
I know this thread is old, but with the new Log viewer that Ryan just set up, I have started putting the following just before the body close tag (in main.inc or foot.inc or wherever needed so it is on all pages): if($user->isSuperuser()) echo '<iframe width="100%" height="200px" src="'.$config->urls->admin.'/setup/logs/view/debug/?modal=1"></iframe>'; Then whenever I need to log a variable or output of a function etc, I use the following in my template file: $log->save('debug', $problem_variable); If you need to output an array, json_encode is quite a nice solution: $log->save('debug', json_encode($array)); This will give you (as superuser) a panel showing the "debug" log output (which shows latest entries at the top) at the bottom of your site - very handy1 point
-
Here are some API additions to the dev branch, primarily for WireArray/PageArray/etc. I've found these very handy lately, and would have on almost any project I worked on, so decided they'd add value to the core. I'll add these to the cheatsheet once 2.4 replaces 2.3, but for now, here they are. The examples here use PageArray, but note that these API additions apply to any WireArray derived type, not just PageArray. WireArray::implode() Implode all elements to a delimiter-separated string containing the given property from each item. Similar to PHP's implode() function. Usage: $string = $items->implode([$delimiter], $property, [$options]); Arguments: $delimiter - The delimiter to separate each item by (or the glue to tie them together). May be omitted if not needed $property - The property to retrieve from each item (i.e. "title"), or a function that returns the value to store. If a function/closure is provided it is given the $item (argument 1) and the $key (argument 2), and it should return the value (string) to use. [$options] - This argument is optional. When used, it's an array with modifiers to the behavior: skipEmpty: Whether empty items should be skipped (default=true) prepend: String to prepend to result. Ignored if result is blank. append: String to prepend to result. Ignored if result is blank. Examples: $items = $pages->find("template=basic-page"); // render all the titles, each separated by a <br>, for each page in $items echo $items->implode('<br>', 'title'); // render an unordered list of each item's title echo "<ul><li>"; echo $items->implode('</li><li>', 'title'); echo "</li></ul>"; // same as above, but using prepend/append options, // this ensures no list generated when $items is empty echo $items->implode('</li><li>', 'title', array( 'prepend' => '<ul><li>', 'append' => '</li></ul>' )); // same as above, but with all items now presented as links // this demonstrates use of $property as a function. note that // we are also omitting the delimiter here as well, since we don't need it echo $items->implode(function($item) { return "<li><a href='$item->url'>$item->title</a></li>"; }, array('prepend' => '<ul>', 'append' => '</ul>')); WireArray::explode() Return a plain array of the requested property from each item. Similar to PHP's explode() function. The returned PHP array uses the same keys as the original WireArray (if that matters). Usage: $array = $items->explode($property); Arguments: $property - The name of the property (string) to have in each array element (i.e. "title"). You may also provide a function/closure here that should return the value to store. When a function/closure is used it receives the $item as the first argument and the $key (if needed) as the second. Examples: // get an array containing the 'title' of each page $array = $items->explode('title'); // get an array containing the id, url and title of each page $array = $items->explode(function($item) { return array( 'id' => $item->id, 'url' => $item->url, 'title' => $item->title ); }); WireArray::data() Store or retrieve an arbitrary/extra data value in this WireArray. This is exactly the same thing that it is jQuery. I've personally found this useful when building search engines: the search engine can store extra meta data of what was searched for as a data() property. Then any other functions receiving the WireArray/PageArray have access to this additional info. For example, the search engine portion of your site could populate an array of summary data about what was searched for, and the render/output code could render it to the user. Usage: // Setting data $items->data('key', 'value'); // Getting data $value = $items->data('key'); // Get array (indexed by key) of all data $values = $items->data(); Arguments: The above usage section explains all that's needed to know about the arguments. The only additional comments I'd make are that 'key' should always be a string, and 'value' can be anything you want it to be. Example: function findSkyscrapers() { $floors = (int) wire('input')->get->floors; $year = (int) wire('input')->get->year; $items = wire('pages')->find("template=skyscraper, floors=$floors, year=$year"); $items->data('summary', array( 'Number of floors' => $floors, 'Year constructed' => $year )); return $items; } // the render function can focus purely on output function renderSkyscrapers($items) { echo "<h2>You searched for:</h2>"; // render the summary of what was searched for foreach($items->data('summary') as $label => $value) { echo "<p>$label: $value</p>"; } echo "<h3>Skyscrapers found:</h3>"; // note use of new implode() function, though a foreach() would be just as well here echo $items->implode(function($item) { return "<p><a href='$item->url'>$item->title</a></p>"; }); } WireArray::and() WireData::and() Return a new copy of the WireArray with the given item(s) appended. Primarily as a syntax convenience for various situations. This is similar to jQuery's add() and andSelf() functions, but I've always felt "add" implied adding something to the original rather than creating a new combination, so went with "and" in this case. The term "and" is actually a reserved word in PHP, so you can't usually have a function named "and()", but through the magic of hooks, ProcessWire can. This function should reduce the instances in which you'd need to do "$a = new PageArray();" for example. Usage: // create a new WireArray with $items and $item (appended) $myItems = $items->and($item); // create a new WireArray with $items and $moreItems (appended) $myItems = $items->and($moreItems); // create a new WireArray with $items and $item (prepended) $myItems = $item->and($items); // create a new WireArray with $item and $anotherItem (appended) $myItems = $item->and($anotherItem); // create a new WireArray 4 items $family = $pappa->and($mamma)->and($brother)->and($sister); Examples: // generate breadcrumb trail that includes current page foreach($page->parents->and($page) as $item) { echo "<a href='$item->url'>$item->title</a> / "; } // check if page or its children has a featured checkbox if($page->and($page->children)->has("featured=1")) { echo "<p>Featured!</p>"; }1 point