Jump to content


Popular Content

Showing content with the highest reputation since 01/13/2020 in Posts

  1. 8 points
    Happy new year, everybody 🥬 I've been sitting on this Dashboard module I made for a client and finally came around to cleaning it up and releasing it to the wider public. This is how it looks. ProcessWire Dashboard If anyone is interested in trying this out, please go ahead! I'd love to get some feedback on it. If this proves useful and survives some real-world testing, I'll add this to the module directory. Download You can find the latest release on Github. Documentation Check out the documentation to get started. This is where you'll find information about included panel types and configuration options. Custom Panels My goal was to make it really simple to create custom panels. The easiest way to do that is to use the panel type template and have it render a file in your templates folder. This might be enough for 80% of all use cases. For anything more complex (FormBuilder submissions? Comments? Live chat?), you can add new panel types by creating modules that extend the DashboardPanel base class. Check out the documentation on custom panels or take a look at the HelloWorld panel to get started. I'm happy to merge any user-created modules into the main repo if they might be useful to more than a few people. Disclaimer This is a pre-release version. Please treat it as such — don't install it on production sites. Just making sure 🍇 Roadmap These are the things I'm looking to implement myself at some point. The wishlist is a lot longer, but those are the 80/20 items that I probably won't regret spending time on. Improve documentation & add examples ⚙️ Panel types Google Analytics ⚙️ Add new page 🔥 Drafts 🔥 At a glance / Page counter 404s Layout options Render multiple tabs per panel panel groups with heading and spacing between ✅ panel wrappers as grid item (e.g. stacked notices) ✅ Admin themes support AdminThemeReno and AdminThemeDefault ✅ Shortcuts panel add a table layout with icon, title & summary ✅ Chart panel add default styles for common chart types ✅ load chart data from JS file (currently passed as PHP array) Collection panel support image columns ✅ add buttons: view all & add new ✅
  2. 5 points
    UPDATE 2020-01-19 I finally finished the integrated taxes provider - SnipWire now also handles shipping taxes correctly - and in a very flexible way which should cover pretty much all tax calculation methods/requirements worldwide! The integrated SnipWire taxes provider is now even more flexible than Snipcart own integrated provider! A store merchant can choose between the following shipping taxes calculation methods: No shipping taxes Apply a fixed tax rate: A fixed shipping tax rate from taxes configuration is used. Apply predominant tax rate: The shipping costs are allocated entirely to the economic good with the highest tax rate. So the highest tax rate from cart is used to calculate shipping taxes. Proportionally split and apply tax rates: The shipping service is divided as a secondary service and shares proportionally the fate of the respective main service. Part of the shipping service is thus subject to the normal tax rate, part to the reduced tax rate. If you find any other constellations which cannot be covered by the present options, please drop me a line and I'll add it.
  3. 4 points
    No problem, as Andreas said, everything you requested is already implemented. If you have other features in mind, do not hesitate guys 💪 Also, with the next version, you will be able to backup the big site with Duplicator. I can make backup of a 1.6GO website without issue. It can be already tested - it's on the dev branch on Github.
  4. 3 points
    The machine names of system fields are always in english, so you need to access the description like this: $description = $download->Datei->description(); By the way, there's also a utility method for the file extension: $extension = $download->Datei->ext(); Also, if your Repeater only contains the file field, you could also get rid of the Repeater altogether and just allow multiple files on the "Datei" field. Then you could iterate it directly: foreach ($page->Datei as $file) { $url = $file->url(); $ext = $file->ext(); $description = $file->description(); // output download links here ... } Look up the Pagefile API documentation for a list of all available utility methods.
  5. 3 points
    The GDPR doesn't actually handle cookies specifically. GDPR is about processing personal data. A cookie is processed by a webserver when a user accesses your website, so GDPR is applicable IF there is personal data involved in regards to the cookie. Even a simple session cookie is personal data, because it identifies a certain browser session, which in turn likely identifies a person. There are a few things GDPR demands you to provide to users in such a case, like what the data is used for (Art. 13/14) and it needs to have a legitimate reason (Art. 6) for you to be allowed to do so. This is even more complex if it's not a cookie set by your website, but by a third party. There it's the shared responsibility between your and the third party that everything is handled correctly. This is usually done with DPA (data processing agreement) which is a binding contract where both parties essentially guarantee each other GDPR compliance. The GDPR gives users the right to deny consent wherever you cannot use Art. 6 1.f) as legitimate reason. Therefore cookie-banners with the option to not have certain cookies set. The GDPR also says you may not auto opt people into giving consent, therefore the default for optional cookies should be unset. Besides the GDPR there's afaik a law in Germany for cookies specifically, which has been the kinda predecessor for the long overdue EU wide ePrivacy directive. I'm not as well versed with this one. It was essentially the law, which started all the cookie banner stuff.
  6. 3 points
    I made some progress and I could finally backup on an Unix machine the bigger setup I have (a backup imported as the method is not finished to be able to work on Windows) - for about 400MB files and a database of 1.6GB using the new implemented method which use the MySQL native tools. The method consist of writing a shell script on the fly which then is executed. If you wanna test it - still a work in progress - you can find the new method implemented on the dev branch on Github.
  7. 2 points
    It does, it's just not documented unfortunately. I linked to information about it in my earlier post above. Here is a demo... Page structure: Field settings for subcategory field: "page.category" will be replaced with the ID of the page selected in the Category inputfield in Page Edit, whenever that field changes. The "has_parent" part is just to avoid unwanted pages appearing in the Subcategory inputfield if the Category inputfield is changed to empty (no page selected). Result:
  8. 2 points
  9. 2 points
    You could use PHP output buffering to read your file into a variable. Something like: ob_start(); include('./emailbody.inc'); $emailBody = ob_get_clean();
  10. 2 points
    Yes it is good and in use on the site https://www.p-jentschura.com/ I added this directly in the HTML so it has nothing to do with ProcessWire in general, but I am thinking about developing a module for it to set the options. But thats only for the far future and next website project.
  11. 2 points
    I'm not familiar enough with Carbon to make a guess how well DateTimeAdvanced works with it. Generally, every operation/function call that expects a string should still work the same as with a regular Datetime field when output formatting is off, since my helper object has a ___toString() method that returns the unix timestamp. If you just want to add database subfield selectors to your system and don't want to search arrays or plug in your own subfield patterns, you can use this stripped-down module: <?php /** * ## Enhanced Datetime Field * * Allows searching for individual components of datetime fields in * standard subfield syntax in the database. * * Note that this doesn't work for searching in PageArrays. For that, * use the DatetimeAdvanced Fieldtype instead. * * ## Possible subfields * * - day * - month * - year (4-digit) * - hour (0..23) * - minutes * - seconds * - day_of_week (0..6, 0 = Sunday) * - day_of_year (0..365) * - week_of_year (1..53) * - date (just the date part, yyyy-mm-dd) * - time (just the time part, hh:mm:ss) * * ## Example * * ``` * $pagelist = $pages->find("mydatefield.year=2016"); * ``` **/ class FieldtypeDatetimePlus extends FieldtypeDatetime implements Module, ConfigurableModule { public static function getModuleInfo() { return array( "title" => "Datetime Plus", "summary" => wire()->_("Datetime field with extended search syntax. Needs timezone support enabled in MySQL, see http://dev.mysql.com/doc/refman/5.7/en/time-zone-support.html for details."), "version" => "0.0.1" ); } protected static $_operators = array( "day" => array("d", "d"), "month" => array("c", "m"), "year" => array("Y", "Y"), "hour" => array("H", "H"), "minutes" => array("i", "i"), "seconds" => array("s", "s"), "day_of_week" => array("w", "w"), "day_of_year" => array("j", "z"), "week_of_year" => array("v", "W"), "date" => array("%Y-%m-%d", "Y-m-d"), "time" => array("T", "H:i:s"), ); /** * Match a date/time value in the database, as used by PageFinder * */ public function getMatchQuery($query, $table, $subfield, $operator, $value) { if($subfield != "data") { if(! $this->isAllowedSubfield($subfield)) { throw new WireException(sprintf($this->_("Unknown subfield name for datetime field: %s"), $subfield)); } if($subfield == "date" || $subfield == "time") { $value = preg_replace('/[^0-9:-]/', '', $value); } else { $value = $this->sanitizer->int($value); } if($operator == "day_of_year") { $value += 1; } $database = $this->wire("database"); if($database->isOperator($operator)) { $table = $database->escapeTable($table); $value = $database->escapeStr($value); $formatarg = self::$_operators[$subfield][0]; $formatarg = preg_replace('/([a-zA-Z])/', '%$1', $formatarg); $query->where("DATE_FORMAT({$table}.data, '{$formatarg}'){$operator}'$value'"); } } else { $value = (int) $this->_sanitizeValue($value); if($value) $value = date('Y-m-d H:i:s', $value); else $value = ''; $database = $this->wire('database'); if($database->isOperator($operator)) { $table = $database->escapeTable($table); $subfield = $database->escapeCol($subfield); $value = $database->escapeStr($value); $query->where("$table.{$subfield}{$operator}'$value'"); } } return $query; } public function isAllowedSubfield($subfield) { return array_key_exists($subfield, self::$_operators); } public function getInputfield(Page $page, Field $field) { $inputfield = $this->modules->get('InputfieldDatetime'); $inputfield->class = $this->className(); return $inputfield; } /** * Return array with information about what properties and operators can be used with this field. * * #pw-group-finding * * @param Field $field * @param array $data Array of extra data, when/if needed * @return array See `FieldSelectorInfo` class for details. * */ public function ___getSelectorInfo(Field $field, array $data = array()) { if($data) {} $selectorInfo = $this->wire(new FieldSelectorInfo()); $info = $selectorInfo->getSelectorInfo($field); foreach(array_keys(self::$_operators) as $op) { $info["subfields"][$op] = array( "name" => $op, "label" => $op, "operators" => $info["operators"], "input" => "text", "hint" => "", "options" => array() ); } return $info; } public function ___getModuleConfigInputfields() { $ifs = new InputfieldWrapper(); $cmd = $this->config->dbInitCommand; $off = date('O'); $needUpdate = false; if(strlen($off) > 0) { if(preg_match('/time_zone/i', $cmd)) { $needUpdate = false; return $ifs; } // PHP returns +0200, MySQL wants +02:00 $off = preg_replace('/(\d\d)$/', ':$1', $off); // Append timezone set statement $cmd .= sprintf(", time_zone = '%s' ", $off); } $f = $this->modules->get("InputfieldMarkup"); $f->label = $this->_("Enable timezone support in MySQL"); $f->description = $this->_("For datetime plus database selectors to be reliable, PHP and MySQL need to add/subtract the same offsets for datetime and timestamp values.") . " " . $this->_("To accomplish that, the timezone setting in MySQL needs to be set to the same value as in PHP.") . " " . $this->_("Copy the following line to site/config.php:") ; $f->attr( 'value', "<code>" . "\$config->dbInitCommand = \"$cmd\";" . "</code>" ); $f->notes = $this->_("Important: timezone data must be loaded in MySQL! See [this link](http://dev.mysql.com/doc/refman/5.7/en/time-zone-support.html) for details."); $ifs->append($f); return $ifs; } }
  12. 2 points
    Here's some updated code to try: // Find IDs of users that have been active in the last $mins number of minutes function onlineUserIDs($mins, $limit = 500) { $table = SessionHandlerDB::dbTableName; $seconds = $mins * 60; $sql = "SELECT user_id " . "FROM `$table` " . "WHERE ts > DATE_SUB(NOW(), INTERVAL $seconds SECOND) " . "AND user_id!=40 " . // exclude guest "ORDER BY ts DESC LIMIT $limit"; $query = wire('database')->prepare($sql); $query->execute(); $results = $query->fetchAll(\PDO::FETCH_COLUMN); return $results; } // User IDs active in the last hour $online_user_ids = onlineUserIDs(60); // Convert to string for use in selector $online_user_ids = implode('|', $online_user_ids); // Online users $online_users = $users->find("id=$online_user_ids"); // Offline users excluding guest user $offline_users = $users->find("id!=$online_user_ids, roles.count>1");
  13. 2 points
    We recently relaunched DOMiD, the Documentation center and Museum of Migration in Germany. Concept, design and implementation by schwarzdesign. Features Bilingual site with German and English A section-based design focusing on flexibility and ease-of-use for editors Multiple forms built with FormBuilder that can be placed on any page Separate feeds for news & press releases Lightning fast page loads with almost perfect ratings in Lighthouse Completely accessible and SEO-friendly Notable tech decisions Forms and form placement There are multiple forms for different services that DOMiD offers. Those are built with the FormBuilder. The editors don't have access to the FormBuilder itself, but we still wanted to allow them to control which form is displayed on what page. For this purpose, every page has a select field to select which form to include (if any). Additionally, the form placement has additional fields for a headline and a description, so a generic contact form can be reused in different contexts. Section-based design Most pages are built through Repeater Matrix sections. There are multiple section types available, for example: A generic text / image column with up to three columns of text and images An accordion (rendered as <details> elements). An image gallery Downloads (for files and images, displayed as a list of downloads) External Embed (e.g. YouTube) All sections have an optional headline and a selection of three different background colours. In addition, text columns may be rendered as a coloured block with some padding. This allows for interesting and diverse layouts. Testimonial database One of the available sections is for testimonials or statements (you can see one at the bottom of the homepage). Because one testimonial may be displayed on multiple pages and the client wanted to be able to switch the displayed testimonial on the fly, there's a separate content type for statements. The statement content type has fields for the statement text, author, and author image. The testimonial section only has a page reference field to select which statement to show. This way, the testimonial definition is separate from the placement on a page. Modules used Form Builder Pro Fields (Repeater Matrix in particular) Unique Image Variations ALIF - Admin Links in Frontend Sitemap Wire Mail SMTP Developer Tools: Tracy Debugger, Duplicator, ProcesWire Upgrade Migration museum As a sidenote for anyone living in Germany, this month the German Bundestag has approved funding for DOMiD's first Migration Museum ("Haus der Einwanderungsgesellschaft")! The museum will be build in Cologne and is scheduled to be finished by 2023. We're looking forward to it! Check out this page if you want to learn more, or find out what people are saying about it here.
  14. 1 point
    Done! 🙂 - donation button in GitHub repo, forum signature and initial SnipWire forum post. (I hope this is not against forum rules?)
  15. 1 point
    thank you @rick it works! here the code: // event ID $eventID = $input->get('eventID','int'); $event = $pages->get($eventID); // config $testEmail = $event->event_mail_test_adress; $fromEmail = $event->event_mail_from; $fromName = $event->event_mail_from_name; $emailSubject = $event->event_mail_subject; $filenameDate = strftime('%d-%m-%Y', $event->date_start); // filename $filename = "{$event->name}_{$filenameDate}"; // create ics file $file = fopen('ics/'.$filename.'.ics', 'w') or die('File can not be saved!'); // fetch start date $event_start_ts = $event->getUnformatted("date_start"); // build the .ics data $ical_data = "\r\n"; $ical_data .= 'BEGIN:VCALENDAR'; … … … $ical_data .= "\r\n"; $ical_data .= 'END:VCALENDAR'; fwrite($file, $ical_data); fclose($file); // HTML BODY ob_start(); include('./_inc/emailbody.inc'); $emailBody = ob_get_clean(); // send email $m = new WireMail(); $m->to($testEmail); $m->from($fromEmail, $fromName); $m->subject($emailSubject); $m->bodyHTML($emailBody); $m->attachment('ics/'.$filename.'.ics'); $m->send();
  16. 1 point
    One other small thing - can you add support for the built in 'title' field in the Page type? I tried this locally editing the getBuiltInFields() function at line 41 in src/Type/PageType.php Thanks, Tom
  17. 1 point
    Yeah, makes me wonder if it would have been better if the Pages::trashed method was called immediately before saving the trashed page rather than after. Maybe Ryan has a good reason for doing it that way. I still think it's better to hook after Pages::trashed if you want to know for sure which pages are trashed because when hooking Pages::trash there are still instances where the trashing can fail. For example, there might be another Pages::trash hook in a module that deliberately prevents trashing of particular pages. If you hook Pages::trashed you can parse the parent page ID (and some other info) from the name of the page in the trash: $pages->addHookAfter('trashed', function(HookEvent $event) { $page = $event->arguments(0); $pages = $event->object; /* @var PagesTrash $trasher */ $trasher = $pages->trasher(); $name_info = $trasher->parseTrashPageName($page->name); if(!empty($name_info['parent_id'])) { $parent = $pages($name_info['parent_id']); // ... } });
  18. 1 point
    You can hook Session::redirect and check the redirect URL and the current process. Not perfect, but as far as I can see the deletePage() method uses a redirect URL that is unique in ProcessPageEdit and is therefore identifiable as coming from that method. $wire->addHookBefore('Session::redirect', function(HookEvent $event) { $url = $event->arguments(0); if($this->process == 'ProcessPageEdit') { $admin_url = $event->wire('config')->urls->admin; if(strpos($url, $admin_url . 'page/?open=') === 0) { $event->arguments(0, '/your/custom/url/'); } } });
  19. 1 point
    Sorry - copy and paste error*. I am using a ConfiguratorQuoteCreateInput field when I get the error. *(I tried using update as well in case the page reference needed a parent page to exist first)
  20. 1 point
    Thank you very much for the reply. Yes, we tried all of these options. The problem seems to be fixed for now by turning off Tracy Debugger and our Hosting provider made some changes as well. Because of that I do not really know what actually fixed the problem, but I'm glad it is actually fixed.
  21. 1 point
    I know this is an older post, but you should be able to do some of this now with this module:
  22. 1 point
    Hey @elabx I did find this module from @kongondo and have no doubt that it would do the job perfectly. The reason not yet to purchase it is because I am just trying to weight every option and see which one fits the best. Quite honestly, I would love to purchase all the premium modules from the PW-guru-gang as this would be just a tiny payout for the flawless and completely free support we've all received throughout the years, but presently I am on a bit tight budget so that would have to wait. I am definitely purchasing the FormBuilder and Dynamic Select as I see them fit in almost every project...
  23. 1 point
    Have you seen: https://modules.processwire.com/modules/process-dynamic-selects/ ? Modules from @kongondo are as pro as it gets :)
  24. 1 point
    Sorry, but couldn't even find any business address for "m3server" on their webpage. I experimented with setting up our own server and during our failure trying to do that we found a recommendation for php-friends.de which offers managed vServer and was recommended for being very helpful and flexible. I keep you posted about the result.
  25. 1 point
    Hi Flydev, Fantastic, thanks for the quick fix. I'm no longer getting the error. However, I do see an invalid timestamp display in the Created column when viewing the list of backups for my AWS backups.
  26. 1 point
    I just tested it and the process ended without error 🤦🏻‍♂️ What's the version of your Duplicator ? Edit1: Ok got it. Will push a fix. Edit2: You can update the module to the version 1.3.15
  27. 1 point
    @flydev 👊🏻 @adrian Thanks for all your had work on this invaluable module. I've updated a few sites to the latest Duplicator version. But have been running into an issue when I have AWS Backups enabled. When AWS Backups is enabled and going to Setup -> Duplicator, I get a Call to a member function getTimestamp() on bool Error. See attached screenshot. The site core and modules are all up to date as of today (Jan 14, 2020). Using php 7.3, got the same error w/ php 7.2. Amazon libraries installed w/ Composer.
  28. 1 point
    @Jens Martsch - dotnetic @Macrura Adding multiple panels via $panels->add([]) should work now.
  29. 1 point
    Hi @Robin S, just wanted to let you know that I tried out the latest version, but it didn't seem to work with the file/image files with no extension. Here are some examples that didn't work for me: Ex. pdf file with no extension: https://www.charleston-sc.gov/DocumentCenter/View/18787/Certificate-of-Construction-Completion-RES-Checklist?bidId= Ex. image file with no extension: https://www.charleston-sc.gov/ImageRepository/Document?documentID=23023 After saving the page with those urls, it shows the following error message: AddImageUrls: The remote file https://www.charleston-sc.gov/DocumentCenter/View/18787/Certificate-of-Construction-Completion-RES-Checklist?bidId= has no file extension and its MIME type does not correspond with a valid file extension for field "page_files". The file field is configured to allow pdf files and the image field is configured to allow png, jpg, jpeg, gif. Do these urls work for you?
  30. 1 point
    Problem solved – Ryan pushed a fix: https://github.com/processwire/processwire/commit/f5f83e814880c862b5bfd0ee935b8c9e7699bd74
  31. 1 point
    This works for me here: if ( count(page()->images) ) { echo "yay!"; } ProcessWire: 3.0.148 PHP: 7.3.13 Webserver: Apache/2.4.35 (Win64) OpenSSL/1.1.1b MySQL: 5.7.24 but you don't even need count... this works as well: if ( page()->images) { echo "yay!"; } and if you really need the number of images, this works too (always has): $imageCount = page()->images->count()
  32. 1 point
    Nice site. Thanks for the insights about how it was built and what are the modules involved.
  33. 1 point
    @HMCB, thanks! So the datepicker itself is nothing fancy, it's just a style-matched version of Calendario https://tympanus.net/Development/Calendario/index2.html In Calendario, you just setup a JSON array of the events, and init the plugin with JS. The link on each date is to a URLSegment which then is processed via the API to get the events for that date. The URL segment logic was tricky because there are events with series dates (using a ProFields Table) which also need to be accounted for, but the beauty of the PW api is that it only took maybe 20 lines of code for all of the event fetching logic for single date calls. ---- also wanted to say thanks for the mention on the blog, newsletter, and SOTW! 😊 ---- and the SkyscrapersTurbo™ Profile is well underway, featuring rangesliders for all numeric attributes (Year, Floors, Height) and architect filter. So this version is is possible to narrow down results for very specific combinations.
  34. 1 point
    I hope you all have had a good start to 2020! Last week's release of the new master ProcessWire version 3.0.148 has gone smoothly, and if you haven't yet upgraded, it's a good time to do so. This week I've been working to finish up the new front-end file upload field called InputfieldFrontendFile, which is part of the LoginRegisterPro module package. I've released beta version 1 of that module in the LoginRegisterPro support board in the downloads topic, so it's ready for download now, as is a new version of LoginRegisterPro to accompany it. Today, I've written up a lot about this module, as well as posted a few screenshots here: Front-end file uploads with InputfieldFrontendFile module I've also got some ProcessWire core updates in progress this week for the dev branch, but this week has gone by so quickly I think I'll have to save those for next week. Thanks for reading and have a great weekend!
  35. 1 point
    Out of curiosity, how tight is the coupling between this inputfield and LoginRegisterPro / a specific form in it? 🙂 I'm working on a project that will require front-end uploads. It's not a huge deal – I can handle that in other ways as well – but just wondering if I could possibly use this new inputfield for those. These uploads would have nothing to do with the LoginRegisterPro module specifically, but I might still find good use for it on this particular project.
  36. 1 point
    I've added support for these sorts of remote files in v0.2.1.
  37. 1 point
    @Harmen So finally I found some more time to investigate and try out a few things. Here's what works and what doesn't for me: My setup: Two PW sites installed locally (one of the latest PW versions from dev branch). Both are accessible locally via dummy domains instead of localhost (pw.test + pw2.test). Windows, Apache, PHP 7.3.13, Laragon. Both sites have the relevant other domains in the site/config.php $config->httpHosts whitelist array. Not sure if that is necessary. I have also temporarily switched from DB-sessions to regular sessions, because I was getting fatal PHP session errors. Googling around I only found various pointers to bugs in PHP 7.2, so I installed PHP 7.3.13. tldr: There are lots of "ifs" and strange things going on trying to make this work. I really wish the docs would be more helpful with that bootstrapping feature. And as mentioned before - I hope someone else chimes in who managed to make it all work as intended.
  38. 1 point
    Yes the PaymentStripeIntents module does implement their new Intents API and is therfore SCA ready.
  39. 1 point
    I use page references heavily in my projects. Page Autocomplete has a field (Settings specific to ...) on the Input tab of the field settings page that can be used to specify what fields are used during the query. You can even select multiple fields, e.g. a category_ref_by_id field can specify multiple ID fields. This way you can merge individual data sets into a single one. Each source set can have its own ID, and the ...ref_by_id field can use all of them. I have no plans for the automatic creation of the missing referenced page but it can be achieved very easily. Just create another DataSet using the same CSV file and import the appropriate "category" columns for creating the missing pages. You can also try to use the location attribute in the DataSet config to make a reference to the file uploaded to the original DataSet (see the wiki) to avoid duplicate uploads. If you need to perform these imports automatically you can create two tasks (category import and the original one) and specify a dependency between them (first import categories then the full data set). See Tasker wiki.
  40. 1 point
    OK. It was time to update the wiki 🙂 I've uploaded a new DataSet version (0.9.5) to GitHub. It contains many improvements for data type conversions, page reference handling and several bug fixes. It also has a new profiler to optimize the import routines. Tasker is also updated.
  41. 1 point
    Yes, you guys are right. But the "best way" would be if PW natively had support for this, regardless of what the user has chosen as the language title. The language name should be the defined value from the ISO 3166 standard, although one should be able to change the URL of the language, so instead of "de" the URL "deutsch" could be used. EDIT: This would enable us module devs to deliver language files with our modules, regardless of the users language setup.
  42. 1 point
    Exactly the same thought came to my mind. Will implement this as an option.
  43. 1 point
    of() is a method of Page objects, but you're trying to use it on an PageArray of Repeater pages. Try: // Get the page you want to work with $home = $pages->get("template=home"); // Turn output formatting off for the page $home->of(false); // Change the Repeater field value $home->submissions->removeAll(); // Save the page $home->save();
  44. 1 point
    Hi @Noel Boss, Thank you so much for the plugin! I'm still trying to figure out how to make it do precisely what I need it to do, so I just have a quick question about formatting. Basically I'm trying to get this kind of json output: [ { "title": "Paul Rand", "year": [ "1993" ], "authors": [ "Abrams, Janet" ], "categories": [ "Criticism", "Design", "Design Histories" ] } ] But I'm getting this instead: { paul-rand: { title: "Paul Rand", year: [ "1993" ], authors: [ "Abrams, Janet" ], categories: [ "Criticism", "Design", "Design Histories" ] } } This is the query I'm currently using: header("Content-type: application/json"); $modules->get('PageQueryBoss')->debug = true; $query = [ 'title', 'year.title#year', 'authors.title#authors', 'categories.title#categories' ]; echo $pages->find('template=book, sort=authors.title')->pageQueryJson($query); Is there some setting I'm missing, or is the problem with how I'm structuring the query?
  45. 1 point
    A quick tutorial how to create file downloads using pages You will be able to create a new page using template "PDF" (or any you setup), upload a pdf file. You then can select this page using page fields, or links in Wysiwyg. The url will be to the page and NOT the file itself. This will allow to keep a readable permanent unique url (as you define it), unlike /site/assets/files/1239/download-1.pdf, and you'll be able to update/replace the uploaded file without worring about its filename. Further more the file will also have an id, the one of the page where it lives. Clicking those links will download or open the file (when target="_blank") like it would be a real file on server with a path like /downloads/project/yourfile.pdf. You'll be also able to use the "view" action directly in the page list tree to view the file. Further more you'll be able to esaily track downloads simply by adding a counter integer field to the template and increase it every time the page is viewed. Since the file is basicly a page. This all works very well and requires only minimal setup, no modules and best of it it works in the same way for multi-language fields: Just create the language alternative fields like "pdf, pdf_de, pdf_es" and it will work without modifying any code! Still with me? ok PW setup Download folder: Create a template "folder" or "download-folder" with only a title needed. Create pages in the root like /downloads/project/ using this template. Setup the template for the pdf files 1. Create a new template in PW. Name it pdf 2. Goto template -> URLs tab and set the URL end with slash to no. (So we can have /path/myfile.pdf as the URL) 3. Create a new custom file field, name it pdf. Set its maximal count to 1 under -> Details tab. 4. Add the pdf field created to the pdf template. Easy. 5. Create a new "pdf" page using the pdf template under a download folder you created earlier. 6. Give it the title and in the name field add ".pdf" to the end (could also leave as is) Template PHP file for the pdf files 1. Create the template file pdf.php in your /site/templates folder 2. add the following code: <?php // pdf.php if($page->pdf){ wireSendFile($page->pdf->filename); } Done. To see the options you have with PW's wireSendFile() you can also overwrite defaults <?php // pdf.php if($page->pdf){ $options = array( // boolean: halt program execution after file send 'exit' => true, // boolean|null: whether file should force download (null=let content-type header decide) 'forceDownload' => false, // string: filename you want the download to show on the user's computer, or blank to use existing. 'downloadFilename' => '', ); wireSendFile($page->pdf->filename, $options); } Simple and powerful isn't it? Try it out. Some thoughts advanced Create as many file types as you like. It might also be possible to use one "filedownload" template that isn't restricted to one field type but evaluate it when being output using $page->file->ext, or save the file extension to the page name after uploading using a hook. One last thing. You can add other meta fields or preview images to the template and use those to create lists or detail pages. It's all open to goodness. Again all without "coding" and third-party modules. Further more you can use the excellent TemplateDecorator to add icons per template and have a nice pdf icon for those pages. This as a base one could also easily create a simple admin page for mass uploading files in a simple manner, and create the pages for the files automaticly. ImagesManager work in the same way. Cheers
  46. 1 point
    @SamC it's really as simple as that: https://processwire.com/blog/posts/new-ajax-driven-inputs-conditional-hooks-template-family-settings-and-more/#new-conditional-hooks $wire->addHookAfter('Pages::saved', function(HookEvent $event) { $page = $event->arguments('page'); bd('page saved'); bd($event, 'event'); bd($event->object, 'event->object'); bd($event->arguments(), 'event->arguments'); }); in the beginning it can be a little confusing when to use event->object, event->arguments and event->return but with the help of tracy you can quickly bring light into the dark: add the code above to the tracy console, set the radio on the right to load it on "ready" (same as placing the code in the site/ready.php file) and save any page: $event->arguments('page') is the same as using $event->arguments(0) that you will see very often and in the tracy dump you see that 0 is simply the key for the first argument in that hookevent. you can also collapse the "data" property of the hookevent and you would see the same:
  47. 1 point
    For a project I've posted yesterday I built a basic module to subscribe users to a Sendy installation. I love Sendy and my client too as we use it to send lots of emails using Amazon SES costing almost nothing at all. Do guys think that's worth to post about? It's very basic now, it doesn't use Sendy's API at all, it just send the data using POST to subscribe the users to different lists.
  48. 1 point
    In case anyone else* (*so that would be the future me then, when I've forgotten o_O) wants to know how to do this, I worked it out, it's probably in the docs but for the life of me I could not find it (sorry doc writers). You have a hanna code called my_hanna_snippet and you want to output it from within a template, not from the Admin i/f. $hanna = $modules->get('TextformatterHannaCode'); echo $hanna->render("[[my_hanna_snippet]]"); Hope this helps someone/me in the future
  49. 1 point
    actually this is in the core, all you need to do is add these to your toolbar (you don't need to add any plugins): so where your first line would look like this out of the box: Format, Bold, Italic, -, RemoveFormat you can change it to be like this: Format, Bold, Italic, Underline, -, RemoveFormat, -, TextColor, BGColor
  50. 1 point
    RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC] RewriteRule ^(.*)$ http://%1/$1 [R=301,L] Just for the record, you might want to consider the benefits of using www-subdomain before doing this, though.
  • Create New...