Leaderboard
Popular Content
Showing content with the highest reputation on 12/07/2014 in all areas
-
Hello there, ProcessWire has been the perfect CMS for me so far but I always missed the Laravel way of doing things when working with PW. Therefore I've built a package that will integrate Laravel into ProcessWire. It's already working out really well and I can imagine a lot of stuff to come in future. Anyways I'm hoping for some peoples support / pull requests / ideas / opinions ... Here's the link: https://github.com/hettiger/larawire Enjoy!8 points
-
One thing to mention about most CSS grid systems is that they work with floats and row markup. Sometimes you need to clear the float, and always you need to prevent box-collapsing. All this brings extra logic (as seen above) what is not really needed if you ask me unless you've to support IE7 and below. Since a year or so I use inline-blocks for grids. With the use of inline-block, you can rid of the whole row element in your grid system and as a bonus you could align columns vertically with the vertical-align property. 'vertical-align: middle' is particularly usefull for grids of logo's and images. // the float way <div class='container'> <div class='row'> <div class='col-50'> <p>Donec sed odio dui.</p> </div> <div class='col-50'> <p>Donec sed odio dui.</p> </div> </div> <div class='row'> <div class='col-50'> <p>Donec sed odio dui.</p> </div> <div class='col-50'> <p>Donec sed odio dui.</p> </div> </div> </div> // the inline-block way <div class='container'> <div class='col-50'> <p>Donec sed odio dui.</p> </div> <div class='col-50'> <p>Donec sed odio dui.</p> </div> <div class='col-50'> <p>Donec sed odio dui.</p> </div> <div class='col-50'> <p>Donec sed odio dui.</p> </div> </div> One disadvantage of the inline-block grid is that the grid should not contain white space characters as those take up space. There are 2 ways to solve those:1. don't use white-space characters. 2. set the font-size in the grid to font: 0/0 a; I always go for option 1. as option 2 eliminates the right usage of ems for fonts inside the grid. Due to ProcessWire it's really simple to not write HTML at all, never escaping PHP. This is how I build my HTML and why there are no whitespace characters in my HTML where it is not needed. A good example of a inline-block grid systems is Griddle, I never have used that one but the idea is exactly the same as my own handcrafted CSS.3 points
-
Horst is right, as far as I can tell, but I'd also like to point out that stacked ternary expressions are a PITA. PHP manual itself suggests avoiding them, and so would I -- not only does your first example work, it also looks better and is much easier to read than the latter one. Getting everything on one line is hardly a goal worth striving for For more details I'd suggest taking a look at http://php.net/language.operators.comparison#language.operators.comparison.ternary and specifically the part about stacked expressions, "non-obvious ternary behaviour".3 points
-
$rowend = $member == $member->siblings->last() ? "</div>\n" : ( $key%3 == 0 ? "</div><div class=\"row\">\n" : '' ); ---------- ^--------------------------------------------------^ not tested but I'm pretty sure. You use surrounding braces where not needed, but not where they are needed.3 points
-
Never said Laravel would be the only PHP framework. You can do all that stuff in other Frameworks or ProcessWire itself too. This is for people that enjoy working with Laravel and want to use it together with ProcessWire.3 points
-
Welcome to the forums! The $user variable is already taken in the globally accessible wire/fuel object, so don't use that. from your module you can set the var as follows (in your before page render hook): $this->wire->set('foo', 'bar'); in your template you can call it like any other wire/fuel variable (like $user, $page, $session etc), since the they are passed to templates automatically: echo $foo; // 'bar' More on THE fuel/wire: https://processwire.com/talk/topic/3876-using-fuel/ Soma:2 points
-
I think this module from Martijn will do what you are looking for: http://modules.processwire.com/modules/textformatter-image-interceptor/2 points
-
Hey Ivan - if I am becoming an expert on character encoding, then we should all be very scared - I still have no clue But anyway, I have committed a fix which seems to work fine now in some limited tests. Can you please take a look and let me know.2 points
-
Don't use the formatter. Why not simply use: <iframe width="560" height="315" src="<?php echo $page->your_field; ?>" frameborder="0" allowfullscreen></iframe>2 points
-
if i was using pages for images, I would keep the images template published and not hidden, but i wouldn't make a php file for it. you can easily find your featured images by using this: $featured_images = $pages->find("template=image, featured=1");2 points
-
Take this <?php class SaveShortUrl extends WireData implements Module { public static function getModuleInfo() { return array( 'title' => 'Rewrite field after save', 'version' => 1, 'summary' => 'An example module used for demonstration purposes. See the /site/modules/Helloworld.module file for details.', 'href' => 'http://www.processwire.com', 'singular' => true, 'autoload' => 'template=admin', 'icon' => 'smile-o', ); } public function init() { // add a hook before the $pages->save, to issue a notice every time a page is saved $this->pages->addHookBefore('save', $this, 'hookPageSave'); } /** * Save as another field. **/ public function hookPageSave(HookEvent $event) { $page = $event->arguments[0]; if($page->template != "yourtemplate") return; //#todo Rename to your field names. if(!$page->source_url) return; $page->domain = parse_url($page->source_url,PHP_URL_HOST); //#todo Maybe some more error handling. $page->of(false); $page->save("domain"); $this->message("Save the domain {$page->domain}."); } } You should name a module first letter uppercase. SaveShortUrl.module and name the class the same "SaveShortUrl" Next you should avoid using a method name same as the class name, that leads to getting called when initialized. In this case the ($event) is not set etc. You only want to execute the method when the hook is executed. Further improvements to the module could be to make autoload a selector to only get loaded in backend: "autoload" => "template=admin". Check for the template or if the field exists or if there's a string could also help.2 points
-
So, let's launch this finally! › https://processwire-recipes.com/ ‹ As you can see, it's a work in progress. The design is going to change, Nico at the helm, and once we got a "critical mass" of recipes, another way of structuring recipes (the "table of contents") will also go online. To contribute, you can post your recipes either in this thread and we'll pick it up, or - codey - via Pull Request on GitHub of this repo here. Frankly, that's the favoured way, simple and a good start dive in to GitHub and this form of Open Source contribution. As always, feedback (and of course, contribution) is really welcome. We're always eager to learn - and helping to learn new stuff and create a place to spread some PW knowledge, that's what PWR was about in the first place Big big thanks to my partner in crime owzim who, among other things, contributed the - imho - heart of this tiny project: a textfile-to-page-importer. Finding an elegant way to contribute was one of the reasons this took so long, and during Beyond Tellerrand conference, it finally clicked into place.2 points
-
Video embed for YouTube and Vimeo ProcessWire Textformatter module that enables translation of YouTube or Vimeo URLs to full embed codes, resulting in a viewable video in textarea fields you apply it to. How to install Download or clone from GitHub: https://github.com/r...atterVideoEmbed Copy the TextformatterVideoEmbed.module file to your /site/modules/ directory (or place it in /site/modules/TextformatterVideoEmbed/). Click check for new modules in ProcessWire Admin Modules screen. Click install for the module labeled: "Video embed for YouTube/Vimeo". How to use Edit your body field in Setup > Fields (or whatever field(s) you will be placing videos in). On the details tab, find the Text Formatters field and select "Video embed for YouTube/Vimeo". Save. Edit a page using the field you edited and paste in YouTube and/or Vimeo video URLs each on their own paragraph. Example How it might look in your editor (like TinyMCE): Here are two videos about ProcessWire https://www.youtube.com/watch?v=Wl4XiYadV_k https://www.youtube.com/watch?v=XKnG7sikE-U And here is a great video I watched earlier this week: http://vimeo.com/18280328 How it works This module uses YouTube and Vimeo oEmbed services to generate the embed codes populated in your content. After these services are queried the first time, the embed code is cached so that it doesn't need to be pulled again. The advantage of using the oEmbed services is that you get a video formatted at the proper width, height and proportion. You can also set a max width and max height (in the module config) and expect a proportional video. Configuration/Customization You may want to update the max width and max height settings on the module's configuration screen. You should make these consistent with what is supported by your site design. If you change these max width / max height settings you may also want to check the box to clear cache, so that YouTube/Vimeo oembed services will generate new embed codes for you. Using with Markdown, Textile or other LML I mostly assume you are using this with TinyMCE. But there's no reason why you can't also use this with something like Markdown or Textile. This text formatter is looking for a YouTube or Vimeo video URL surrounded by paragraph tags. As a result, if you are using Markdown or Textile (or something else like it) you want that text formatter to run before this one. That ensures that the expected paragraph tags will be present when TextformatterVideoEmbed runs. You can control the order that text formatters are run in by drag/drop sorting in the field editor. Thanks to Pete for tuning me into these oEmbed services provided by YouTube and Vimeo a long time ago in another thread.1 point
-
Often times, creating a side project is first and foremost scratching your own itch Or to start differently: Currently, I'm developing a site where I need CKeditor (and later jQueryUI Datepicker) outside of the admin, in frontend. I searched Google and the forums and found an approach to follow - but during the research the site laravel-recipes.com came into my mind (since I'm currently also looking into this framework). It's content consists of small, spot-on bits of information, for example: http://laravel-recipes.com/recipes/269 Just to think out loudly here, wouldn't it be nice to have a ProcessWire counterpart of such a site? processwire-recipes.com for example? Target group: Developers, from beginner to advanced Difference to these forums: Stripping the discussion part, concentrating on the info; and if done properly: bypassing the mediocre forum search, better tagging Difference to the official tutorial section: Focusing on not creating a whole site, but modular parts of it. Single solutions. For example: First child redirects (shameless plug, but this is the format of information pieces I'm having in mind) Difference to the API documentation: Situation-based ("I need this and that") instead of architecture-based Laravel.io (forum), laravel.com (official, and doc) and laravel-recipies.com possibly prove that these type of sites can coexist in a framework ecosystem, so I guess a recipes site would not cannibalize this forum here nor the doc section. A recipe site like this would live and die with its content, of course. I would be ready to share all the small pieces of information I encounter that would make a good "recipe". But that alone wouldn't be enough so this project won't survive without contribution of the community. What's your opinion on this? Yea or nay? Update: ...it just took, erm, nearly three months,... but here it is: https://processwire-recipes.com/1 point
-
I think this idea has come up a couple of times in other threads, but this is just to rationalise it a little. Currently, the Page Table works rather like a repeater in that it is a way of adding and arranging new content in sections via a field. However, because it works in a more public way (the pages created can be arranged within a publically accessible part of the tree rather than buried under admin), the potential for the field is greater than with repeaters. Primarily, there is the opportunity for the field to have the characteristics of a page field. Take this case: A complex, technical article about using Processwire will be much improved by the use of additional information in the form of asides. Using a page table, it is possible to create the five or six asides needed, sort them into the right order and then insert them into the right places using hanna code. (the hanna code could either call them using their key in the array, or by title or by page id, even - if it were displayed) However, in such an article, it would be also very useful to use an existing aside. For instance, the nice aside "what is php?" might be very useful. But that does mean that it needs to be selected from the existing stock and that is not currently possible with a page table, though it is possible with a page field. What it looks like If the functionality were added to the page table field, this is how I would see it being used in practice: On the creation of the page table field, under the option for selecting a parent, a check box is added "allow selection of existing page" When using the field on a template, where currently you have "Add New" at the bottom of the field you now have a second button "Select" This opens up a lister in a modal window where you can select one or more Asides from the given parent as set in the field. These are then added to the list of asides in the field. Preventing Editing One of the dangers of such a system is that the content of a page called by a page table might be edited out of context. Back to our field! A third checkbox says "prevent editing if used in more than one page." Basically, if our aside is used in more than one page it can no longer be edited from the page table field. The only way it can be edited is by going to the page directly as normal via the page tree, and that might be subject to permission restrictions. There may also be a situation where asides are created in their position within a page tree and the ability to edit them via a page table is completely removed for everyone. Perhaps that is an automatic feature where the aside is created via the page tree and not via a page table field. However, in this case where an aside is not editable, another option may be available: Cloning Another possibility is the idea of New from Old. for instance, I want to use that very good aside about what is PHP but I want to add my own relevant information. In this case I can choose my starter aside and click on "Create New from this aside" in the lister modal. A new aside will be created using the same template and the same content which I can then edit. Limiting Inclusion There are situations in this case, however, where an aside has content that is really limited to that one article and should not be reused. (Legal reasons, perhaps, or just simple editorial context). Back on the field settings there would be another check box "allow limited use" Now, when as Aside is created, on the modal window in the Settings tab it has a checkbox that says "Limit to this article only?" This Aside can now not be selected for use by another article. Other Uses Using this modification as an aside is a very basic example, but there are probably hundreds of others. For instance, product variations or attributes in e-commerce where certain variations need to be available to a range of products. With e-commerce, the various restrictions about editing and limiting inclusion would become very important. So there you go, a lot more power and uses. (Note: this might be the wrong starting point, of course, as it could be argued that this should be a modification of a page field!)1 point
-
this is a work in progress... needs testing. in this module the edit links are added to the green boxes using 2 methods: jquery for the page list select and page auto complete, and the native asm select feature for asmSelects (thanks to Soma). Since the PageAutoComplete and PageListSelect are using the hover and icon from asmSelect, the asmSelect assets need to be loaded now on every page edit, in order to make the links look right. if you downloaded this before, please replace with the latest version, as there have been many changes, corrections/bugfixes and improvements. Please report any issues. Updated 12/6/14 at 10PM - please delete earlier versions due to issue with links being added on template editor. Updated 5/7/15: Module removed in anticipation of new official version release: https://processwire.com/talk/topic/9857-module-page-field-edit-links/?p=945991 point
-
Introducing PVC PvcCore: https://github.com/oliverwehn/PvcCore/ PvcRendererTwig: https://github.com/oliverwehn/PvcRendererTwig/ (coming soon) PvcGenerator: https://github.com/oliverwehn/PvcGenerator/ (coming soon) Each time I’ve built a ProcessWire page I’ve struggled with organizing (and separating) code, markup and stuff. Playing around with frameworks (backend as well as frontend) like Rails, Ember and stuff I really liked having a given structure, knowing where to put what piece of code. Therefor I started working on a MVCish way to deal with templates in PW that considers the $page object kind of the data/model layer and adds View and Controller upon it. First by just catching everything via a small processor file added to all PW templates as altFilename. I’ve digged a bit deeper since then and hooked into the native rendering process, have been refactoring my code base more than a dozen times. Now I got a first version that seem to work and I’d love some of you guys to try it out! PVC (instead of MVC) stands for Page-View-Controller, as it considers PW’s $page var the model/data layer. I’m still working on the README.md on GitHub to document the basics. So have a look for more detailed infos there. I’ll give you a short overview here: Code separation: PVC introduces views and controllers to your PW templates as well as multiple action templates. Controllers, as most of you already know, keep all the business logic. Controllers execute actions wired to routes (urlSegment patterns). Even routes with dynamic segements (e.g. /edit/:id/) can be defined and assigned to an action, providing input through $this->input->route. Values can be set on the controller to be accessable in your templates later on. It’s also possible to set dynamic values as closures to be calculated (e.g. using field values of the current page) on render. Also controllers allow you to set layouts, styles and scripts to be available in your layout or template. Logic can be shared through inheritance between controllers. Views introduce view helpers. Helpers are functions that are made available to you (only) within your template context. There are predefined ones like embed(), snippet(), styles() or scripts(). But you can implement your own helpers easily and share them among your view classes through inheritance. Action templates contain the actual markup. Every action defined in a controller uses its own template. So the same page can display content accordingly to the action that was addressed via urlSegments/route. Within templates you can access all field values and values set on your controller like globals (e.g. <?=$title?>). Modular renderers: PVC implements rendering through separate renderer modules. PvcCore comes with PvcRendererNative that gives you template syntax the good ol’ PW/PHP way. A Twig renderer is in the making. And maybe you want to build your own renderer for the template syntax of your choice? I consider the module an early Beta version. So make sure to try it within a save environment! Would love to get some feedback (and error reports). I’m no professional developer, so code quality may suck here and there. So I’m glad for inputs on that, too. Also there is some old stuff in there yet, I still have to get rid of.1 point
-
I don't ever use the ternary operator for stuff with more condition level, if that's what it's called. It's just not readable at all, and I think without setting brackets properly the result can be unexpected. Only ever one ? and one :1 point
-
@pwired, I don't get your post. @hettiger, thanks for the module, looks interesting, have to look into it at a later point in time though.1 point
-
Santa Claus, with a little delay, brought to you an updated Hungarian language pack for ProcessWire 2.5.10, including the strings for the latest improvements, e.g. the threaded commenting system. Totally 7 files changed. The changelog for this release is available on the GitHub Project page, as before. Download the full package via the ProcessWire Modules Directory: http://mods.pw/7h1 point
-
The problem seems to be solved. As I did not localize it accurately enough in the first place, I cannot be 100% sure, but it seem to work alright now. Thank you, Adrian. Will report back if something goes wrong.1 point
-
...and just to contribute to the original topic. I use this function to get all subpages of a page: function getAllDescendants ($root, $prependRoot) { $descentants = new PageArray(); foreach ($root->children() as $child) { $descentants->add($child); if ($child->numChildren()) { $descentants->add(getAllDescendants($child)); } } if ($prependRoot) $descentants->prepend($root); return $descentants; } I include this function in _functions.php file which prepends every template file and use it like this: <?php $listingPages = getAllDescendants($pages->get('some selector'))->find("some selector"); foreach($listingPages as $listingItem) { ... }; ?>1 point
-
@Kay Lohn: Your "...limit=3" selector fires several times - as many, as there are children under "Projects". And each time it limits maximum number of featured photoes under that category. So you can output a maximum of (3 x NumberOfProjectsChildrenPages) featured photo pages. As Macrura suggested there are better/simplier ways to do what you intend to, like: <?php foreach ($pages->find("template=photo_page_template, include=hidden, sort=-date, limit=3")) { ?> <a href="<?php echo $child->link_to_page->url; ?>"> <img src="<?php echo $child->my_image->url; ?>" alt="<?php echo $child->image_desc; ?>"/> </a> <?php }; ?> Where photo_page_template is the template of you photo pages.1 point
-
The other option would be to change the regex in the module. This is what I use in Get Video Thumbs and it works fine with plain text (URL) and Textarea fields: /\s*(?:http(?:s)?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/(??:watch)?\?(?:.*&)?v(?:i)?=|(?:embed|v|vi|user)\/))([^\?#&\"'<>]+)/ That said, I agree with Martijn's approach1 point
-
Agreed with Martijn, that's probably better in most (if not all) cases. If you really want to use this module, something like this should work too: if ($page->your_field) { $value = "<p>" . $page->your_field . "</p>"; $modules->get('TextformatterVideoEmbed')->format($value); if (strpos($value, "<p>") !== 0) echo $value; } Only benefits of that approach would be having a common way to present videos on your site, and using oEmbed instead of relying on your own case-by-case embed codes. Other than that, I don't really see much value in it1 point
-
Version two would then be <?php class SaveShortUrl extends WireData implements Module { public static function getModuleInfo() { return array( 'title' => 'Rewrite field after save', 'version' => 2, 'summary' => 'An example module used for demonstration purposes. See the /site/modules/Helloworld.module file for details.', 'href' => 'http://www.processwire.com', 'singular' => true, 'autoload' => 'template=admin', 'icon' => 'smile-o', ); } public function init() { /** * Hook called just before a page is saved * * May be preferable to a before(save) hook because you know for sure a save will * be executed immediately after this is called. Whereas you don't necessarily know * that when before(save) is called, as an error may prevent it. */ $this->pages->addHookAfter('saveReady', $this, 'hookPageSave'); } /** * Save domain info to another field * No need to call page->save() when changing a value * as it will get saved just after this call. * * Make sure there's something in source_url and it has actually changed * with $page->isChanged(what) */ public function hookPageSave(HookEvent $event) { $page = $event->arguments("page"); if($page->template != "basic-page") return; if(!$page->source_url) return; if(!$page->isChanged("source_url")) return; $page->domain = parse_url($page->source_url, PHP_URL_HOST); if($page->domain) $this->message("Saved the domain: {$page->domain}."); } } now you have already a module in the "modules" table in your DB, go to phpmyadmin and remove the entry for "saveShortUrl" Come back and refresh modules, and install the new one.1 point
-
I had busy times in Linux world, but i started with 2.5 localization before few days as i am making new PW project. So YES there will be support for Czech 2.5 version.1 point
-
exactly! You can call size() with second param = 0 (zero), what is the same as calling width(). Pageimage::size build the names according to passed values for width, height and optional options for cropping and suffix: basename.widthxheight-suffix.ext and the code of the function for blog images should be look like that, because there is no $event->arguments(2) available like it could be when calling from templatecode: public function sizeBlogCloud($event) { $image = $event->object->getPageImage(); $width = (int) $this->input->get->width;//$event->arguments(0); // $height = (int) $this->input->get->height; //$event->arguments(1); // not needed here, or set it to 0 $options = array(); if ($width < $image->width) { // add a suffix if it is a resized version, don't add a suffix if it is the original unresized image $options["suffix"] = array("is"); } $resized = $image->size($width, 0, $options);1 point
-
A bit of jquery to the rescue, assuming you are using jquery for things like your lightbox anyway. For the moment, lets assume that the output of your RTE editor field is in a div with the class .post Then you can add a class to all images within that div $('.post img').addClass("myclass"); Likewise, you can add classes to the <a> wrapper - this is for colorbox: $('.post img').closest("a").addClass("colorbox ").attr('rel', 'group'); Having it all inside some sort of containing div saves confusion.1 point
-
Let's see what happens. Of course alternatively, serializing the form object also works quite simply with a foreach, without using PW's WireArray::explode() which is fun anyway. After all the trick here is the $form->getAll(), which returns a InputfieldWrapper containing a flat array of only the fields whithout fieldsets and nested wrappers. And example of how to populate the form directly without using processInput($data), which requires to toggle the CSRF and the form validates and show errors when rendering the form. So setting values to the form object using $form->get(field)->attr("value", $value) does that "silently" and it doesn't trigger any validation thus not show errors when initially loading the form. if($input->post->send) { $form->processInput($input->post); if(!count($form->getErrors())){ $exclude = array("send"); // skip fields by name foreach($form->getAll() as $field) { if(in_array($field->name, $exclude)) continue; $formdata[$field->name] = strip_tags($field->value); } $session->setFor("shop", "userformdata", $formdata ); $session->redirect($this->page->url . $this->nextStepSegment); } } else { // populate data to form inputfields without using processInput(), which processes the form if($formdata = $session->getFor("shop", "userformdata")){ foreach($formdata as $name => $value) $form->get($name)->attr("value", $value); } } return $form->render();1 point
-
1 point
-
You could use an autocomplete pageField for the tags. That way existing tags are available across all pages using the field. Check the "allow new pages to be created from this field" option. The end result, is that your tags are now page references that you can search against by the sanitized name field.1 point
-
This might be the finest big, data-heavy PW site (aside from some of Ryan's sites obvs ), I've seen. Great work, and please keep us up to date on future developments.1 point
-
All the taxonomy is handled by page fields. Some are just basic fields like Origin and Growth habit, and others like Conclusion Type and Zones are part of a PageTable. (Screenshots at the end of the post). There is a lot of JS involved in the filters — more than I have time to explain at the moment. The short version: Filter selections build a URL containing the page IDs for any selected filters via JS. We needed the URIs to be as short as possible, since they will be frequently emailed, and eventually cited in publications. They end up looking something like: /?zones=1030,1028&types=1082,1080&growth_habit=19040,1022 A selector is then built from those GET variables in the template. // default selector, used for assessment page $selector = "template=species, limit=16, sort=name,"; // zones GET variables if ($input->get->zones){ $zones = explode(",", $input->get->zones); $q = implode("|", $zones); $selector .= "@conclusions.conclusion_zones={$q}, check_access=0,"; } It gets a little more complicated, because of the infinite scroll. A maximum of 16 results are initially shown for any query, additional items are pulled in via AJAX on scroll. So if there are GET variables involved, the AJAX call needs to pass them along. In the JS function getUrlVars() { var vars = {}; var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) { vars[key] = value; }); return vars; } var GET_zones = getUrlVars()["zones"]; There is a GET_* for each possible filter. All of which are passed via the data param in the $.ajax() call. data: { start:start, query:query, zones:GET_zones, types:GET_types, origin:GET_origin, growth_habit:GET_growth_habit, tool_used:GET_tool_used }, Screenshots This is a bit of a disjointed explanation, but should give you some idea.1 point
-
Well, I’ll give you a short example to demonstrate how it all works together. Imagine the following simple setup: Page Tree Home |- Products (template=products) | |- Product 1 (template=product) | |- Product 2 | |- Product 3 | |- Product 4 | |- Product 5 | |- ... Page Products with template “products” is meant to show a gallery of product images. So we create a controller for the template (that otherwise would be auto-generated on the fly) in site/templates/controllers/ named products.controller.php. <?php /* site/templates/controllers/products.controller.php */ class ProductsController extends BaseController { // init() is executed on controller setup before action is called public function init() { // Just in case you defined general stuff in BaseController’s init() method parent::init(); // Set (for all actions of this controller) a script 'jquery/my.jquery.plugin.js' in group 'footer' with priority 5 // In layout and template you can then output the scripts group 'footer' using view helper <?=scripts('footer');?> // Assets defined via path are looked up in the corresponding folder within site/templates/assets/. You can alternatively // define assets using a URL. $this->script('jquery/my.jquery.plugin.js', 'footer', 5); } // index is the default action executed if no other route matches public function index() { // Add (for this action only) a stylesheet 'gallery.css' to group 'footer' with priority 10. // In layout and template you can then output the styles group using view helper <?=styles('footer');?> $this->style('gallery.css', 'footer', 10); // Set a template var 'products' that contains current page’s children as PageArray $this->set('products', $this->page->children('template=product, sort=title')); } } We’ll need an action template for products’ index action in site/templates/views/products/ named index.tmpl.php. <!-- site/templates/views/products/index.tmpl.php --> <h2><?=$title?></h2> <ul class="products"> <?php foreach($products as $product) { ?> <li class="products__item <?=gridClass($product->images->first(), 'products__image');?>"><a href="<?=$product->url?>"><img src="<?=$product->images->first()->url;?>" alt="<?=$product->title;?>"></a></li> <?php } ?> </ul> We’ll have a look at the used view helper gridClass() later. The first page in PW’s page stack (the one rendered through processPageView directly) is rendered and outputted in a layout’s outlet. Layouts are stored in site/templates/layouts. There is a default layout default.tmpl.php preinstalled. The rendered action templates are outputted within the document structure by using <?=$outlet?>. You can add as many layouts as you want and change the layout the action template will be rendered in by setting another template within your controller’s init() method like this: ... public function init() { parent::init(); // use layout site/templates/layouts/shop.tmpl.php for this template $this->set('layout', 'shop'); } ... Our product template of course also gets a controller: <?php /* site/templates/controllers/product.controller.php */ class ProductController extends BaseController { public function init() { parent::init(); // assign route pattern with dynamic segment to action 'reviewVote' $this->route('/reviews/:id/vote/', array('id' => '^[0-9]+$'), 'reviewsVote'); } public function index() { // set dynamic value to be calculated right before rendering the action template $this->set('similarProducts', function() { // your logic to get a PageArray of similar products goes here $tags = $this->page->get('tags'); // or $tags = $this->get('tags'); as it will pass the request through to page, if 'tags' isn’t set on controller itself $similarProducts = ...; return $similarProducts; }); } // action accessible via route /reviews/ public function reviews() { // we should set up this for pagination in template settings $this->set('reviews', $this->pages->find("template=reviews, product={$this->page->id}")); } // action accessible via route /reviews/new/ public function reviewsNew() { // Later versions will allow to handle actions depending on REQUEST_TYPE, but for now: if($_SERVER['REQUEST_METHOD'] == 'POST') { // create review logic goes here ... $this->session->redirect(...); } else { $this->set('form', $this->forms->get('review')); } } // action for action route /reviews/:id/vote/ public function reviewsVote() { if($id = $this->input->route->id) { // save vote for review ... } } } Template product’s action templates go into site/templates/views/product/ named index.tmpl.php, reviews.tmpl.php, etc. When we talk about routes in PVC, it means patterns of PW’s urlSegments. If you want to have multiple actions and routes, you have to enable urlSegments for the particular template. Index action is performed when a page is addressed directly via its path. If urlSegments are added to the request, PVC captures the urlSegments as a action route and provides it to the controller which determines the action to perform for the route. Until now all template specific view classes (like ProductsView, ProductView) are generated on the fly. Let’s use some view helpers to reduce logic in templates. For example we want pagination looking all the same all over the site. Therefor we define a general view helper for that in site/templates/views/base.view.php. <?php class BaseView extends PvcView { /** * Add custom view helpers that will be available as * global functions within your templates. * @method customViewHelpers * @param Array $scope contains an associative array of template scope * @returns Array An associative array with key/closure pairs */ public function customViewHelpers($scope) { return array( /** * Each helper is defined as a pair of key (name of the helper * function in template context) and closure (function body) * Via use() you can pass in $scope and custom values to be * available within your helper. * The helper below can be called like this: <?=sayHi('your name');?> */ 'pagination' => function(PageArray $pageArray) use($scope) { return $pageArray->renderPager(array( 'nextItemLabel' => "Next", 'previousItemLabel' => "Prev", 'separatorItemClass' => "pagination__item--separator", 'nextItemClass' => "pagination__item--next", 'previousItemClass' => "pagination__item--prev", 'currentItemClass' => "pagination__item--current", 'lastItemClass' => "pagination__item--last", 'listMarkup' => "<ul class='pagination pagination--{$scope['template']}'>{out}</ul>", 'itemMarkup' => "<li class='pagination__item {class}'>{out}</li>", 'linkMarkup' => "<a href='{url}'><span>{out}</span></a>" )); }) ); } } Now you can render your pagination in your action templates just by passing a PageArray value to your pagination view helper like <?=pagination($reviews);?>. You also can create template-specific view helpers (like gridClass() used in our products action template for index at the top) by creating a view class for the template in site/templates/views/ like this: <?php /* site/templates/views/products.view.php */ class ProductsView extends BaseView { public function customViewHelpers($scope) { $helpers = array( 'gridClass' => function(PageImage $img, $class=null) { $w = $img->width(); $h = $img->height(); $orientation = 'square'; if($w < $h) $orientation = 'portrait'; if($w > $h) $orientation = 'landscape'; return is_string($class) ? sprintf('%s--%s', $class, $orientation) : $orientation; } ); return array_merge($helpers, parent::customViewHelpers($scope)); } } Predefined view helpers are for example embed() to render other pages within your current action template, scripts() and styles() to output asset groups and snippet() to render reusable code chunks stored in site/templates/snippets/ within the current action template’s scope. Hopefully this gives you a vague idea how you can organize your code base using PVC.1 point
-
@Deltanic, It sounds like you are doing after-the-fact debugging using the call-stack shown in the browser. I try not to do that as I don't find it as helpful as using breakpoints or single-stepping new code in my IDE of choice (chrome + xdebug helper & vim + vdebug.) In an IDE you get the full call-stack, variables, code, execution location and can use breakpoints etc. Regarding cleaning up the format of the xdebug callstack output - have you tried the xdebug stack-trace documentation? (There's plenty of good documentation for xdebug.) Like I said, I don't use xdebug the way you seem to want to so I can't help any more than that. I would recommend giving some kind of IDE a go - as I find it so much more convenient for debugging than in-browser stack traces.1 point
-
Not really sure what you are trying to do, since you don't have to check if a parent exists. A parent always exists in ProcessWire. This is how you can check the number of parents. Maybe that will help you out. $parentCount = count($page->parents); if ($parentCount == 2) echo "do something";1 point
-
here it is... https://processwire.com/talk/topic/8477-processpageselectlinks/?p=820201 point
-
If one want to exclude all hidden pages one may use: if ($item->isHidden()) continue;1 point
-
div class='breadcrumbs'><!-- breadcrumbs --> <?php // breadcrumbs are the current page's parents foreach($page->parents() as $item) { //skip SideBarPages using id of the page. //You can also use $item->id=='1234' $item->title=='titleOfSideBarPages, $item->name, etc //using the ID, though less readable, is more foolproof if ($item=='1234') continue; echo "<span><a href='$item->url'>$item->title</a></span> "; } // optionally output the current page as the last item echo "<span>$page->title</span> "; ?> </div><!-- end breadcrumbs -->1 point
-
Managed to do this $pages->get(1)->get("image")->get("name=myotherlogo.png")->url and even $pages->get(1)->image->get("name=myotherlogo.png")->url I guess it's the shortest version...1 point
-
To get a page by index number from a PageArray is easy: /* returns a page object */ $pagebyindex = $myparentpage->children->eq(integer $num); Now I needed a function which returns the Index of the page from the siblings. It took me a while to figure this out, and I like to share this here: /* returns an integer */ $indexbypage = $myparentpage->children->getItemKey($page); // or $indexbypage = $page->siblings->getItemKey($page); Found this information in http://cheatsheet.processwire.com/ under section PageArray/WireArray and here: http://processwire.com/apigen/ To get complete information about API this page is your (and my) friend. Have a nice day1 point
-
You could create a module based on LazyCron by Ryan and combine it with my E-Mail-Verfication-Module which checks also availability of mailhost. Here is a function to check availability of an url. function check($str, $needle) { return (strpos($str, $needle) !== false); } function validate($url) { $header = @get_headers($url); return (isset($header) && count($header) > 0 && check($header[0], "200")); }1 point
-
Hello Horst, Adding the "images.tags=coupon" selector to Kongondo's code DID THE TRICK! You guys ROCK! Thank you1 point
-
Hi Dave, Welcome to PW and the forums... Glad you sorted it out but would like to suggest you do it differently...assuming I understood the question correctly... If all you want is to grab one random image from a coupon page, there is probably no need to find several pages first. Why not grab ONE random page instead (one containing your coupon images) and display its coupon image? Assuming that a coupon page contains other images as well but you are only interested in the coupon image (i.e. tagged 'coupon'), something like this maybe... //we only get one random coupon page (we use 'get' rather than 'find') $couponimage = $pages->get("template=service-pages, sort=random")->images->getTag('coupon'); Edit: This code assumes there will always be a coupon image in those service pages.1 point
-
Fixed module versioning after feedback here, tested under 2.5, added small improvements.1 point
-
I am sure lots of very cool things could be done, all of which will require a little more than the 5 mins of planning and 20 mins of coding I put into this one Here is a revised version (with a new name) with about an extra 15 mins of coding that adds support for batch adding/removing of fields from templates. I think I might actually make use of this version quite a bit. EDIT: I don't think "Template Setup Batcher" is the right name - not really about setting up templates from scratch. Oh well - maybe change again if a newer version ever happens ProcessTemplateSetupBatcher.module1 point
-
How about this little process module You can choose the templates, access permissions, roles, and whether you want to add or remove these roles from the selected templates/access permissions. Test carefully, but I think it should observe all other permission rules etc. For example, it won't let you add Create if Edit is not checked, or already assigned in the current settings. Also, if you choose to remove Edit, it will automatically remove Create even if you don't select it. These are to adhere to PW's access rules. Not sure if this is worthy of being added to the PW modules directory or not - what do you guys think - would people actually use this?1 point
-
Just an FYI if you're new to SVG in PW - take a look at ImageRasterizer. It comes in very handy if you want to provide fallback PNGs for older browsers, or you just want to rasterize really complex SVGs (that take too long to render) before displaying them on the site.1 point
-
@owzim glad to have seen that article about ello.co funding, thanks for the find. While that article (that as far as I can see essentially says ello has VC backing QED the author won't use it) is clearly one way one could feel, my main influences come from my lucky/unlucky chance membership of a management team that did a management buyout (backed by a bank and a VC) and from just my general assumptions. I am comfortable that the outcome of a VC investing will be an exit strategy because a future exit does not predetermine who will be exiting or whether current or new owners actions will be good or bad. An exit does not by definition mean a business will necessarily be sold away from the original owners to bad people or that the current owners will begin to do bad things just because a VC is currently part of the business. Sometimes the owners pay off the investors and buy themselves out. Other times there is a sale but it's a partial sale to other people who invest out of interest and enthusiasm. Other times a strategy might indeed involve 'evil' data-selling strategies etc. My hope is that the ello.co people retain the motivation that got them here, a comprehensive dislike of the bad points of Facebook (apologies for second guessing their motives and assuming these are they, but that's my best guess from what I've read). I do get the point of the article however that a decentralized, not owned by anyone other than you, approach might be best. But my problem with that is that I expect ello.co to have a very, very hard climb making itself even 10% as popular as Facebook, I think anything less 'focussed' or easy to use or needing of tech/geek knowledge is even less likely to get widespread adoption. And I want to use a social network with widespread adoption so my utterly tech-clueless bother can use it as easily as anyone else can. So for me I am looking at a glass half full when I look at ello.co (that is I am choosing with, I think, some justification, to be optimistic): yes it may currently be surviving via VC cash but they have a clear aim of creating an open and sustainable income soon: "Very soon we will begin offering special features to our users. If we create a special feature that you like, you can choose to pay a very small amount of money to add it to your Ello account forever." I love the UX/UI of ello I am liking everything they say about themselves, so far. I may be proved naive but I am banking on ello.co not doing the bad stuff that I (and clearly lots of others too) feel the likes of Facebook have done/do. Here's hoping my glass stays half full!1 point