Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 02/09/2016 in all areas

  1. Today I want to share a little module that adds 2 additional save buttons with redirect and 1 unpublish button to the page edit. 2 additional save buttons: My intention was that it would be nice if someone saves an article in the backend and will be redirected after saving directly to the frontend page of the article. This module adds 1additional save button at the bottom next to the default save button and 1 at the top. So you can choose if you want to save the article with the default save button or you will save it with the custom save button and you will get redirected to the frontend article. 1 unpublish button: The idea behind this was that I want to disable the setting tab for non superuser. The problem was if I hide it, then non superusers are no longer able to unpublish an article. Therefore this module adds an additional unpublish button at the bottom - the user clicks it and the page will be saved with status unpublished. All pages under the admin section will not be affected of this module. Module is multilingual, so you can set the button texts in all languages. Top view page status published: Bottom view page status published: Bottom view page status unpublished: Here is the code: <?php /** * Adding 2 additional save buttons with redirect to frontend and 1 unpublish button for page edit form. * * ProcessWire 2.x * Copyright (C) 2010 by Ryan Cramer * Licensed under GNU/GPL v2, see LICENSE.TXT * * http://www.processwire.com * http://www.ryancramer.com * */ class CustomPageSaveAndUnpublish extends WireData implements Module { /** * getModuleInfo is a module required by all modules to tell ProcessWire about them * * @return array * */ public static function getModuleInfo() { return array( 'title' => 'Custom page save and unpublish module', 'version' => 1, 'summary' => 'Example for adding 2 additional save buttons with redirect and 1 unpublish button to page edit', 'href' => 'http://www.processwire.com', 'singular' => true, 'autoload' => true ); } /** * Initialize the module * * ProcessWire calls this when the module is loaded. For 'autoload' modules, this will be called * when ProcessWire's API is ready. As a result, this is a good place to attach hooks. * */ public function init() { $this->addHookAfter("ProcessPageEdit::buildForm", $this, "addSaveButton"); $this->addHookAfter("ProcessPageEdit::buildForm", $this, "addUnpublishButton"); // tell processwire that this is a page save if ($this->input->post->submit_save_minor) { $this->input->post->submit_save = 1; // attach hook on page save $this->addHookAfter("Pages::saved", $this, "hookPageSave"); } if ($this->input->post->submit_unpublish) { $this->input->post->submit_save = 1; // attach hook on page save $this->addHookAfter("Pages::saveReady", $this, "hookPageSaveReadyUnpublish"); } } public function hookPageSave($event) { //function to redirect to the frontend after save $page = $event->arguments("page"); if ($this->input->post->submit_save_minor) { // this will get saved after this saveReady hook so no need to save here $pageid = $page->id; $goto = wire("pages")->get("id=$pageid")->url; //get url of frontend article wire("session")->redirect($goto); } } public function hookPageSaveReadyUnpublish($event) { //function to change the status to unpublished $page = $event->arguments("page"); $status = $page->status; $unpublishmessage = __("Status of the page is set to unpublished"); if ($this->input->post->submit_unpublish) { if ($status == 1) { $page->status = "2049"; $this->message($unpublishmessage); } } } public function addSaveButton($event) { //function to add the 2 additional save button with redirect at the top and at the bottom $page = $event->object->getPage(); $status = $page->status; if (($page->rootParent->id != "2") AND ($status == 1)) { //dont show on all pages which are under the admin section and which are not published $form = $event->return; $buttontext = __("Save and go to page"); // new submit button $f = $f2 = $this->modules->InputfieldSubmit; $f->attr("name", "submit_save_minor"); $f->attr("value", $buttontext); $f2->attr("name", "submit_save_minor"); $f2->attr("value", $buttontext); $f2->class .= ' ui-priority-secondary head_button_clone'; // add submit button after the regular save button only if page is published $form->insertAfter($f, $form->get("submit_save")); $form->insertAfter($f2, $form->get("submit_save")); } } public function addUnpublishButton($event) { //function to add the unpublish button at the bottom if page has status published $page = $event->object->getPage(); if ($page->rootParent->id != 2) { //dont show on all pages which are under the admin and dont show the button under the delete tab $form = $event->return; $unpublishbuttontext = __("Unpublish"); // new submit button $f = $this->modules->InputfieldSubmit; $f->attr("name", "submit_unpublish"); $f->attr("value", $unpublishbuttontext); // add unpublish button after the save button if ($page->status == 1) { $form->insertAfter($f, $form->get("submit_save")); } } } } Everybody who is interested can download the modul here: CustomPageSaveAndUnpublish.zip Best regards Jürgen
    4 points
  2. .. resolved the issue in less than three minutes, yesterday I was too lazy to take a closer look (in other words: my battery was empty). Edit php.ini, to avoid this warning set 'always_populate_raw_post_data' to '-1', restart php and the message is gone ; Always populate the $HTTP_RAW_POST_DATA variable. PHP's default behavior is ; to disable this feature and it will be removed in a future version. ; If post reading is disabled through enable_post_data_reading, ; $HTTP_RAW_POST_DATA is *NOT* populated. ; http://php.net/always-populate-raw-post-data always_populate_raw_post_data = -1 Before: <br /> <b>Deprecated</b>: Automatically populating $HTTP_RAW_POST_DATA is deprecated and will be removed in a future version. To avoid this warning set 'always_populate_raw_post_data' to '-1' in php.ini and use the php://input stream instead. in <b>Unknown</b> on line <b>0</b><br /> <br /> <b>Warning</b>: Cannot modify header information - headers already sent in <b>Unknown</b> on line <b>0</b><br /> [{"error":false,"message":"Added file: body.png","file":"\/site\/assets\/files\/1\/body.png","size":26952,"markup":"\n\t<li id='file_60984d6634e55217fe6424485038dd7d' class='InputfieldFileItem InputfieldImage ui-widget'>\n\t\t<p class='InputfieldFileInfo InputfieldItemHeader ui-widget ui-widget-header ui-helper-clearfix'>\n\t\t\t<span class='HideIfSingle HideIfEmpty InputfieldFileDrag'><i class='fa fa-sort'><\/i> <\/span>\n\t\t\t<i class='fa fa-caret-right InputfieldFileDrag HideIfMultiple'><\/i> \n\t\t\t<label class='InputfieldFileDelete'><input type='checkbox' name='delete_images_60984d6634e55217fe6424485038dd7d' value='1' title='Delete' \/><i class='fa fa-fw fa-trash'><\/i><\/label>\n\t\t\t<a class='InputfieldFileMove InputfieldFileMoveBottom' href='#' title='Move to bottom'><i class='fa fa-fw fa-angle-double-down'><\/i><\/a> \n\t\t\t<a class='InputfieldFileMove InputfieldFileMoveTop' href='#' title='Move to top'><i class='fa fa-fw fa-angle-double-up'><\/i><\/a> \n\t\t\t<a class='InputfieldFileName pw-modal pw-modal-large' title='body.png: body.png (720x101)' href='\/processwire\/page\/image\/edit\/?id=1&file=1,body.png&rte=0&field=images' data-buttons='#non_rte_dialog_buttons button' data-autoclose='1' data-close='#non_rte_cancel'>body.png <i class='fa fa-pencil ui-priority-secondary'><\/i><\/a> \n\t\t\t<span class='InputfieldFileStats'>26 kB, 720x101 <\/span> \n\t\t<\/p>\n\t\t<div class='InputfieldFileData ui-widget ui-widget-content'>\n\t\t\t<div class='InputfieldImagePreview'>\n\t\t\t\t<a class='InputfieldFileLink' target='_blank' href='\/site\/assets\/files\/1\/body.png?nc=10'><img height=\"100\" src=\"\/site\/assets\/files\/1\/body.0x100.png?nc=1455052838\" alt=\"\" data-gridsize=\"100\" \/><\/a>\n\t\t\t\t<div class='InputfieldImageActions'>\n\t\t\t\t\t<a title='body.png (720x101)' href='\/processwire\/page\/image\/edit\/?id=1&file=1,body.png&rte=0&field=images' class='pw-modal pw-modal-large' data-buttons='#non_rte_dialog_buttons button' data-autoclose='1' data-close='#non_rte_cancel'><i class='fa fa-fw fa-crop'><\/i><\/a>\n\t\t\t\t<\/div>\n\t\t\t<\/div>\n\t\t\t<div class='InputfieldFileDescription'><label for='description_images_60984d6634e55217fe6424485038dd7d' class='detail'>Description<\/label><input type='text' name='description_images_60984d6634e55217fe6424485038dd7d' id='description_images_60984d6634e55217fe6424485038dd7d' value='' \/><\/div>\n\t\t\t<input class='InputfieldFileSort' type='text' name='sort_images_60984d6634e55217fe6424485038dd7d' value='2' \/>\n\t\t<\/div>\n\t<\/li>","replace":false,"overwrite":0}]
    3 points
  3. AFAIK all that's needed should be what ProcessPageEdit::___buildForm does for the "Save + Keep Unpublished" button: $submit2->class .= ' ui-priority-secondary head_button_clone';
    3 points
  4. Sadly, I wasn't able to do all the mock-ups this weekend as I was away. However I've managed to make a start on the edit page. @LostKobrakai - Thank you for your feedback, I let the sidebar in for this exact reason. I also have some ideas on how to fill out that space when only the Page link is available (due to permissions - editorial role). My idea is to have maybe the recently edited appear as like widgets in the sidebar - or something along those lines. I really need to think about it. This will be just be a theme I'll be building mostly to gain experience on the system but also to use in my personal projects. I will be releasing it for others to download. Anyway, here is the edit page (http://i.imgur.com/kp96yng.jpg):
    3 points
  5. Hello all PW fans. I wanted to contribute with a simple but short tutorial on how i created a simple visitor counter to have a rough estimate on what pages got visits on my website. This tutorial assumes that you have setup a field on your page template that you want to monitor with a name of visit_counter of the field type integer. I put this simple code together by reading some answers by the Excelent Ryan Cramer here on the forums and decided to make a few changes to his implementation. This is because i did not want the code to count my visits to the site if i was logged in. Instead the code shows the current pages visits if i am logged in, and hides it if i am not. Enough talking. Here is the code: <?PHP /* simple code for recording current page visit count to its visit_counter field of "integer" type. But only if the visitor is not currently logged in. */ if($user->isLoggedin()) { /* if the user is logged in */ echo("<p>Visits: {$page->visit_counter} st.</p>"); } else { /* if the user is NOT logged in */ /* get current visit counts and save them plus one */ /* turn of output formating so PW do not give an error when we change the value */ $page->of(false); /* increment the current integer plus one */ $page->visit_counter++; /* save the visitor_counter field */ $page->save('visit_counter'); /* turn on output formating so PW work as it should */ $page->of(true); } ?> I also put this code in a PHP file by itself and named it _visit_counter.php so that i could easily include it in whatever page i want to monitor. You could potentialy expand this and make it more advanced. i would look at the "roles" and "user" section of the: http://cheatsheet.processwire.com Bonus Tip, in the PW admin go to your template that you have the field setup in and on the "advanced" tab look for the "List of fields to display in the admin Pagelist" section and in that field put: {title} - Visits: {visit_counter} This again assumes the fields name is visit_counter . Now in my page list in admin i can see the title followed by the number of visits. Handy This is my first post to these forums. And i will try to help out where i feel my abilites come to the rescue. Hope this helps anyone out there working with Processwire.
    2 points
  6. Check your field settings, tab Detail. There you can define, what do you want to receive. If your field will contain multiple pages, then you should select the first option (PageArray). If your field only needs to contain a single page, then select one of the single Page options. Select one of the single Page options and your initial code should work. If you want to get multiple pages, you need to use the second foreach.
    2 points
  7. what i've been trying out today is just doing it in javascript, using this: https://github.com/nwcell/ics.js/ because then whatever events are viewable in the dom can all be put into a one-click add to calendar on the fly I'm showing events list in a datatable) what i'll probably do is put each piece of the info into data attributes, like data-title, data-description, data-dtstart, data-dtend as a test i have this working: ('#download-ics').click(function(e) { var cal = ics(); e.preventDefault(); $("#events tbody tr").each(function(){ var title = $(this).find('h3').text(); var desc = $(this).find('h3').text(); var loc = $(this).find("dd.location").text(); var dtstart = $(this).find("dd.dates ul>li").text(); cal.addEvent(title, desc, loc, dtstart, dtstart); }); javascript:cal.download(); }); you could also have a single click id for the button next to any individual event and then not do the each
    2 points
  8. In a hurry, so, quick answer. Assuming that you want to limit this to echo a summary on the site (as opposed to limit the editing to only one paragraph), a quick google search returned this http://www.webdevdoor.com/php/php-get-first-paragraph-function
    2 points
  9. He's addressed that in the (new) official release thread found here @horst, maybe we should now lock this topic and direct discussion to the new thread?
    2 points
  10. Great introduction to urlSegments. This is a textbook use case. However, some pointers for beginners who might want to copy and paste the code. You can probably skip the whole $allowedSegs array, imo. I would sanitize the segment like you did, then cut off the extension afterwards (not sure if anything can go wrong doing that string operation on unsanitized input, but it works the same). Then I’d just get a child page of that name. Most importantly, get the child page using $page->child('name=' . $sanitizedSegment), because with $pages->get() you might get an unexpected page of the same name from a different branch of the page tree. If it returns a NullPage, you can still 404. Also, I would refrain from overwriting $page, just to rule out any potential headaches that might cause
    2 points
  11. I would like to thank all the Nice people giving me likes for this post. It realy gives me encuragement to come up with future tutorials when i have something usefull to share.
    2 points
  12. @Nukro: A few points that may help you get into the right direction: Never use ECB mode. With the same key, it always produces the same output for the same input, thus making patterns obvious and rainbow table attacks cheap. If you see an example on a web page that uses ECB, navigate somewhere else (that includes the examples at php.net). Never store keys in clear text or with weak encryption on the same system where you hold the data (or have it easily accessible), or you're replacing security by obscurity. Whenever you use symetric encryption, use an initialization vector (and make sure your cipher mode, e.g. CBC, supports IVs). It is much like the salt in password hashes and needs to be random for each encrypted entry and reasonably unique. Prepend the IV before you store the encrypted data and extract it for decryption. Depending on how you model your system, implementing a secure system can get slightly tricky. If visibility of information shouldn't be limited to a single PW user, they all need to know the key (let's call it the 'master key') used. To avoid storing it in plain text, encrypt and store the master key for every user. Use a secure, repeatable hashing function to generate a long enough encryption key from the user's password for this step. On password change, re-encrypt and store the key (and, again, don't forget to use IVs). Of course, in such a system, all user passwords need to be strong, or an attacker with access to the DB contents could easily crack the encryption to the master key through brute force. To populate the encrypted master key for new users (or if you add the feature to a system with existing users) you have to assign their password from an account that already has the correct master key. This way, at least no plain encryption key is ever stored in the system. There are more elaborate (and, of course, even more secure) approaches, but these would require an automated pulic/private key infrastructure and off-site services. That's what the "good guys" among the big internet companies use, but things get really complex at that point.
    2 points
  13. Update: So I've been using Pete's dashboard module instead of the technique described above; it's quicker to setup, and easier to clone to other installs, and no need to have code in various places like the themes folder and in ready.php The hope/plan is to turn it into a full widget enabled dashboard module that i can install on various installations a) without having to do any hardcoding b) enable control over which widgets are shown on the dashboard (by user/role/permission) c) allow for easy configuration of colors, icons, columns, etc. To achieve this I setup some templates for different widget types, one is a shortcuts widget, another is a page lister widget; these each have pages and fields to configure them; i can set them to appear by user/role. in the future i can add different types of widgets as needed and then include them in the needed user's dashboard. This is using a combination of a free (MIT licensed) admin theme for bootstrap, but instead of using bootstrap as the css layout, it uses a tiny grid called rwdgrid which i s 2KB; it uses the box, and other widget classes from the admin theme.
    2 points
  14. Hi there, I'm a newbie and I'm trying to learn more about Processwire so I've setup XDebug + PHPStorm to get some real time debugging happening. I've noticed that no matter where i put a breakpoint in any of my php modules, it seems to return the following error. Error: __debuginfo() must return an array (line 55 of /Users/FrancisChung/site/Test/templates/head.inc) This error message was shown because you are logged in as a Superuser. Error has been logged. I was wondering if fellow Processwire users have a similar setup, and was successful in getting debugging going. I've also read this forum post: https://processwire.com/talk/topic/1611-yes-debugging-templates-and-core-code-works/ I'm also aware there's an issue with XDebug and Processwire: https://github.com/ryancramerdesign/ProcessWire/issues/1316
    1 point
  15. This is a reworked website of Imre Baksa, a Hungarian actor and director. His former (static) website was also made by me back in around 2009. He asked me to do a redesign but I decided to involve ProcessWire to make content management easier. http://baksaimre.hu/ Lesson 1: templates - no thanks It is tempting to use ready-made templates for a project because most of the work is done, there's some tweaking here and there and mission completed. Okay, this is the theory. I have to admit that I have never found a suitable template/theme for my projects, but being a web designer this is the way it should be, I guess. This time I found one that looked fine: Landed from HTML5 UP. In fact there was a self-made design for the site and this template looked 90% similar, so it really seemed to be an easy task to bring it into ProcessWire. Soon turned out that some of the fancy features of the template required advanced JavaScript knowledge. That means, I had to dive into it and learn how it worked. I did so and I was able to tweak things then, but I wasn't satisfied with the outcome. So I went back to the drawing board and dropped the template. This experience reassured that starting from scratch would have been a better choice on the long run. The good thing is that I learned some new things that will come in handy in the future. Lesson 2: template engines - Latte is still my best friend I was using Nette's templating engine, Latte in previous projects and I liked it a lot. It is similar to Twig, which is more widespread, so I decided to try that for learning purposes. This sounded like an easy journey because ProcessWire supports Twig through modules. However, at the end of the day I sort of regretted that. Latte has some small helper features that makes templating easier, and these differences made up a huge difference. Maybe I'm getting old but I felt Twig more of a nuissance after Latte. Finally I got rid of Twig and I guess I will never look back I will keep using Latte in the future even if I have to bootstrap it manually. Lesson 3: frontend development is hard Having ProcessWire at hand, backend is the minor part in web development, at least in smaller projects. I've used only a few jQuery plugins but making them work together nicely was a real challenge. At the end I had to make a compromise by removing SmoothScroll because it didn't work well with JScrollPane. Even so, I had tough times to eliminate content "jumps" when a lightbox is opened, and to make JScrollPane work the way I wanted to work. Making the whole thing responsive added another level of complexity as features needed to be destroyed/reinitialized on different screen sizes. I like to polish things as much as possible but the current state of the web makes it almost impossible to reach certain level of perfection. Moduling up This was the project where I felt that a link checker button would be helpful so started to develop InputfieldURLChecker. I use this in a few projects and it does the job nicely. My other pet PW module FEEL also got some polish during site development. For site-wide settings I used MultiValueTextformatter so I didn't need to create a field for every setting, just one for all. Other modules used: SEO: must have! Admin Template Columns for better admin layout Page List Image Label: to add featured image to the list of pages in the admin Pageimage Remove Variations: remove unused image variations Forms: one step closer to simpler form processing For the contact form I used Nette Forms, because it is so easy to add forms to the site with it, with frontend & backend validation in one go. Processing them still needs some sweat, but this time I finally put together a class to make this easier. Surely it still needs some iterations but it's a huge help: it takes only a few parameters to save submissions as Pages or send email to the admin or the visitor. For email templates I also used Latte (what else? ). I also created a "json_storage" field to store submission. The beauty is that while it's only one field (a textarea), in the admin it is displayed as formatted key-value pairs using a hook. Summary The site looks fresh and the year knob on the left makes it fun to use. Content management is easy as 1-2-3 thanks to ProcessWire, even if it's mostly me who will deal with the updates. While it's not visible on the site, in the background there are many improvements to my development tools and workflow that will be of huge help in the future.
    1 point
  16. Hi, I'm toying with the idea of having a central place in PW's translation system for phrases and strings that are frequently used throughout several template files. The way I understand PWs translation system after reading the docs (http://processwire.com/api/multi-language-support/code-i18n/) is that everytime a __('translate-me') is used in a different .php file, the system will pull that 'translate-me' in a context of the .php file where it is used - so basically all translatable strings of _one_ file at a time are manageable via the (very cool) translation tool in PW's admin interface. So if for example I were to use __('Daytime phone number') in, lets say, the home.php as well as in the login.php and maybe a profile.php, I'd need to translate this three times, right? - A solution given in the docs is using the gettext context, and have this pointing to the first template file where the string was used first, so I only need to translate that template's strings and the other two will "see" their translation threre. So in my fabricated example: //home.php: __('Daytime phone number'); //login.php: __('Daytime phone number','/site/templates/home.php'); //profile.php: __('Daytime phone number','/site/templates/home.php'); So I only need to look for the translations of /site/templates/home.php in the translation tool and the other occurances of that string will be taken care for. This now brings me to the idea of creating a php file in, say, "/site/language/strings.php". So what I can now do (but what feels kind of hacky to me…?), I use all the frequently used strings of my several template files in this "strings.php" file, wrapped in their language functions like __('translate me'). in _init.php or maybe in config.php I store this file's path for the context: $i18lctx = '/site/templates/language/strings.php'; And now I can use the transalatable strings in several template/php files like this // in home.php, in archive.php etc __('Hello pilgrim',$i18lctx); In PWs admin interface I'll have all the strings in one place: the strings.php (?textdomain=site--templates--language--strings-php). Is this the right way to go about it or am I overlooking a basic fact (maybe like, dude, this is what the language packs are for)? Cheers, Tom
    1 point
  17. The exclamation marks are simply pattern delimiters. Normally, this is "/", but PHP looks for the first character in a pattern string and treats it as the delimiter. This comes in handy whenever you have a pattern containing a forward slash, as you'd otherwise have to escape it to tell PHP that it's part of the pattern and not its end.
    1 point
  18. @Macrura I like where you are going with this. Every user needs a good starting point and a comprehensive Dashboard is a proven and effective way of doing it. Exciting times
    1 point
  19. You could expand this even a little further - mix and match: $page->setOutputFormatting(false); $key = "visit_counter".$page->id; if(!$session->$key) { $page->visit_counter++; $page->save('visit_counter'); $session->$key = 1; } PS: i don't take credit for this, I found this here too Have fun with PW!
    1 point
  20. I figured out the issue. User error and some default behavior in the mix. When an invalid date format is entered for example "2012", the date reverts back to 1969-12-31 after page save. I guess that is the default date input format, because if I select none, it reverts back to that. Can someone confirm this is normal behavior. Thanks for all the suggestions and assistance so far.
    1 point
  21. Hello I have a repeater ("banner") with two fields, one a text field ("keyword"), the other one is a page reference field ("color"). How can I get the title of the page this field is referencing? I have tried this $slogan = $page->get("banner"); foreach ($slogan as $s){ echo "<div class='" . $s->color . "'>"; echo "<p>" . $s->keyword . "</p></div>"; } This way I get the ID of the "color" page selected. As I need the title of this page, I tried echo "<div class='" . $s->color->title . "'>"; But then the output is empty. How do I get the title?
    1 point
  22. I'm confused. What @kongondo says is undoubtedly true: a page field will return a Page object, even from within a repeater. I just tested it myself. But knowing that, @joe_ma's code from the OP should have worked: echo "<div class='" . $s->color->title . "'>"; Here, $s is the repeater item, color is its page field, and title is the title property of that page field's page. Seems fine to me? Edit: Oh, I get it. The page field "color" is multi-page, so it returns a PageArray which silently fails to return a title property. @joe_ma, try changing color to a single page field, unless you need to define multiple colors per repeater item. Then your original code should work. It's a good idea to name your fields according to what they return, i.e. this one is called "color" as opposed to "colors", so when you access it, you're likely to expect a single item
    1 point
  23. No need for that get again. $s is already an object...just foreach it and output its properties...This works (in my tests) $page->banners here is the repeater field. And you have to foreach, if the page field is multi foreach ($page->banners as $banner){ foreach ($banner->page_field_name as $b) {// page field in repeater is 'page_field_name' echo "<div class='" . $b->title . "'>"; echo "<p>" . $b->keyword . "</p></div>"; } } $s->color is actually not an id ...That's just the toString() method kicking in. It is an object
    1 point
  24. In the latest 3.x version, there's also some nice additions that will let you easily do this. http://processwire.com/blog/posts/processwire-3.0.7-expands-field-rendering-page-path-history-and-more/#render-groups
    1 point
  25. Very interesting concept indeed. And definitely loving this new colour-scheme, too. Nice work, Tom. Quick note about the editor: a expander button at the top left of the editor [<] to expand it to the width of the page would come in handy, especially useful when working with multi-column editor layouts.
    1 point
  26. Thanks Diogo I tried this: <?php $lotes = $pages->get(1025)->children(); function getFirstPara($string) { $string = substr($string,0, strpos($string, "</p>")+4); return $string; } foreach ($lotes as $lote) { echo getFirstPara($lote->body); } ?> And it worked great !!!
    1 point
  27. Yes, this what certainly needs improvement in the ProcessWire admin. The site tree (Pages) is just confusing and click intensive to use. After almost two months of using it, I still keep clicking on the name (title) of the page to edit it or wonder about how to open the subtree, or does it have a subtree (children) at all? I know there is the number of children after the name, but the whole tree is so unconventionally crafted, that I have too keep reminding myself of how it works.
    1 point
  28. Thanks BitPoet this does exactly what I wanted. Here is the complete code for all who are interested in: Edit: Condition not to show on pages under the admin section was added <?php /** * Adding other types of save buttons for page edit form. * * ProcessWire 2.x * Copyright (C) 2010 by Ryan Cramer * Licensed under GNU/GPL v2, see LICENSE.TXT * * http://www.processwire.com * http://www.ryancramer.com * */ class CustomPageSave extends WireData implements Module { /** * getModuleInfo is a module required by all modules to tell ProcessWire about them * * @return array * */ public static function getModuleInfo() { return array( 'title' => 'CustomPageSave', 'version' => 1, 'summary' => 'Example for adding other save buttons to page edit', 'href' => 'http://www.processwire.com', 'singular' => true, 'autoload' => true ); } /** * Initialize the module * * ProcessWire calls this when the module is loaded. For 'autoload' modules, this will be called * when ProcessWire's API is ready. As a result, this is a good place to attach hooks. * */ public function init() { $this->addHookAfter("ProcessPageEdit::buildForm", $this, "addSaveButton"); // tell processwire that this is a page save if ($this->input->post->submit_save_minor) { $this->input->post->submit_save = 1; // attach hook on page save $this->addHookAfter("Pages::saved", $this, "hookPageSave"); } } public function hookPageSave($event) { $page = $event->arguments("page"); if ($this->input->post->submit_save_minor) { // this will get saved after this saveReady hook so no need to save here $message = __("Page was saved"); $this->message($message); $pageid = $page->id; $goto = wire("pages")->get("id=$pageid")->url; //get url of frontend article wire("session")->redirect($goto); } } public function addSaveButton($event) { $page = $event->object->getPage(); if ($page->rootParent->id != "2") { //dont show on all pages which are under the admin section $form = $event->return; $buttontext = __("Save and go to page"); // new submit button $f = $f2 = $this->modules->InputfieldSubmit; $f->attr("name", "submit_save_minor"); $f->attr("value", $buttontext); $f2->attr("name", "submit_save_minor"); $f2->attr("value", $buttontext); $f2->class .= ' ui-priority-secondary head_button_clone'; // add submit button after the regular save button $form->insertAfter($f, $form->get("submit_save")); $form->insertAfter($f2, $form->get("submit_save")); } } }
    1 point
  29. I agree with that loading the page edit through ajax could be a great improvement, a user sent me a module once that basically accomplished this and it is wonderful, I keep it in all of my PW installs. It just misses to update the tree without reloading. I have been working on Shopify, and this "smoothness" in going from one part of the site to the other is very well accomplished in their interface.
    1 point
  30. This sounds more like a local issue then, I will try to track down.
    1 point
  31. @gmclelland - it should be really simple to achieve an add to calendar, especially with processwire. 1.) check for url segment and if it is valid echo your ical, and exit... /* HANDLE SEGMENT AND ROUTING -------------------------------------*/ if($input->urlSegment2) throw new Wire404Exception(); if( strlen($input->urlSegment1) ) { $pageName = substr($input->urlSegment1,-4); $pageName = $sanitizer->pageName($pageName); $event = $pages->get("name=$pageName"); if($event->id) { header('Content-type: text/calendar; charset=utf-8'); header("Content-Disposition: inline; filename={$input->urlSegment1}"); $options = array( // associative array: // send vars to your vevent processor here... ); wireRenderFile('./inc/vevent.php', $options); exit(); } } } here is the alternate way if($input->urlSegment2) throw new Wire404Exception(); if($input->urlSegment1) { // if urlSegment1 render the single camp $pageName = substr($input->urlSegment1,-4); $pageName = $sanitizer->pageName($pageName); $event = $page->child("name=$pageName"); if($event != '') { header('Content-type: text/calendar; charset=utf-8'); header("Content-Disposition: inline; filename={$input->urlSegment1}"); $options = array( // associative array: // send vars to your vevent processor here... ); wireRenderFile('./inc/vevent.php', $options); exit(); } else { // if the event does not exist... throw new Wire404Exception(); } } you can generate the VEVENT using a class, function etc..
    1 point
  32. Please review the following information https://github.com/ryancramerdesign/ProcessWire/tree/devns
    1 point
  33. Thank you sir. I must say that i fell in love with Processwire when i discovered it by chance. My Autism makes it difficult for me to grasp concepts sometimes and with Wordpress i had a tough time. Put Processwire changed everything for me. It sounds silly byt its like a dream come true. Processwire totaly make sens to me and the way the API works and as a fan of jQuery i love the route Ryan Cramer took with it.
    1 point
  34. This has got some small fixes and updates and is officially released now: https://processwire.com/talk/topic/12151-wire-queue-basic-implementation-of-simple-queues/
    1 point
  35. PHPStorm does have a few damn nice features (mostly related to namespaces and inheritance), but it's a big resource hogger. It's consuming about 1,5 GB of ram on my machine, which just isn't nice if you're also running one or two vm's at the same time. Therefore I find myself using SublimeText most of the time. But I've lately also considered using vim more and set it up once like I need it. It's just super easy to put it on another machines and work from wherever you need to (as long as you've some shell access).
    1 point
  36. My setup: ->every event is a page ->events are listed only in a overview page ->i change the name of the event pages ot something like my-great-event-title-2016-02-04-id.ics that the page template itself works as a ics file I've this in my event.php: <?php //set correct content-type-header header('Content-type: text/calendar; charset=utf-8'); header('Content-Disposition: inline; filename='.$page->name.''); //get the data for the ical file $homepage = $pages->get('/'); $event_desc = stripslashes(strip_tags($page->event_text)); // get the startdate $dtstart_date = $page->getUnformatted("event_start_date"); $dtstart_time = $page->event_start_time; $tsstart = strtotime($dtstart_time); // get the enddate $dtend_date = $page->getUnformatted("event_end_date"); $dtend_time = $page->event_end_time; $tsend = strtotime($dtend_time); //set the repetition_type to the recur option value $event_repetition_type = $page->event_recur; //set the output for rrule in ical file $rrule = ''; switch ($event_repetition_type) { case '2': $rrule = 'RRULE:FREQ=WEEKLY;COUNT=5;'; break; case "3": $rrule = 'RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=5;;'; break; case '4': $rrule = 'RRULE:FREQ=WEEKLY;INTERVAL=3;COUNT=5;;'; break; case '5': $rrule = 'RRULE:FREQ=MONTHLY;COUNT=5;;'; break; case '6': $rrule = 'RRULE:FREQ=YEARLY;COUNT=5;'; break; default: $rrule = ''; } //build the .ics data $ical_data = 'BEGIN:VCALENDAR'; $ical_data .= "\r\n"; $ical_data .= 'VERSION:2.0'; $ical_data .= 'PRODID:-//'.$homepage->title.'//NONSGML ProcessWire//DE'; $ical_data .= "\r\n"; $ical_data .= 'CALSCALE:GREGORIAN'; $ical_data .= "\r\n"; $ical_data .= 'METHOD:PUBLISH'; $ical_data .= "\r\n"; $ical_data .= 'BEGIN:VEVENT'; $ical_data .= "\r\n"; $ical_data .= 'SUMMARY:'.$page->title; $ical_data .= "\r\n"; $ical_data .= 'UID:' . md5(uniqid(mt_rand(), true)) . '@'.$config->httpHost; $ical_data .= "\r\n"; $ical_data .= 'CLASS:PUBLIC'; $ical_data .= "\r\n"; $ical_data .= 'STATUS:CONFIRMED'; $ical_data .= "\r\n"; if ($rrule) { $ical_data .= $rrule; $ical_data .= "\r\n"; } $ical_data .= 'DTSTART:'.date('Ymd', $dtstart_date).'T'.date("His", $tsend); $ical_data .= "\r\n"; $ical_data .= 'DTEND:'.date('Ymd', $dtend_date).'T'.date("His", $tsstart); $ical_data .= "\r\n"; $ical_data .= 'DTSTAMP:'.date('Ymd').'T'.date('His'); $ical_data .= "\r\n"; $ical_data .= 'CATEGORIES:Landwirtschaft,Maschinenring'; $ical_data .= "\r\n"; $ical_data .= 'LOCATION:'.$page->location; $ical_data .= "\r\n"; $ical_data .= 'URL:'.$page->httpUrl; $ical_data .= "\r\n"; $ical_data .= 'END:VEVENT'; $ical_data .= "\r\n"; $ical_data .= 'END:VCALENDAR'; //output echo $ical_data; ...not the smartest code - could be refactored and opitmized - but it is working for an "save to outlook" link... additional i've a "save in your google calendar" link that works with the google calendar API: //output Ical Outlook link echo '<a href="'.$e->url.'"><span class="fa fa-windows" aria-hidden="true"></span> Outlook iCal Link</a><br>'; //output Google Cal link echo '<a target="_blank" href="https://www.google.com/calendar/event?action=TEMPLATE&text=' .$e->title. '&dates=' .date('Ymd', $e_start).'T'.date("His", $tsstart).'/'.date('Ymd', $e_end).'T'.date("His", $tsend). '&location=' .$e->event_location->title. '&details=' .$e_text_decode. '&trp=true"><span class="fa fa-google" aria-hidden="true"></span> Google Calendar Link</a>'; maybe not the best but useful examples... best regards mr-fan
    1 point
  37. I posted a similar topic that might be relevant to this discussion. https://processwire.com/talk/topic/11905-globally-access-the-page-tree-in-the-admin-theme/ I like how wagtail.io has a sliding page tree.
    1 point
  38. I like where you are headed with this Tom. One of the things that I find still quite cumbersome in PW is the need to return to the Page Tree after editing a page so I can find and edit the next page. Having the tree in the side menu would be a huge timesaver in my opinion, and also just help the user to visualize the site structure when they are editing a page - they can see where the page fits - context can often be very helpful in a complex site. I have actually been thinking about this today too and my thoughts are that the Setup, Modules, Access items should be along the top (like the default theme). Then the page tree could simply be in that side menu - no need for a second side menu. As far as implementing this, I don't think we need to have ajax loading of pages when clicking the edit butt on a page - it can simply reload the entire page with the page tree open to the page that is being edited. I also like the color scheme - matching the recent PW dev mockups.
    1 point
  39. I've decided to make a start on the design, I thought I'd get a feel on what people think about the idea behind me doing this before continuing. I've always been passionate about UI/UX, and I'm always up for the extra practice wherever I can get it. Here is the design I've started: (http://i.imgur.com/2SY50ua.jpg - full size) So here are few things I've tried to solve with this: Unify the user journey, this page tree is going to be the main focus for every user and often unless you are an admin, the sidebar doesn't get used often. This is great from a UX point of view because that means they know exactly where everything is and there is no complicated bloat. I've tried to move very useful features into this screen as this is where I find users spend the most time. Also giving admins the ability to better define that user journey. The cog will edit the filter and the add filter button will, well, add a filter. Filters will work much like Peter mentioned ListerPro, however they will work just as well with Lister. I will do a mock-up if people want on how a filter page will look. The other people is moving items in the page tree. Many people expect to be able to drag and drop and if they want to move into a child and didn't open the page tree first. Then they will have to stop the move, open the tree and then move again. This works by hovering over a parent, the child will then expand. You can start a move just by dragging the page name. Another people I've seen people do, is not knowing to click on the edit on the right of the page name. Instead they frantically click the page tree and watch the page name highlight change, but nothing happen. I hope to do some work on this, but part of the problem is that the page names look like links. However I think they should be clickable to go into edit. I see people doing it, so they must feel natural doing it that way. I haven't yet thought of a solution for this as I also like how the click expends children. However one thing I've thought of is have it click it edit if it doesn't have children. If it does the will have to use the edit as normal. I'm not completely sold on this idea as I don't like the idea behind having two ways to get to edit. I can see this confusing users. Advanced Search will be the same as Lister.
    1 point
  40. Once an update was made, the version number gets visible again due the notification message
    1 point
  41. I'm using almost the same setup, but my "t" function handles context too. The good thing is that _strings.php can be copied to your next projects too. function t($text, $context = 'General', $textdomain = '/site/templates/_strings.php') { return _x($text, $context, $textdomain); } _strings.php: // Search _x('Search site', 'Search'); _x('No matches', 'Search'); // Forms _x('From', 'Forms'); _x('From email', 'Forms'); _x('To', 'Forms'); ...
    1 point
  42. I use a similar solution to a _strings.php file, and include it from my _init.php file (prependTemplateFile), so that it's available to all templates. But I bundle all the translated values into a new function (which I'll call _t(), but you could name it whatever you want). So you can call upon that _t() function anywhere that you'd call a __() function. For instance, in your /site/templates/_strings.php file... function _t($label) { static $labels = null; if($labels === null) $labels = array( 'Yes' => __('Yes'), 'No' => __('No'), 'Maybe' => __('Maybe'), 'And so on...' => __('And so on...') ); return isset($labels[$label]) ? $labels[$label] : $label; } From there, you can replace any __('label') call with _t('label'). In this manner, you only need to translate your _strings.php file, and all the other template files can use the translated labels. For the cases where you need to translate text that only exists in a particular file, then you'd continue to use the __() function as usual.
    1 point
  43. There's no UI to add Listers. That's part of the pro module. The core module is used in Pages > Find and for listing the users of your installation. You can also use it via custom modules.
    1 point
  44. For anyone interested this was the only way I could get it working. <? $page->of(false); $desc = $page->images_single->description; $page->of(true); echo $modules->get('TextformatterMarkdownExtra')->format($desc);
    1 point
  45. Welcome gebeer, There're some ProcessWire addicts over here, watch out, so you've been warned now! if(count($page->images)) { foreach($page->images as $img) { if($img->width > 600) { $config->styles->append('/path/to/popup.css'); $config->scripts->append('/path/to/popup.js'); break; } } }
    1 point
  46. Ryan, Many thanks for this module, I just used it to add 436 users. There were a small handful of things that weren't initially clear, so I detailed them below for anyone else trying to import users. If you plan to import passwords, you need to open the module and add FieldTypePassword to $fieldtypes protected $fieldtypes = array( 'FieldtypePageTitle', 'FieldtypeText', 'FieldtypeTextarea', 'FieldtypeInteger', 'FieldtypeFloat', 'FieldtypeEmail', 'FieldtypeURL', 'FieldtypeCheckbox', 'FieldtypeFile', 'FieldtypePassword', // add this line ); Since users are pages and all pages require a title, your CSV will need to have a title column. In my case, I duplicated all the usernames into that column — so name and title are the same. In order for title to show as a connection option during your import, you need to add the title field to the user template file. To do this, go to: Setup > Templates (open the filters area at the top, and choose "show system templates". Select the user template and add the title field. One other thing to note, be sure to have a roles column in your CSV with roles for each user. I forgot that during my first test import and all the users were set to guest. You should be all set to import your users.
    1 point
×
×
  • Create New...