Leaderboard
Popular Content
Showing content with the highest reputation on 03/09/2015 in all areas
-
Thanks horst! I've watched a ps tutorial and was able to recover some bits of personality.4 points
-
Update: version 0.0.3 Updated original post. Now in modules directory: Option to allow Markup in menu items as per this request (superusers only) [note: although values are sanitized via HTML Purifier, it is your responsibility to ensure correct HTML input; also, if you previously allowed HTML markup but the menu was subsequently edited by non-superusers, your HTML will be stripped off] Added more options to MarkupMenuBuilder render() method - first, last classes, etc.(see Read Me) and... Added/enhanced wrapper_list_type and list_type options. The former allows you to specify menu items outer wrapper tag, e.g. <ol>, <div>, <nav>, etc....The latter allows you to specify alternatives to <li>, e.g. <span>. Note, if no list_type is specified and you override the default by 'list_type' => '' in your options, then menu items css classes/IDs are applied to the <a> tag instead. Updated code to ensure menu settings saved by superusers remain even for non-superusers (e.g. menu maxLevels, selectable pages, etc.) Note: Had an extra jquery.asmselect-mb.js in the root folder. I have deleted this in the repository. Note, it seems PageAutocomplete will not allow non-superusers to select pages unless they have a page-edit permission as well. Could somebody confirm/clarify? Thanks.4 points
-
Check out this post from Ryan where he explains how to use: ProcessPageEdit::buildFormSettings https://processwire.com/talk/topic/510-name-field-in-content-instead-in-settings-tab/?p=48303 points
-
New version 0.0.9 includes fixes for deactivated save message setting. On line :343 I removed the exclamation mark (!) and I corrected the instance to compare with. This has to be Page instead of Pages because findOne returns a single page (because of this double bug the messages has been stored). I included a second check here just to make sure that save messages is on. I also included this additional check on line :396.3 points
-
Menu Builder As of 29 December 2017 ProcessWire versions earlier than 3.x are not supported Modules Directory Project Page Read Me (How to install, use, etc..) For highly customisable menus, please see this post. If you want a navigation that mirrors your ProcessWire page tree, the system allows you to easily create recursive menus using either vanilla PHP or Soma's great MarkupSimpleNavigation. In some cases, however, you may wish to create menus that: 1. Do not mirror you site's page tree (hirarchies and ancestry); and 2. You can add custom links (external to your site) to. That is primarily where Menu Builder comes in. It is also helpful if you: 3. Prefer creating menus via drag and drop 4. Have a need for menus (or other listings) that will be changing regularly or that you want to allow your admin users to edit. The issue of custom menus is not new here in the forums. The difference is that this module allows you to easily create such menus via drag and drop in the Admin. Actually, you can even use it to just create some list if you wanted to. In the backend, the module uses the jQueryUI plugin nestedSortable by Manuele J Sarfatti for the drag and drop and is inspired in part by the WP Custom Menu feature. Please read the Read Me completely before using this module. For Complex or highly-customised menus, it is recommended to use the getMenuItems() method as detailed in this post. Features Ability to create menus that do not mirror your ProcessWire Page Tree hierarchy/structure Menus can contain both ProcessWire pages and custom links Create menu hierarchies and nesting via drag and drop Easily add CSS IDs and Classes to each menu item on creating the menu items (both custom and from ProcessWire pages) or post creation. Optionally set custom links to open in a new tab Change menu item titles built from ProcessWire pages (without affecting the original page). E.g. if you have a page titled 'About Us' but you want the menu item title to be 'About' Readily view the structure and settings for each menu item Menus stored as pages (note: just the menu, not the items!) Menu items stored as JSON in a field in the menu pages (empty values not stored) Add menu items from ProcessWire pages using page fields (option to choose between PageAutocomplete and AsmSelect [default]) or a Selector (e.g. template=basic-page, limit=20, sort=title). For page fields, you can specify a selector to return only those specified pages for selection in the page field (i.e. asm and autocomplete) For superusers, optionally allow markup in your menu titles, e.g. <span>About</span> Menu settings for nestedSortable - e.g. maxLevels (limit nesting levels) Advanced features (e.g. add pages via selector, menu settings) currently permissible to superadmins only (may change to be permission-based) Delete single or all menu items without deleting the menu itself Lock down menus for editing Highly configurable MarkupMenuBuilder - e.g. can pass menu id, title, name or array to render(); Passing an array means you can conditionally manipulate it before rendering, e.g. make certain menu branches visible only to certain users [the code is up to you!] Optionally grab menu items only (as a Menu object WireArray or a normal array) and use your own code to create custom highly complex menus to meet any need. More... In the backend, ProcessMenuBuilder does the menu creation. For the frontend, menus are displayed using MarkupMenuBuilder. Credits In this module's infancy (way back!), I wanted to know more about ProcessWire modules as well as improve my PHP skills. As they say, what better way to learn than to actually create something? So, I developed this module (instead of writing PW tutorials as promised, tsk, tsk, naughty, naughty!) in my own summer of code . Props to Wanze, Soma, Pete, Antti and Ryan whose modules I studied (read copied ) to help in my module development and to Teppo for his wonderful write-up on the "Anatomy of fields in ProcessWire" that vastly improved my knowledge and understanding of how PW works. Diogo and marcus for idea about using pages (rather than a custom db table), onjegolders for his helpful UI comments, Martijn Geerts, OrganizedFellow, dazzyweb and Mike Anthony for 'pushing me' to complete this module and netcarver for help with the code. Screens2 points
-
Just checked in a first beta version of a new module I'm working on. Feel free to test out and see what's up with it. Pollino (beta) A simple poll module for ProcessWire This module makes it simple to setup polls for your website. It is based on a simple page setup to create the polls. So each poll is a page, and its children are the answers. Pollino will create the templates and a PollinoPolls page in the root to start with. You can add fields to the templates as you wish and later use hooks to modify the output of the poll. This can be useful, for example, to use images as options or just some custom markup etc. It provides some API to render the poll form and the result. These methods are hookable and it's easy to customize the output if needed. It can be rendered on any page and even multiple on the same page. Pollino takes care of saving the votes and preventing multiple votes. It comes with some configuration settings to choose what method to use to prevent from multiple votings: using cookie and an expire time or by IP and optionally also the UserAgent with and expire time or by logged in User Pollino isn't 100% plug'n'play but it provides a solid foundation and comes with some premade theme and output for you to start. It takes care of the boring stuff and lets you concentrate on the front-end stuff and styling. That's what matters after all. It does support multilanguage, as all strings are translatable in the module. Also since it's using simple pages and templates you're free to add or change fields to make its output multilanguage without much hassle. ----- Read more and download https://github.com/somatonic/Pollino Online Demo I setup a little demo here to see using https://lightning.pw http://titanium-x77.lightningpw.com/ Have fun.2 points
-
This is called "Code Internationalization". There are several ways to do this, also for plurals and context. Read more in the multi language support docs.2 points
-
$username = $sanitizer->username($input->post->username); $pass = $input->post->pass; $u = $users->get($username); if($u->id && $u->tmp_pass && $u->tmp_pass === $pass) { ... } $u = $session->login($username, $pass); This is where you try to find the user by the submitted name and then check if you got one by seeing whether $u->id is 0. So what you want to do is, if you didn’t find a user with that name, assume that an e-mail address was submitted instead, and check that: // Get user by the input name $username = $sanitizer->username($input->post->username); $pass = $input->post->pass; // (password is irrelevant for this, so we don't have to repeat it below) $u = $users->get($username); if ($u->id == 0) { // no user was found, so let's do the same thing, only // this time, treating the input as a mail address $usermail = $sanitizer->email($input->post->username); $u = $users->get("email={$usermail}"); // select by matching the mail field } // Now do the same checks as before and see if // the user was found after all if($u->id && $u->tmp_pass && $u->tmp_pass === $pass) { ... } // log in with $u->name, the name of the matched // account, instead of $username. If only the NullUser // was found, this will be an empty string. $u = $session->login($u->name, $pass);2 points
-
Wow thanks for using this code and thanks for improving it. Well A technique I use for avoid using sessions is a token and send that token with every request. I previously created a "token" field inside the user page. So when the user logs in a token (normally sha1) is created and saved to his token field. So for example you made a login request and the backend responds with this data. { "status" : "ok", "data" : { "id" : 142, "username" : "clsource", "name: "Camilo", "token": "abc123" } } And then you need to update its name sending a PUT request to the corresponding endpoint PUT { name : Camilo C. } https://api.example.com/v1/users/{token}/profile/name in this case {token} would be "abc123" As the token is saved in the user page you can check if the user exists quering by the token $usr = $users->get("token=$token"); As you may notice the token gives you access for seeing and modifying the user, so its better to keep it safe. A rule of thumb is creating a token with a 1 hour valid time. And when the token is invalid you should request a new one in background. Hope it helps2 points
-
This is what you are looking for: $this->pages->addHookAfter('added', $this, 'hookAdded'); public function hookAdded(HookEvent $event){ $newPage = $event->arguments[0]; $this->message('it worked here is the id of your new page:'.$newPage->id); // Yes, this will work }2 points
-
FieldtypeSelectFile & InputfieldSelectFile Inputfield Select File is an Inputfield & Fieldtype to select a single file or folder and stores the name and / or use the selected file as page template. The last option enables the editor to use multiple views for a page, depending on the selected template. Settings The folder containing the files and/or folders.A relative path relative to the /site/templates/ folder. Hide file extensions Hide files Hide folders Natural Sort (Select options)Sort files and folders in natural ordering (PHP >= 5.4.0) Change Page Template Just before the Page::loaded event the selected file is set as template file for the page. This setting can only be applied once per a page and folders are exluded from the select inputfield. Note that a page with no associated template file will render with the selected file. When to use ? Let editors select a file and base your own logic upon this. With the change page template setting you're able to use the selected file as template file. This could reduce the amount of normal templates needed and let editors choose how the page get rendered. There are plenty of use cases for this Inputfield. In the examples I call the field selected_file. // let the editor choose a CSS file $config->styles->append($config->urls->templates . "styles/" . $page->selected_file); /** * advanced usage example * * You need multiple ways to render your markup. Let the site editor choose which * file the page need to render. * */ $tpl = new TemplateFile($config->paths->templates . "includes/" . $page->selected_file); $tpl->set('current', $page); $markup = $tpl->render(); (It could be a real good companion with InputfieldSelector) Download at GitHub Modules directory1 point
-
ProcessWire LazyCron Module This core module provides hooks that are automatically executed at various intervals. It is called 'lazy' because it's triggered by a pageview, so the interval is guaranteed to be at least the time requested (and maybe more) rather than exactly the time requested. The more pageviews your site gets, the closer it is. This is fine for most cases, but if you need it to be fully accurate I'll describe how you can make it not-lazy a little further down. How to download and install This was built as a core module for ProcessWire 2.1 (development version), so you need to grab the latest commit of the development version and you will have the module. This module is not compatible with PW 2.0 (stable version). ProcessWire 2.1 is at: https://github.com/ryancramerdesign/P21 If you already have the latest commit of ProcessWire 2.1, go to your modules menu, click "check for new modules" at the bottom, and then click the "install" button for the LazyCron module. LazyCron is now installed and ready to be used by other modules or via the API. Hookable time intervals These are the function names you can hook from LazyCron. The function names describe the time intervals they provide. If you think I'm missing any important time intervals, please let me know and I can add more. every30Seconds everyMinute every2Minutes every3Minutes every4Minutes every5Minutes every10Minutes every15Minutes every30Minutes every45Minutes everyHour every2Hours every4Hours every6Hours every12Hours everyDay every2Days every4Days everyWeek every2Weeks every4Weeks How to use it This module is mainly of use inside the API and to other modules. You hook one of the named intervals mentioned in the list above, and the function you provide will be executed at approximately that time interval. Here's how you do it, both from a class (module) and outside of one: Usage in a class/module <?php // initialize the hook in your AutoLoad module public function init() { $this->addHook('LazyCron::every30Minutes', $this, 'myFunc'); } // the function you want executed every 30 minutes public function myFunc(HookEvent $e) { echo "30 Minutes have passed!"; } Procedural usage (like in a template or elsewhere in the API): <?php // create your hook function function myHook(HookEvent $e) { echo "30 Minutes have passed!"; } // add a hook to your function: wire()->addHook('LazyCron::every30Minutes', null, 'myFunc'); Arguments provided to the hooks If desired, you can retrieve the number of seconds that have actually elapsed since your hooked function was last called: <?php function myHook(HookEvent $e) { $seconds = $e->arguments[0]; echo "30 Minutes have passed! (actual seconds were: $seconds)"; } Note that in production usage, you probably wouldn't want to echo anything from these hooks because they are executed after the pageview was already delivered. So while you can directly echo output from these, it's probably not that useful (other than for demonstration purposes, like this). How it works When installed, LazyCron hooks into ProcessWire's ProcessPageView::finished() method. This ensures that the scheduled tasks are executed after the pageview has already been delivered rather than before or during it. This hopefully avoids any perceived slowdown if the scheduled tasks take time. LazyCron provides a bunch of hooks that anything else can hook into. These functions are outlined in the section above titled "Hookable time intervals." These functions are called at the interval specified in their name. LazyCron simply uses ProcessWire's existing hook system. So when you hook into any one of these functions, your hook will also be executed at that same time interval. LazyCron hooks are only executed during pageviews that are delivered by ProcessWire. They are not executed when using ProcessWire's API from other scripts. How to make it not-lazy In most cases, the way that LazyCron works out of the box is just fine. But if your need requires assurance that the module will always execute at exactly the interval you need (rather than possibly later), you need to setup a real cron job to trigger a pageview in your site. So if you needed accuracy to 1 minute, you'd setup a cron job to execute every one minute, and pull a page from the site. There are any number of ways you could pull a page from your site, but here is one using wget: wget --quiet --no-cache -O - http://www.your-site.com > /dev/null That command basically says to pull a page from the site, don't tell us anything, don't cache the request, and discard any output.1 point
-
Update: Such a module already exists: kongondo's Module Menu Builder When you look at other CMS such as Wordpress or Drupal, no matter what you think of them, they share a feature that would suit ProcessWire very well: The seperation of menus and content structure via backend. While this is kind of possible programmatically, in the template via MarkupSimpleNavigation, both Drupal and Wordpress have their take on a “Menu Creator” in their backends, where editors could compose their menus, consisting of references to pages or external links: While not part of these screenshots, both visual editors provide the option to nest menu items. As you maybe have noticed, I’m kind of experimenting with modules right now And I’m eager to learn more about module creation and the inner architecture of PW. But: the longer I thought about this “Visual Menu Module” idea the more I noticed it’s still far ahead of my abilities. In the following I’ll write down how I would approach such a module. It would be really helpful for my understanding of modules and programming itself if you can give me feedback… concerning if such a module worthwhile - or I’m misjudging the demand if it’s possible the way it is described if there are some quirks or factors I haven’t thought of Thanks in advance! And here we go: The module itself would establish a process, let’s call it ProcessCustomMenus. During module installation, the plugin would create a subpage of “Setup” within the admin branch of ProcessWire backend main navigation, “Custom Menus”. Navigating on “Custom Menus”, there would be a possibility to list, create and edit the custom menus. Technically, all custom menus would be child pages of said new “Custom Menus” page. Then, three new templates would be necessary: customMenu, customMenu_pageReference and customMenu_externalReference. customMenu is used for direct children of “Custom Menu”, while the other to templates will serve as items on a particular menu. Phew, hard to describe. Here’s a screenshot: For starters, customMenu_pageReference and customMenu_externalReference could have only two fields each: title and pageArray (for internal references), or title and text (for external references). Later one could try to save the menu item nesting state in an additional field. On the template side, a named custom menu could be output as easy as: $modules->get(“MarkupCustomMenus”)->renderCustomMenu(“named_menu”, $options) …where $options could be an array full of further markup config. Of course, the content of “Custom Menus” in backend should be as easy as in the Drupal and Wordpress examples above, and appareled with all the interface magic jQuery UI has to offer, and also maybe ASM select (though I’m not sure how to achieve nesting with ASM) or other ProcessWire backend concepts. So, what do you think?1 point
-
1 point
-
I am sorry @lisandi but odoo.com just cannot be taken seriously. I would never dream or risking implementing it for any clients or my own operations. http://www.sorryopenerp.com/ and other millions of horror stories all over the web. It is famous for all the wrong reasons. But it does some (1 or 2) pretty things. Now, ProcessWire is very different in most aspects. It is not built by an army of beginner developers in a marketing driven frenzy creating the biggest mess of code the world has seen, like odoo (openerp) is. PW seems very methodically, slowly and steadily going about its development roadmap. Extremely well coded, well thought out and with practically no bugs. Secure and fast. So a massive effort like website builder/theming system will have to be separate for sure and to be a respectable match to its core it will probably take a long time to be designed and coded in a fashion that would make sense for it to be a worthy addition to PW ecosystem. I am sure it will happen at some point but not in the next few weeks... some good ideas/thoughts and suggestions floating about from everyone though. Nico - I am going to play around with your setup and will report back what I find/think. Very nice to see something being done in this direction.1 point
-
Most of the time a form or is a one time setup, so I just use the following and translate it with processwires on board language management. <label for="name"><?php echo __("Name"); ?></label> <input name="name" …> For the navigation, why don't you use a simple multilanguage textfield for each page's title?1 point
-
This is really great, it works like a charm. By the way I've changed dl.accordion > dt > a.open:before { content: '-'; } to dl.accordion > dt > a.open:after { content: '-'; } In my case the minus-sign was jumping before the panel headline, probably related to my Css-Bootstrap setup. Thanks so much renobird! Edit: In your demo-fiddle it's also 'a.open:after', maybe this is a tiny bug in the master on line 35.1 point
-
About handling the display of images from a twitter feed, I did this today by adding code inside MarkupTwitterFeed.module. I post it here in case it could help other pw users having the same needs. // To be added in public function renderItem, before: $out = $options['listItemOpen']; if (isset($item['entities']['media'])) { $mediaUrlField = wire('config')->https ? 'media_url_https' : 'media_url'; foreach($item['entities']['media'] as $m) { if($options['listItemPhoto']) { $photoHtml = str_replace(array('{src}', '{title}'), array(wire('sanitizer')->entities($m[$mediaUrlField]), wire('sanitizer')->entities($m['display_url'])), $options['listItemPhoto']); $text = preg_replace('!' . preg_quote($m['url'], '!') . '(\b|$)!i', $photoHtml, $text); } else { // Hide the media reference in the text $text = preg_replace('!' . preg_quote($m['url'], '!') . '(\b|$)!i', '', $text); } } } where $options['listItemPhoto'] contains the html for a Twitter photo. For example, if you simply want to display the image, you can set it to: $options['listItemPhoto'] = "<img src='{src}' title='{title}'>"; This will be rendered like in this screenshot:1 point
-
1 point
-
Bump version to 0.5.3 Replaced fhOption "callback" with "callbackPrepare" (executed before field added to form) and callbackProcess (executed after form process to do additional processing). Examples callbackProcess // You can use "$field" (current field object) and "wire()") $fhOptions['title']['callbackProcess'] = function ($field) { // Modify a form field (wire('fh') is the FormHelper instance / object which is added to "wire()") wire('fh')->form->get('body')->error('CALLBACK-ERROR to another field...'); // modify current field by callback $field->error("CALLBACK {$field->name} ERROR"); }; callbackPrepare $fhOptions['title']['callbackPrepare'] = function ($field) { // replace field value (executed after clear / value / unformatted feature) $field->value = "TEST PREPARE CALLBACK"; // or skip the field just before it will be added to the form... wire('fh')->fhOptions($field, array('skip' => true)); }; FormHelper creates forms by Page, Template, Fields, Inputfields, InputfieldForm (fields) or an array with field data with 371 lines of code. Maybe it could be separated into a basic form process module and an sub module to handle additional features (Template based form with file upload, CKEditor image plugin, ...) but I don't know if it should be necessary.1 point
-
@Qurus On that line you are only calling one image....specifically the first one. If you want to output several images you would have to loop through them. Have a read here. something like $out = ''; foreach($post->blog_images as $img) { $out .= '<img src="' . $img->url . '" alt="img">'; } //then output $out later on down...within your <div class="img"....... @creativejay...get back to you later.....1 point
-
Well actually, that hook and function I put together also works perfectly on the front-end, as well as the admin - I assumed you wanted both. All you need to go is use that as part of a module and then be sure to load the module in your login.php file for it to also work on the front-end. But if you only need front-end, then Jan Romero's version is simpler.1 point
-
You rock! My preference is to have all traffic go to the non-www URL, so I've taken the custom rule I added at the end and moved it to the section you pointed out in your response: Now I guess I'll just keep watching it to see if the ugly URLs start to dwindle. Many many thanks!1 point
-
Yeah, that explains it perfectly. In fact, if I open www.thesharktankproducts.com/products/ I end up at index.php?it=products/. The problem is that you're doing your own custom redirect after ProcessWire has already rewritten the URL to index.php?it=some-url You might want to take a look at ProcessWire's default .htaccess rules. There's similar www redirect there, though it works the other way around: non-www URLs are prefixed with www. The important thing is the position of this rule in the .htaccess file; you'll want to add your custom rule to similar position. Generally speaking, though, redirects like that should be placed as early as possible in the .htaccess file, since that's the most efficient way: you don't want to parse any extra rules, if there's going to be a redirect that makes it necessary to go through that very same process again anyway.1 point
-
That GET param ("it") is what ProcessWire internally uses; a request to example.com/categories/food/ gets passed from .htaccess to index.php as example.com/index.php?it=categories/food. This explains why these work, but not why they're showing up in your analytics data So far my best guess would be that, for some reason, sometimes an error happens somewhere, causing such URLs to become visible to visitors, including robots. I did notice that Google has indexed some of these URLs, so it's actually possible that this error doesn't even happen anymore. Google is quite good at holding on to indexed pages, even if there are no more links to those pages If you want to debug this further, the first question would be if you have any custom rules in your .htaccess or anything like that. Something that could clash with existing rules there? I'd also suggest taking a look at your error log files (Apache and PW) to see if anything weird shows up there related to these URLs.1 point
-
Huh. This looks like your MySQL server is reachable from everywhere in the internet, which is rarely a good idea. I usually bind only to localhost (if its running on the webserver itself), or add an iptables rule that only allows access from the servers I explicitely grant access to port 3306. For maintenance access, there's always the possibility to use ssh (MySQL CC has that built-in) to forward the connection to *nix systems, or VPN when using Windows hosts. One important measure to be able to pin-point problems quickly is to eliminate as much of the background noise as possible beforehand which port scans and distributed dictionary attacks tend to produce in the logs. Not letting these get to your services in the first place makes your logs much more meaningful. I don't think the database log will tell you much though, and the above lines just say that there was a connection attempt from a host that didn't resolve to a name. The most likely source of information would be the webserver log for that time, where you can see the real URL that was requested. If you can get that, also look out for POST requests in the timeframe in question. Mysterious error like yours can (I don't want to stir up panic there, but it would feel wrong not to mention it) sometimes also point to issues that arise by passing on unsanitized form values. The answer is likely much more less dangerous, but taking a look at that never hurts. Keep in mind that MySQL sits on the end of the food chain. The webserver log is your most important means to deduce what happened, then the application logs may give you further insight about how it happened.1 point
-
I think it's kinda like this: Page -> Pageimages (/-files) -> Pageimage (/-file), just like $page->images->eq(0). You'll find the corresponding classes in the core folder. Also having a look at FieldtypeFile and FieldtypeImage wouldn't do harm.1 point
-
Assuming that the structure of the fields is always the same (Question, Checkbox 1, Checkbox 2), I think that the approach depends on who defines the questions: the user (like a "create your own form" type of page) You (with a fixed set of questions that the user must answer on the page) In the first case, you could use the Profield Table, as suggested by nickie, or even simpler, a PageTable (which is not a Profield): You create a template called question, with the fields question_text (text field), question_answer (text field), display (checkbox field), include_in_email (checkbox field). Then you create a PageTable field questions, that uses this question template. Finally you add this questions field to the target template. This way, the user can add as many questions as he wishes on a page. The second case is more difficult. The "pure" way of doing it would be to create a field for each question's subquestion, i.e children_answer, children_display, children_email, pets_answer, pets_display, pets_email, etc. The problem is that this would create an enormous amount of fields, which becomes inefficient (see this post from Ryan). You could create your own compound Fieldtype to group the three subquestions in one field (see the Events Fieldtype/Inputfield as an example), but this would still require 50-80 different fields, which is a lot. I think that the approach that I would attempt is the following: Create a question template with fields question_text (text field, with visibility set to visible but not editable), answer_text (text field), display (checkbox field) and include_in_email (checkbox field). Create a questions PageTable field that uses this question template. Add the questions field to the target template (let's call it questionnaire). Write a module to hook after Pages::added, so that each time a questionnaire page is created, it adds the question pages under it. Here is an example of what I mean: // Inside a module public function init() { $this->addHookAfter("Pages::added", function($event) { // Activate the hook only if the added page is a questionnaire if ((string)$event->arguments(0)->template == "questionnaire" $this->hookAdded($event); }); } public function hookAdded($event) { $questionnaire = $event->arguments(0); // List of all your questions $questions = array( "Do you have children?", "Do you have pets?", "Do you have rabies?", // etc. ); foreach($questions as $q) { // Create a question page under the new questionnaire $p = new Page(); $p->template = "question"; $p->parent = $questionnaire; $p->question_text = $q; $p->save(); // Add the question page to the questions field of the questionnaire $questionnaire->questions->append($p); } // Save the questions field of the questionnaire $questionnaire->save("questions"); } You should also adjust the permissions for the question template so that a user can't delete them. Yet other approaches would be to create a custom Process module (see the Hello Process Module for an example), or to set up a custom front-end template to answer the questions instead of using the admin back-end.1 point
-
Hehe...who has rabies sometimes? Anyway, I'm not sure if you want the user to select all these additional settings for each field, or whether you want to choose it for them based on their answer to the first bit of each question? (Or is each question its own field ?? ) If it's the former (user should answer and select settings as well), maybe look at the table field? https://processwire.com/api/modules/profields/table/ So each row of the field would have 4 columns: a question, its answer, its display setting and email-inclusion setting. You would set the first field (question) and the user can provide the answer in the second, and they can also check/uncheck the third and fourth. I have tried using it once, and I think (I think) the way in which it is different from repeater is that you must specify a range of how many total rows you will need. I wanted to use it for comments so it didn't really work, but it might be just right for your case if you want a lot of input of different types but same pattern grouped together, and you know exactly how many. If you are currently creating new fields for every question, table field might help you eliminate it. Just use an additional column called "name" or something for each row, which will help you identify what your question was (for using it in the API). "children", "pets", "rabies" - just like fieldnames... If you want to show them specific fields based on their answer to "Do you have pets" etc... I'm not sure. Maybe inputfield dependencies? http://processwire.com/api/selectors/inputfield-dependencies/ Sorry, hope it helps..1 point
-
Story: https://processwire.com/talk/topic/9250-what-should-i-do-to-make-pw-to-work-with-a-reverse-proxy-cache/ After a few days of reading documentation, I got processwire to work with nginx reverse proxy cache. This is not a tutorial on how to install server software. A working nginx config copy will be posted on next reply. Ubuntu 14.04.1 x64 Nginx 1.4.6 apache 2.4.7 mysql 5.6.19 php 5.6.6 (php5-fpm) A new copy of content will be fetched instantly. A screencast of this demo1 point
-
1 point
-
nginx.conf user www-data; worker_processes 4; pid /run/nginx.pid; events { worker_connections 768; # multi_accept on; } http { sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; include /etc/nginx/mime.types; default_type application/octet-stream; include /etc/nginx/cache.conf; access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; gzip on; gzip_disable "msie6"; gzip_vary on; gzip_proxied any; gzip_comp_level 2; include /etc/nginx/conf.d/*.conf; include /etc/nginx/sites-enabled/*; } cache.conf proxy_cache_path /etc/nginx/cache levels=1:2 keys_zone=one:100m inactive=60m loader_threshold=300 loader_files=200 max_size=200m; proxy_cache_key "$scheme$proxy_host$uri$is_args$args"; /etc/nginx/sites-available/default server { listen 80 default_server; proxy_cache one; add_header X-Cache-Status $upstream_cache_status; proxy_cache_valid any 1m; proxy_cache_min_uses 3; proxy_ignore_headers Set-Cookie; proxy_ignore_headers "Cache-Control" "Expires"; root /var/www/html; index index.php index.html index.htm; server_name dev.local.net; location / { try_files $uri $uri/ /index.php?it=$uri&$args; } location ~ \.php$ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } location ~ /\. { deny all; } } A modified basic-page.php of pw installation profile <?php $lastModified=($page->last_modified); $etag = md5($page->body); $ifModifiedSince=(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? $_SERVER['HTTP_IF_MODIFIED_S INCE'] : false); $etagHeader=(isset($_SERVER['HTTP_IF_NONE_MATCH']) ? trim($_SERVER['HTTP_IF_NONE_MATCH']) : false); header("Last-Modified: ".gmdate("D, d M Y H:i:s", $lastModified)." GMT"); header("Etag: $etag"); header('Cache-Control: public'); //check if page has changed. If not, send 304 and exit if ((@strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])===$lastModified) || $etagHeader == $et ag) { header("HTTP/1.1 304 Not Modified"); exit; } //$content = "This is a demo"; // basic-page.php template file // Primary content is the page's body copy $content = $page->body; //$content .= $etag; // If the page has children, then render navigation to them under the body. // See the _func.php for the renderNav example function. if($page->hasChildren) $content .= renderNav($page->children, 0, 'summary'); // if the rootParent (section) page has more than 1 child, then render // section navigation in the sidebar if($page->rootParent->hasChildren > 1) { $sidebar = renderNav($page->rootParent, 3) . $page->sidebar; }1 point
-
Kongondo, I have replied to your PM about this.1 point
-
Yes, The verb is on the Http Request and different responses are given depending on the way you call an endpoint. If you got www.your-pw-site.com/products/ then you can have these methods GET -> list of products POST -> create a new product then if you got this particular product "computer-1" www.your-pw-site.com/products/computer-1 you can have these methods GET-> info of computer 1 PUT -> replace all the info of computer 1 PATCH -> replace specific info like name or sku of computer 1 DELETE -> removes computer 1 Now taking that as an example, you can have this way to program and endpoint in Processwire 1.- Create a template named "products" (with a file products.php in templates folder) 2.- Create a page that use that template, and creates the url www.your-pw-site.com/products/ (Enable UrlSegments) 3.- Now products.php will be coded like this Note that Product.php is just a helper class that just have some methods for easier output formatting <?php require_once './includes/Rest.php'; require_once './models/Product.php'; // Vars with the default output $code = 200; $output = null; $header = Rest\Header::mimeType('json'); // First check if you have Url segment1 // this segment you can have the product id if($input->urlSegment1) { $productId = $input->urlSegment1; $product = Product::find($productId); // Check if we found a product if(!($product instanceOf NullPage)) { // Convert the page object to our model $product = new Product($product); // Detects the Verb if(Rest\Request::is('get')) { // json encodeable array $output = $product->get(); } else (Rest\Request::is('put')) { $params = Rest\Request::params(); $output = $product->put($params); // Could be 202 if modification is made async // 200 if OK // $code = 202; } else (Rest\Request::is('patch')) { $params = Rest\Request::params(); $sku = $params['sku']; $output = $product->patch('sku', $sku); // Could be 202 if modification is made async // 200 if OK // $code = 202; } else (Rest\Request::is('delete')) { $output = $product->delete(); } // Product not found } else { $code = 404; $output = Product::notFound(); } } else { // Detects the Verb if(Rest\Request::is('get')) { $params = Rest\Request::params(); $page = $params['page']; $output = Product::fetch($page); } else (Rest\Request::is('post')) { $params = Rest\Request::params(); // You can get the params like // $params['name'], // $params['sku'] // and so on $newProduct = Product::create($params); // Returns and array that can be json encoded $output = $newProduct->get(); // 201 Created $code = 201; } } // End if // Show the response and body http_response_code($code); header($header); echo json_encode($output);1 point
-
My, and a guess kongondos intention as well, of such a module would not be to replace or deny PW page tree approach (or even tricking someone), but providing an additional option to create a menu. Sometimes menus are needed which contain a wild bunch of references across the site, as well as some external links. The "virtual tree" approach is possible, but another interface solution would be better in some cases. That's just what motivated me to describe this approach (and I guess that motivated kongondo to write such a module in the first place, a year ago )1 point
-
Well basically when you follow the RESTful approach when creating systems, you use resources rather than actions. you can know more here http://restcookbook.com/ http://restpatterns.org/ http://www.restapitutorial.com/ and this books https://leanpub.com/build-apis-you-wont-hate http://www.soa-in-practice.com/ Example if you want to make an admin for the users resource. you can have this URL http://api.example.com/users In the traditional CRUD aproach, the verb is inside the URL like http://api.example.com/users/create but in REST you must use only HTTP Verbs to interact, so the same endpoint url makes different actions depending on the verb used to call it. In our system that could be http://api.example.com/users GET - result in a list of users POST - creates a new user ------------------------------------- http://api.example.com/users/clsource GET - result in the user data PUT - updates the all the data of a specific user PATCH - updates a specific data field of a specific user DELETE - deletes the user Using the HTTP response codes and mime types you can notify the result of an operation, usually with a JSON or XML body for information. The Web services Approach, specially REST web services, enables us to separate complex systems in smaller ones, easier to mantain and test. Basically you can have multiple backend systems that just are APIs and one frontend system that glue them all. this add a layer of security and robustness, because you can separate your systems in different servers. A possible attack can not affect all the system, just small parts of it.1 point
-
Very impressive Kongondo, looks great. If there were a way of combining the two steps into one (adding new items & arranging items) I think it would be a better experience for the user. 1) Add a new item 2) Choose from a dropdown whether it's a native page or external 3) Add item and item details. I can understand that with required fields it may be harder to achieve all of this from within the item boxes. The other slight improvement would be if the page tree remains open when an item has been clicked? But it already looks like a great addition to PW PS Love the music!1 point
-
I love it. I said before: "I like it that way, PLEASE stay talkin' on the forums" Now I think, stay away from the forums and go code for us1 point
-
I personally think the Cheatsheet should be made into a nice tea-towel that I can frame on the wall. Just a bit on docs - the ideal (for me, at any rate) would be to have the cheat sheet, complete with its short hand explanations (which are good!) and then a link to a longer explanation and real-world example or two. If anyone ever writes one anything for any of the items on the Cheatsheet, stick it on the wiki, or send it to me and I will stick it on. That must have been fun! (And potentially a bit of a shock for the unwitting....) Joss1 point
-
The problem lays in the if( $newsChildPage == $newsChildrenArray->first() ) { Comparing object shoudl be done using "===" Not sure why the error, but with "===" it works. However I cleaned up a little your code, using heredoc (<<<_END) which can be handy to generate output code so the indentation stays. foreach( $newsChildrenArray as $newsChildPage ) { $newsImg = ''; //Test for an image on the current page if( count($newsChildPage->images) ) { //If this is the first article, make a larger image if( $newsChildPage === $newsChildrenArray->first() ) { $newsImg = $newsChildPage->images->first()->size(157,151); } else { $newsImg = $newsChildPage->images->first()->size(77,71); } } $class = ''; $imgstr = ''; if( $newsChildPage === $newsChildrenArray->last() ) $class = ' class="last"'; if( $newsImg ) { $imgstr = "<img src='{$newsImg->url}' alt='$newsImg->description'/>"; } $out = <<<_END <a href="{$newsChildPage->url}"> <li$class>$imgstr <p> <span class="preview-title">{$newsChildPage->title}</span> • <span class="preview-date">{$newsChildPage->date}</span><br /> {$newsChildPage->summary} <a href="{$newsChildPage->url}" class="read-more">Read More...</a> </p> </li> </a> _END; echo $out; } Also changed some things to avoid further problems. Using ->eq(0) for example to check if there's an image is wrong. It won't work if there no image. Use count(); Also the check if($newsImg) will fail if there's no image as the variable won't exists. So you need to set it before.1 point
-
Thanks... I've been wanting to build it for awhile, as the need has come up a few times in some of my projects too. Our messages from the other thread motivated me to go ahead and build it. Let me know how it works for you if you use it at some point in the future. For anything that requires significant time to process, regular cron is still the way to go, but this LazyCron should be good for the majority of smaller automated tasks.1 point
-
This was pretty quick Looking very good, I will probably implement this on twitter puller. And nice list of other commits to P21 also!1 point