Leaderboard
Popular Content
Showing content with the highest reputation on 08/24/2020 in Posts
-
Heads-up! Wireframe 0.12.0 is now the latest master version. This is a pretty big update (in both lines of code, as well as features), so I'd recommend testing carefully after updating. The biggest parts are implemented as a companion module Wireframe API, but there are quite a few changes and updates for the main module as well: ### Added - Support for named arguments when using `Wireframe::component($component_name, $args)`. - JSON API. See comments in the WireframeAPI module file for more details. - New Page methods Page::getController() and Page::setController(). - Module config screen provides support for creating directories corresponding to configured Wireframe URLs, assuming that they were provided as relative paths. ### Changed - Wireframe::setView() accepts optional view name as an argument. - View::setController() accepts Controller name (string) in addition to Controller class instance or null. - When Controller is instantiated, it no longer overrides the Controller property of the related View instance. ### Fixed - Wireframe::partial() now works as expected for partial names with file ext included (partial_name.php etc.) - Wireframe::partial() prevents 2 or more dots in partial name, just in case (directory traversal is not intended).3 points
-
Relative to version 3.0.164, this latest version on the master and dev branch (3.0.165) contains 16 commits with various minor fixes, improvements and optimizations, and it resolves 6 reported issues. Version 3.0.164 has been very stable, and this version should be even more so. Both the dev branch and master branch are identical at this moment. As mentioned last week, new versions of both FormBuilder and ProCache are in development, and I’ve been working on both of them quite a bit this week. I mentioned some of the updates being made in FormBuilder last week. Another new feature to mention is the addition of reCAPTCHA to the spam prevention tools. While our built in honeypot is just as effective (when setup properly), it does its work silently, behind the scenes, never visible to anyone. Whereas reCAPTCHA is hard to miss, which is a benefit for clients that want to to be able to literally see spam prevention measures in place. No doubt, it brings a certain level of comfort, and adds another level of authority to your forms. So I’m really glad to add it to our set of form tools. Rather than building it into the form “actions” I’ve instead built it as an Inputfield module (InputfieldFormBuilderRecaptcha), as this gives you a little more flexibility with regard to placement and customization. It is ready for download now in the FormBuilder board Download thread (login required). It can be used with any version of FormBuilder 0.3.9 or newer; meaning it’s ready to use now. If you download it, be sure to read the pinned reCAPTCHA thread in there for detailed instructions (it's very easy to setup). I’ve been talking about it for a long time, but it seems like it may finally be a good time to re-do the modules.processwire.com site. It all works just fine, but just needs an update, consistent with the rest of processwire.com. So we’ll likely slow the pace of core updates for 2-3 weeks to work on that, sometime between now and the end of the year. Thanks for reading and have a great weekend!2 points
-
Could you please show the code you are using to access your custom image fields in your template file, and explain what error/problem happens when you try to do this? I might be misunderstanding but it seems like you are getting hung up on the inputfield names in Page Edit when really you don't have to worry about these. You just access the custom fields from the Pageimage object using the names of the fields that you added to the template. So if you added the "title" field to the template that defines your custom image fields you access that field as $image->title, where $image is the Pageimage object from your image field. The relevant part of the introductory blog post is:2 points
-
ConnectPageFields Allows the connecting of two related Page fields so that changing one updates the other. Purpose of module An example: suppose your website is about movies. You have a template "movie" with Page field "actors". For each movie you add the actors that appear in the movie. All good, but what if you want to find results like... the 10 actors who have appeared in the most movies actors who haven't appeared in any movies since 1990 You cannot retrieve these pages with a single efficient $pages->find() query, and must load a large PageArray into memory in order to iterate or filter it. For the sake of making these types of queries more efficient you could structure your templates/fields so that movies are added to actors instead, but this may be a less comfortable workflow and can run into equivalent problems (e.g. "find the 10 movies with the largest cast"). The solution is to have a two-way relationship so that movie pages have an "actors" Page field and actor pages have a "movies" Page field. This module will keep these two Page fields in sync so that adding "Ryan Gosling" to "Drive" automatically adds "Drive" to "Ryan Gosling". Also, you can select the same Page field in both Page field A and Page field B. For example, create a "Related" Page field for related pages. Choose "Related" for both fields in a pair in the module config. Now when you add "Orange buffoon" to Related for "Donald Trump", "Donald Trump" is automatically added to Related for "Orange buffoon". Usage Install the ConnectPageFields module. If you haven't already done so, create the two Page fields you want to connect and add them to templates. In the module config select the two Page fields in a "Connected field pair" row as Page field A and Page field B. You can add rows as needed using the "Add another row" button. Troubleshooting Make sure you have set the "Selectable Pages" settings for each Page field correctly: The settings for Page field A should allow pages using the template(s) that Page field B has been added to. The settings for Page field B should allow pages using the template(s) that Page field A has been added to. http://modules.processwire.com/modules/connect-page-fields/ https://github.com/Toutouwai/ConnectPageFields Module config: Demo showing how changing one Page field updates the other:1 point
-
1 point
-
It depends on your needs. If you want something quick with ability to manage blog posts in one place, then go for it. Alternatively, for new sites, @BitPoet created blog site profiles off the Blog Module. Another alternatively is to roll out something on your own if all you need is pages, e.g. with a template 'post' and a few categories. Depends on how much time you got and the experience you are after, as well as your coding skills, to some extent. The site profiles1 point
-
Line 437: <?php namespace ProcessWire; $content = $page->body;?> Just delete the "namespace ProcessWire" ?1 point
-
@horst ...Wow!! I had no idea and I've been using PW since about forever. thank you!1 point
-
You first must enable / allow the upload and processing of zip files in the images inputfield settings screen!1 point
-
@Roych, you should probably report the issue at the module's support board, so that the author gets notified: .1 point
-
As a workaround on servers that behave like that, you may wrap your images into a single ZIP archive for a single file upload.1 point
-
Hello friends, I participated in a project that needed lots of relationships between fields and pages. and I struggled on how to arquitech such complex scenario. So I figure out and documented this little idea. A CSS like language for data relationships. https://github.com/joyofpw/solidwire Hope you find it useful and all ideas and contributions are welcome ? Thanks.1 point
-
If you can't put the iframe in a template (as @kongondo suggests) and need it in a CKEditor field, it'd be worth considering (if you haven't already) the Hanna Code module.1 point
-
Hi @teppo Thx, it made click in my head ? I'll write about my journey and come up with suggestions at the end of the post: First try: <body> <?php if($page->is(['template' => [GemeindeGuru::tpl_teaser]])) { echo $partials->{GemeindeGuru::tpl_teaser}->body; } else { echo Wireframe::component("Header"); echo Wireframe::component("Search"); echo Wireframe::component("Slider"); } ?> </body> Nice, this already works ? 2nd try: <body> <?php switch($page->template->name) { case GemeindeGuru::tpl_teaser: echo $partials->{GemeindeGuru::tpl_teaser}->body; break; default: echo Wireframe::component("Header"); echo Wireframe::component("Search"); echo Wireframe::component("Slider"); break; } ?> </body> A lot better imho, but what if I wanted to add a custom 404 design? I'd need to duplicate the switch case: <body> <?php switch($page->template->name) { case GemeindeGuru::tpl_teaser: echo $partials->{GemeindeGuru::tpl_teaser}->body; break; case GemeindeGuru::tpl_404: echo $partials->{GemeindeGuru::tpl_404}->body; break; default: echo Wireframe::component("Header"); echo Wireframe::component("Search"); echo Wireframe::component("Slider"); break; } ?> </body> Not bad as well, but I wanted something better ? Next try: <body> <?php $body = $partials->{$page->template->name}->body; if($body) echo $body; else { echo Wireframe::component("Header"); echo Wireframe::component("Search"); echo Wireframe::component("Slider"); } ?> </body> ProcessWire\WireException Partial file not found: C:/laragon/www/kaumberg/site/templates/partials/home.php Fast-forward: I did some more testing and realized that I was actually mixing up partials and placeholders! The final (and great) version looks like this: // layouts/default.php <body> <?php if($placeholders->body) echo $placeholders->body; else { echo Wireframe::component("Header"); echo Wireframe::component("Search"); echo Wireframe::component("Slider"); } ?> </body> // views/home/body.php echo Wireframe::component("Header"); echo Wireframe::component("Search"); echo Wireframe::component("Slider"); echo Wireframe::component("News"); echo Wireframe::component("Contact"); echo Wireframe::component("Footer"); // views/teaser/body.php <div class='uk-height-viewport uk-flex uk-flex-center uk-flex-middle'> <div class='uk-text-center'> <div><img src='mylogo.svg' class='logo'></div> <div class='teaser'><?= $page->title ?></div> </div> </div> This simply means: If the view file /views/[template]/body.php exists, render it, otherwise render the default sections. That should make it really easy to create custom layouts for some templates whily still having all other assets and scripts in place (like seo, styles, analytics etc). I think I'm happy with this setup ? I realized, that I was missing the link between $placeholders->foo and /views/[template]/foo.php I read the docs again and I saw that this information IS already there ? My personal addition to this sentence: If you know where you have to look ? This illustration makes a lot of sense now. But I didn't get it quickly I have to admit. Here's my conclusion and suggestion: IMHO your docs are great explaining "how it works", but they could be improved in regards of "how I can work with it". In my experience this can make a huge difference in the experience of someone looking at it the first time. It's basically what you said earlier: Exactly that! I think if the docs where split into two sections, the first being "quickstart" and the second being what you already have, that would be brilliant. I know that what I'm asking for is a lot of work, but if we (you ?) want to build some kind of standard, it should be as easy as possible for everybody to adopt to it ? Finally I've got a question/request that you have to review from a technical point of view. As stated above, it took some time for me to grasp this easy concept: $placeholder->default loads /views/MyTemplate/default.php $placeholder->head loads /views/MyTemplate/head.php If you look at the rest of wireframe this becomes quite obvious: echo Wireframe::component("Header") --> /components/Header/default.php echo $partials->menu->top() --> /partials/menu/top.php echo $placeholders->body --> /views/[template]/body.php This really confused me when starting with wireframe! I'd expect placeholders to live in /placeholders or, the other way round, the variable to be called $views echo Wireframe::component("Header") --> /components/Header/default.php echo $partials->menu->top() --> /partials/menu/top.php echo $views->body --> /views/[template]/body.php These 3 lines would make it obvious what options you have in your layout file. But maybe that's because I've already a better understanding of what's going on, so maybe other newcomers could comment on this? What I was really missing was a guide that helps me go through a typical web project: You got a design, you got a PW installation, you installed Wireframe, what then? Now after 2 days of testing this was the way that worked great for me (so far): Set wireframe as alternate template file for all templates that should be handles via wieframe (for this walkthrough we take home.php and basic-page.php of the default site profile). Create a default layout: /layouts/default.php <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title><?= $page->title ?></title> </head> <body> <h1>I am the default layout</h1> </body> </html> Open the root page of your site and the 404 page --> both should display the default layout We want a common page header and footer that is displayed on all templates. Shared markup with little to none business logic is best placed in a partial. Create 2 files: /partials/header.php <div style='border: 1px solid blue; padding: 20px;'>I am the page header</div> /partials/footer.php <div style='border: 1px solid red; padding: 20px;'>I am the page footer</div> Now you can output these partials in your layout's body: <body> <?= $partials->header ?> <h1>I am the default layout</h1> <?= $partials->footer ?> </body> What if we wanted a slider on the frontpage? And ONLY on the frontpage? That's what view files are for and they are accessed via $placeholders. (Don't you think that this is really misleading?) /views/home/slider.php <div style='padding: 20px; border: green;'>I am a SLIDER</div> And add it to the layout: <body> <?= $partials->header ?> <?= $placeholders->slider ?> <h1>I am the default layout</h1> <?= $partials->footer ?> </body> Now open both pages on the frontend and you'll see that the slider does only show up on pages having the [home] template. That magic is simply applied via file structure /views/[template]/slider.php Ok now we want to add the current local time of the user visiting the page. That will obviously need some PHP logic and as we want to separate concerns as much as possible we do NOT want to put that code into a partial. Instead, we create a component that consists of business locig (a PHP class extending \Wireframe\Component and a view file that is responsible for the output. <?php namespace Wireframe\Component; class LocalTime extends \Wireframe\Component { public function render() { return "<div> Your timezone: <strong>XXX</strong><br> Your local time: <strong>XXX</strong> </div>"; } } And add it after the footer of our layout: <body> <?= $partials->header ?> <?= $placeholders->slider ?> <h1>I am the default layout</h1> <?= $partials->footer ?> <?= Wireframe::component('LocalTime') ?> </body> Nice! We get the output at the bottom of all of our pages! We know that doing lots of output within php classes is ugly, so we refactor our setup a little and adopt another concept of wireframe: View files for components: /components/LocalTime.php <?php namespace Wireframe\Component; class LocalTime extends \Wireframe\Component { } /components/LocalTime/default.php <div> Your timezone: <strong>XXX</strong><br> Your local time: <strong>XXX</strong> </div> Of course, this is no a lot better for now, but it is a lot more future proof! Check your frontend, it should still display the same markup for your local time. Now we can add the "business logic" to our component: <?php namespace Wireframe\Component; use ProcessWire\WireData; use ProcessWire\WireHttp; class LocalTime extends \Wireframe\Component { public function __construct() { $http = new WireHttp(); $data = new WireData(); $data->setArray($http->getJSON("http://worldtimeapi.org/api/ip")); $this->timezone = $data->timezone; $this->unix = $data->unixtime; } } And update our view to output the component properties (note how they magically become available as view variables!): <div> Your timezone: <strong><?= $timezone ?></strong><br> Your local time: <strong><?= $unix ?></strong> </div> Go to your frontend and see the magic! Your timezone: Europe/Vienna Your local time: 1598007959 What, you don't want a unix timestamp? Ok... let's provide a formatter method so that you can easily output the time in different formats: <?php namespace Wireframe\Component; use ProcessWire\WireData; use ProcessWire\WireHttp; class LocalTime extends \Wireframe\Component { public function __construct() { $http = new WireHttp(); $data = new WireData(); $data->setArray($http->getJSON("http://worldtimeapi.org/api/ip")); $this->timezone = $data->timezone; $this->unix = $data->unixtime; } public function date($format = "d.m.Y H:i:s") { return date($format, $this->unix); } } @teppo now I got stuck again ? How would I display that formatted date in my view file for this component? Or would I need to provide the date format as variable for the constructor ( echo Wireframe::component('LocalTime', ['format' => 'Y-m-d']); ) PS: Regarding the last question. Controllers do expose methods to views. Is there a similar concept for components? I did implement the local time as component because this does not belong to a single template (then it would have been a controller, right?)1 point
-
There are three parts from my perspective: Filesystem => Have wordpress and processwire in separate folders URLs => You need to find a (likely .htaccess) way to move certain paths away from wordpress to processwire Migration => How do you deal with migration needs to be clarified. Can you have downtime between taking wordpress content down and pushing the pw replacement up? How much downtime is ok? All of those are solvable problems. The complexity of the last part mostly depends on how much downtime you're allowed. The less time the more automated things need to be.1 point
-
These topics will give you some helpful hints: Large in terms of assets or database? Either way, it's best to make a local copy of the WordPress site and do the migration to ProcessWire locally first.1 point
-
Hey I updated the spec. Now it's more like Pseudocode than a whole language in itself. The idea is just to communicate better and make plans before building or thinkering with the fields. Example for this small project that I'm building https://github.com/NinjasCL/chileanbirds-api @document { @wire { @templates { home: template().options({@once, @strong, @root}), notfound: template().options({@once, @childless}).description("Used in 404 errors"), birds: template().options({@strong}).family({ parents: ["home"], children: ["birds-item"] }), birds-item: template().options({@childless}).family({ parents: ["birds"] }) }, @fields { title: text().options({@i18n, @global}), body: textarea().options({@i18n}), uid: text(), href: url(), file: files().max(1), migration: checkbox(), dimorfism: checkbox(), size: text(), order: text(), species: text(), image: images().max(1).mediatypes([@jpg, @png, @svg]), images: images().mediatypes([@jpg, @png, @svg]), value: text().options({@i18n}), habitat: textarea() .description("Stores the habitat property") .options({@i18n}), didyouknow: textarea() .description("Stores the did you know property") .options({@i18n}), iucn: pagetable() .max(1) .fields({ body: body().description("Stores the iucn description"), value: value().description("Stores the iucn value") }), audio: pagetable() .max(1) .fields({ file, title.label("Author") }), map: pagetable() .max(1) .fields({ image, value }), }, @pages { + "Home" (template:home) @many -> { + "Birds" (template:birds) @many -> { + "Bird 1" (template:birds-item, fields: { title: title().label("Names"), body: body().label("Description").description("Stores the bird description"), images, habitat, didyouknow, iucn, audio, map, species, migration, dimorfism }) } // /birds } // /home } // /pages } // /wire } // /document1 point
-
Hey everyone, there is a new module AppApi available which is based on my original RestApi module but has some additional features which are really cool! I have been struggling to find time for the RestApi module over the course of the last 1+ year, so I'm really glad @Sebi is stepping in and doing work in this area. I think it would be a good idea, to see AppApi as a successor of the RestApi module and put all focus and development efforts there. What do you guys think?1 point
-
@gornycreative, thanks for the report. I was able to reproduce that in PW 3.0.161 when the Connect Page Fields module config has been saved with one or more of the field selects left empty. Should be fixed in v0.3.1.1 point
-
This week I was back to focusing on the core and got quite a lot done. A lot of GitHub issue reports were resolved, plus several minor tweaks and additions were made in 3.0.156 as well. But the biggest update was the addition of the $pages->parents() API, which is something that I think you’ll likely have zero use for (and why I’m not putting it into a blog post) but something that the core itself will use quite a bit, and is a really nice improvement for the system and its scalability. So if you don’t mind some technical reading, read on. Whenever you call a $page->find() method ($page, not $pages) or use a “has_parent=“ in a selector, ProcessWire joins in a special table for the purpose called pages_parents. It uses this table to keep track of family relationships that aren’t otherwise apparent. For instance, let’s say we have page “g” that lives at path /a/b/c/d/e/f/g/. Page “g” only knows that it has page “f” as its parent. It doesn’t know that page “e” is its grandparent unless or until page “f” is loaded. Once “f” is loaded, then “f” can reveal its parent “e”. It works the same for every relationship down to the root parent “a”. So the pages are like a linked list or blockchain of sorts, where only 1 relationship forward or backward is known per page. The “pages_parents” table fills in this gap, enabling PW to quickly identify these relationships without having to load all the pages in the family. This is particularly useful in performing find() operations that you want to limit to a branch started by a particular parent. It’s the reason why we have both $pages->find() that searches the entire site, and $page->find() that limits the search within the branch started by $page. I haven’t paid much attention to the code behind this pages_parents table because it generally just worked, needing little attention. But I came across a couple of cases where the data in the table wasn’t fully accurate with the page tree, without a clear reason why. Then I became aware of one large scale case from a PW user where it was a huge bottleneck. It involved a large site (250k+ pages) and a recursive clone operation that appeared to involve hundreds of pages. But that operation was taking an unreasonable 10 minutes, so something wasn’t right. It came down to something going on with the pages_parents table. Once I dove into trying to figure out what was going on, I realized that if I was to have any chance of keeping track of it, we needed a dedicated API for managing these relationships and the table that keeps track of them. So that’s what got a lot of attention this week. While still testing here, it does appear initially that the 10 minute clone time has gone down to a few seconds, and everything about this relationship management is now rewritten, optimized and significantly improved. It was a lot of work, but absolutely worth it for PW. Rebuilding the entire table from scratch now takes between 2-3 seconds on a site with 250k pages and 150k relationships. The new API can be accessed from $pages->parents(). This API is really useful to the work that I do here (maintaining the core) but I’ll be honest, it’s probably not useful to most others, so I won’t go into the details here, other than to say I’m happy with it. But if you are interested, there are methods finding all the parents in a site, or a particular branch, and methods for rebuilding the pages_parents table, among others. Maybe more will be added to it later that actually would be useful in the public API, but for now I’ll likely leave it out of our public API docs. The $sanitizer->selectorValue() method also got a full rewrite this week (actually, one the last few weeks). It’s now quite a bit more comprehensive and configurable (see the new method options). The previous version was just fine, and actually still remains — you can use it by specifying [ ‘version’ => 1 ] in the $options argument to the selectorValue() method. But the new version is coded better and covers more edge cases, plus provides a lot more configurability for the times when you need it.1 point