Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 11/21/2018 in all areas

  1. The Katharinenhöhe is a rehabilitation clinic in the Black Forest (Germany) for teenagers, young adults and families with children who suffer from cancer. Our agency designconcepts was lucky to relaunch their website. Our goal was to unify the previous separated areas (families and teenagers/young adults) and provide a clear structure for potential patients. Also we wanted to show that the clinic is a nice place to live, despite the circumstances. We rebuild the website from scratch with the framework UIkit and used large images as well as videos. www.katharinenhoehe.de Features: Repeater Matrix Tour Contrast Theme Glossary Autocomplete Search Facebook Repeater Matrix Most of the pages use a basic page template which have one Repeater Matrix field. This field has around 15 different content elements, so it is easy to build a page with different elements in various amounts. Tour On the site tour we build a image map with markers showing interesting places of the clinic. For this task the nice module Image Marker and the Marker component of UIkit came in handy. The tour is available on every page containing a marker. Contrast Theme For a better reading experience you can switch to a more contrasting theme of the website by clicking the theme switcher (on the top right). This is a separate stylesheet with darker color variables. The choice will be saved in a session variable and stays as long as the browser is opened. Glossary To explain complicated medical terms better, we highlight a set of terms in every textarea they occur and explain them with a tooltip. For this task we wrote a simple Textformatter module which looks for the terms in a page and replaces the terms with the tooltip. This tutorial by @benbyf helped me. Thank you! ? Modules used: Color Email Obfuscation (EMO) Front-End Page Editor Functional Fields Image Marker Markup Sitemap XML Phone ProCache Repeater Matrix Tracy Debugger Upgrades Regards, Andreas
    9 points
  2. It might be @Nvim is a troll, an AI bot or just someone not polite. We should treat them accordingly as soon as we find that out. But they might as well be someone very new to web development, forum software and even English language. I would presume Nvim innocent for now ? (after reading his other posts). It is more about our community spirit than about someone else) So until some of you moderators ban him, he is a proper forum citizen, I guess)) Come on @Nvim, give me some likes for defending you (and @BitPoet and everyone else you received some good answers from) by clicking the heart icon in the lower right corner under a post. Prove your good will ?. Install Codelobster or whatever you choose and try out ProcessWire, the favorite CMS of this forum members and simply put the best one in the world at the time of writing. P.S. Forum search is still broken by design, so more duplicate beginner questions will come)
    4 points
  3. I really like your sentiment @Ivan Gretsky and I do think we should refrain from dismissing any questions, but if you look at Nvim's post history: https://processwire.com/talk/profile/7184-nvim/ their very first post in this forum was "Is Codelobster IDE any good?" - unfortunately I don't think they have much (if any) genuine interest in PW. Even when they posted what seemed like it might have been a genuine question (https://processwire.com/talk/topic/20104-how-to-convert-relational-data-in-pw-pages) which was given a thoughtful response, they never followed up - not even a "like" on the response.
    4 points
  4. Actually it's very easy. You can get the email of the page creator with $page->createdUser->email
    4 points
  5. S. Siedle & Söhne Telefon- und Telegrafenwerke OHG is one of the leading manufacturers of building communications technology in Germany and Europe. For a company of this size it is important to have a solid brand communication. For this reason our agency designconcepts developed a portal where the employees of the company as well as others who work with or have interest in their brand can find everything that is important for the brand Siedle. The website introduces the brand, provides informations for different topics (for example logos, typography, colors etc.) and has a large Media-Center with images, videos and documents. The website was build with the framework UIkit. brand.siedle.com Features: Repeater Matrix Protected Content Media-Center Cart Auotcomplete Search Repeater Matrix The information pages are build with a Repeater Matrix field and have a two columns layout. To be even more flexible, a section is a content element containing two Repeater Matrix fields for each column. This maybe sound crazy at first but was no problem and made complex layouts possible. ? Protected Content Not all of the informations are meant for guests, so we made it possible to protect each page, section and content element with a checkbox. If this checkbox is checked, the content will only be visible for logged-in users. Media-Center The core of the brand portal is the Media-Center. The Media-Center is actually a mirror of a separate digital assets management database where the client can manage images, videos and documents with a nice interface in a protected environment. All new assets will be synced via a shell script containing PHP and curl commands calling the API of the database. This script will be executed every 15 minutes via cron job. This way all of the assets are saved as pages in ProcessWire and we can expand them with our own logic and fields. Also for the unlikely case that the database is not available, the Media-Center would still work. Cart Inside the Media-Center you can add assets to your cart. This cart is for downloading selected assets or sharing them with others. A cart will be accessible for 30 days. Modules used: Admin Custom Files Continents and Countries Email Obfuscation (EMO) Front-End Page Editor Functional Fields Markup Sitemap XML ProCache Repeater Matrix Tracy Debugger Upgrades Regards, Andreas
    3 points
  6. While it is not an 'out of the box' solution, it is perfectly possible to for instance to use the RestAPI module to parse any bit of content, such as (items from a) repeater matrix. Just like when you would write your regular template logic, it does require some custom programming on your end though: you will have to go through the matrix' items and decide how they should be presented through your API. Which is exactly the flexibility which makes PW our go-to solution for pretty much any modern (headless) webapp or old-school site! ?
    3 points
  7. Do not agree with you, @bernhard for a couple of reasons. 1. Choosing ProcessWire over WordPress or some other more popular solution is a hard decision. It is often made because of this forum friendly support before one finds the beauty and power of the system. 2. Every answer we post here helps someone who googles for it later. And even answering the same questions later in time helps to prove the answers still exists and there are people still caring to answer. And often there are more new ways to solve the already solved problem, so the got to be mentioned in this forum. I can see @Nvim came here to learn about ProcessWire, to compare it with other solutions he knows about. Just maybe in a year or two he (or she?) will be the author of a new e-commerce module for PW, who knows)) If he does not stop here, the discovery of ProcessWire may become the beginning of one of the most exciting adventures for him (her?) as it was for me and so many others here, who perfectly know and have used systems like WordPress, Drupal, Joomla or MODx.
    3 points
  8. A couple of new features worth mentioning: 1) The RequestInfo panel's Field List & Values section now has a new "Adminer" column with a link to open the row for chosen field for the current page directly in Adminer. 2) I have finally committed the autocompletions for PW methods, properties and $page fields for the Console and File Editor panels. Hopefully you'll find this a nice shortcut, but more importantly a learning experience by finding new methods you didn't know about (or forgot).
    3 points
  9. Hi LAPS, based on the error message it appears the file your function lives in is not namespaced. So your type-hint Page $item is telling PHP that you expect an object of the class Page in the global namespace. All ProcessWire classes are in the Processwire namespace (check out the namespace declaration at the top of any given core PHP file). So you need to type-hint against the class in that namespace: function functionName(\Processwire\Page $item) { //... } Alternatively, you can alias the Page class for the file with your function, or put that file in the Processwire namespace itself. At the top of the PHP file, add one of those: // importing the Page class, this tells PHP that `Page` should be interpretated as `\Processwire\Page` <?php use \Processwire\Page; // namespacing the file itself, so ´Page´ will resolve to ´\Processwire\Page´ <?php namespace Processwire; Hope this works for you. Cheers!
    2 points
  10. 2 points
  11. That fixed it. Thanks for the quick fix.
    2 points
  12. Hello, Before outputting anything in your template file, simply check: if ($config->ajax)... eg: If you do not use jQuery – which does this for you – you need to add this key-value-pair to your requests: key: X-Requested-With value: XMLHttpRequest like : request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    2 points
  13. Hey, @jrtderonde! If you have some money to spend, maybe you could approach @dadish and hire him to modify his GraphQL for ProcessWire module to handle Repeaters and Repeater Matrix fields? If you decide to make it open source, this would be an amazing contribution to the community. I also tend to use the RepeaterMatrix as a flexible content builder ?. Suggesting this because you talking about agency and long term plans.
    2 points
  14. I'm not sure, but I was just quickly looking at two modules I've installed - apparently you can do it in two ways: In the module file itself, getModuleInfo(): public static function getModuleInfo() { return array( 'title' => 'myModule', 'version' => '0.1.1', 'nav' => array( array( 'url' => 'export/', 'label' => 'Export', 'icon' => 'arrow-right', ), array( 'url' => 'import/', 'label' => 'Import', 'icon' => 'arrow-left' ), array( 'url' => 'restore/', 'label' => 'Restore', 'icon' => 'reply' ) ) ); } taken from the great Migrator module (shortened) Or with a JSON file named ModuleName.info.json: { "title": "Module Name", "page": { "name": "db-backups", "parent": "setup", "title": "DB Backups" }, "nav": [ { "url": "backup/", "label": "New Backup", "icon": "plus-circle" }, { "url": "upload/", "label": "Upload", "icon": "upload" } ] } taken from Ryan's Database Backup module.
    2 points
  15. I think it is highly improbable that anyone has the time to write a book on PW in the near future, however, the docs are being updated, extended and reorganized by Ryan so it will probably be a good substitute for a book anyway (and free ? ). See: http://processwire.com/blog/posts/rebuilding-pw-website-part2/
    2 points
  16. THIS. IS. AWESOME. @adrian: A nice guy who never sleeps and always is improving. Master of Tracy and other girls ? Thank you. The autocomplete is, well I said it before: AWESOME!!!!
    2 points
  17. I knew it is easy, after all we’re talking about The #Processwire ? Thanks for the help.
    2 points
  18. News Update - 20 November 2018 Hi all. This update should have gone up about 3 weeks ago :-). Some of you are aware that I've been having computer issues but that's sorted (fingers crossed) for now. Before that, I'd managed to work on and mostly complete a number of things. I'll try and remember them now as I write and hopefully I don't forget anything (these posts not only help with generating discussions but they will be useful references when I get round to doing the docs). Orders First, thanks (especially @arjen and @szabesz) for the feedback on orders and customers. We now have a FieldtypeOrders and FieldtypeOrderItems which store permanent records of orders. The former stores the aggregates (total price, etc) and customer info. The latter stores data about each individual item in the order (name, price, etc). These two simplify querying orders (backend stuff). Customers FieldtypeCustomers stores registered customers' data. It is also useful for frontend customer login areas/dashboards and for returning customers in cases where customers have to login in to make purchases, e.g. retrieve their stored address. Products The products field has been updated to store product dimensions. Shipping This is by far the biggest work to date, I think. It consists of 3 modules, one each for Shipping Zones, Options and Rates. I think it is comprehensive enough to do away with the need to write custom shipping classes although we'll keep that option open. Please note that although the explanation of the underlying logic behind shipping can seem complicated, the GUI for setting up shipping will be quite simple and intuitive to use. Zones Setting up shipping zones To create a shipping zone, you will have first set up countries where you ship to. This is a global setting since it also affects taxes. Once that is sorted, you can start creating shipping zones. You can create as many shipping zones as you need. A shipping zone consists of geographical areas where you ship physical products to. A shipping zone can have as many shipping regions as needed. Regions A shipping region can be any of these: Continent (E.g. Asia) Country (Germany, Lithuania, UAE) State/Province (Kowloon, Goa, Texas, Arezzo) The selection of regions (GUI) will be inbuilt. I.e., shop admins will be able to select Quebec or Nigeria or Asia, etc. In addition, one will be able to further refine their region definitions using postal/zip codes. Postal/Zip codes Postal codes can be matched in several ways: Verbatim: in this case, you specify an exact postcode(s) to match, e.g. CD30 78GH or 18000 Range: For numerical postcodes such as US zip codes, shop admins can specify the desired range(s), e.g. 60001 - 61909 Wild cards: This will match the first n characters of the postal code, e.g. SW* or BH3* You can enter as many postal codes to match as you wish. A customer's shipping address details at checkout are used to match their order to the shipping zones defined in the system. If no match is found, the system defaults to a generic shipping zone/rate if you have one set up AND assuming the customer's delivery address is in a 'place' you ship to. If not match is found at this point, the customer will be shown an error or custom message you set up. Shipping zones examples 1. State/Province + Postcodes Say you ship only to California. It's a big place so you decide to divide up the place , into 5 shipping zones using zip codes. In this example, the first 4 shipping zones are defined by zip code ranges. The fifth zone takes care of any other areas inside California not within the zip code ranges in the first 4 zones. California zone 1: 90001 - 92999 California zone 2: 93000 - 93705 California zone 3: 94200 - 94799 California zone 4: 95000 - 95750 California zone 5: All other zip codes in California In this example, zone #5 will be matched as long as the State in the shipping address state is California and the zip code is not within the ranges described in zones 1 - 4. 2. Country + States/Provinces In this example, you ship only to Canada. You create several zones based on Canadian provinces as follows: Canada zone 1: Alberta, British Columbia Saskatchewan (multiple regions in one zone) Canada zone 2: Ontario, Quebec Canada zone 3: All other provinces In this example, zone #5 (all other provinces) will be matched as long as the shipping address country is Canada and the province is not one of those in zones 1 or 2. 3. Continent In this example, we use continents to set up shipping zones based on groups of countries. For instance, setting up zones for Africa: Africa zone 1: East Africa consisting of the countries (regions) Kenya, Uganda, Tanzania Africa zone 2: West Africa; Gambia, Cameroon, Togo, Niger Africa zone 3: North Africa: Egypt, Tunisia, Morocco Africa zone 4: South Africa: Zimbabwe, Botswana, South Africa In this case, the system will match the country to their continent. Customers do not have to enter 'continent' in their shipping address details :-). Rates Shipping zones alone are useless without rates :-). Padloper 2 allows you to create rates that you can use and reuse across different zones. Please note that rates in this case just define the applicable rates(s) for an order (or part of an order) based on certain conditions. Rates do not specify how much shipment should be paid. We will come to that later below. Rates can be based on: Flat rate: A single flat rate should be paid for this zone. For instance, using our examples above, we can set a flat rate for all shipments to California zone 1 but set a different rate for California zone 3. Price: Shipment is calculated based on price. E.g. if order is worth < $30, charge $2.50 Weight: Shipment rate calculated based on weight of items in basket Quantity: Shipment calculated based on quantity of items in basket Rate Applies To A rate 'based on' value in itself is incomplete. One also needs to specify how the shipment rate will be applied. The choices are: Order: Shipment will be charged PER ORDER (the whole order). This means apply the shipment once irrespective of number of items in the order, or their price or weight, etc. Item: Shipment will be charged PER ITEM in the order. So, if we have 10 items, shipping will be charged 10 times, etc. Item-group: Shipment will be charged once PER EACH MULTIPLES OF THE 'SAME ITEM'. By item-group is meant a product with the SAME ID and same VARIANT ID. So, if we have two shirts, that have the same product ID, but one is orange with variant ID 1 and the other is blue with variant ID 2, those are NOT the same item. If our basket contains 3 of the orange shirts, that is counted as one item-group and if we have 1 blue short, that is another item group. In this case, shipment will be charged once for each item-group - once for the orange shirts and once for the blue shirt. Product-class: This is a special rate so let's examine it more closely. Product Class Shipment is a powerful feature that allows shipment to be calculated based on a product class. A product can only belong to one class at a time. A class is a special category which you use to group similar products for the purposes of shipping. For instance, you can have the following classes: Heavy: This would be a class for heavy items Bulky: A class for bulky items Small Fragile Etc... You can create and use as many product classes as you wish. Product class shipment is used with any combination of the above Rates and Rate Applies To. With product class shipments, you can mix and match as required. For instance, for your Small items, you can use Flat Rate shipping applicable to the whole Order. You could also have a Weight-based Rate that is Applied per Item. Or, a ship your Fragile items using a Quantity Rate applying per Item-group. As you can see, using product class shipments allows for more flexibility within an order. Of course, you can set up different product class rates depending on your shipping zone. For example, you can Flat Rate shipment for Bulky items shipped locally and an Item Rate shipment for Small items shipped to your international customers. Please note that in the case of Rate Applies To Order with respect to Product Class shipment Rate Value Using a Rate Value setting, you can specify what value should be used to determined whether an order has met a shipping criteria. Consider the following examples: €2 shipment will be charged PER ITEM in the basket if the ITEM COSTS at least €15. In this case the Rate Value looks at the price of a single item (i.e, >= €15) in order to apply a €2 per item shipment Now consider a similar example but with a different Rate Value. €2 shipment to be charged PER ITEM in the basket if the TOTAL COST of the basket is at least €15. In this case, the total cost of the basket is used to determine if the threshold has been reached. The last piece of the puzzle is Shipping Methods. You might have a flat rate or a price-based rate, but in relation to time (or level of service) how do you actually deliver the product? Methods Shipping methods are straightforward. You can add as many shipping methods as you want. For instance: Collection: Buyer collects from your shop Same day delivery Standard delivery 3 - 4 Days' delivery Express delivery Etc There are no predefined methods. You set this up yourself to suit your needs. Options Shipping Options complete the shipment system. A shipping option is a combination of a shipping rate and a shipping method. Think of them as a matrix (similar to Table rate shipping in WooCommerce). Shipping options are automatically created based on selected Shipping Rates and Shipping Methods for a zone. The admin then has to enter a shipping fee/charge/cost for each combination of Rate and Method. At checkout, the customer will be presented with the shipping options available to them and how much that would cost them. Here are some examples Shipping Options (Methods X Rates): Options: a 1 x 1 combination You should could offer a Standard Delivery (Method) charging a Flat Rate £10 for all transactions. Options: a 2 x 1 combination Offer Normal and Express shipping charged at a Flat Rate £5 and £10 respectively for either Method. Options: a 1 x 2 combination Offer Standard delivery on all shipping based on Quantity of order and charging $7 if order contains less than 10 items and $4.50 if order contains more than 10 items Options: a 2 x 2 combination Delivery Methods: Collection Normal Same Day Weight-based Rates: Rate 1: Less than 10 kg Rate 2: Greater than 10 kg but less than 20 kg Rate 3: Greater than 20 kg Shipping options will be the matrix of the above methods and rates each with a shipment fee, e.g. Collection x Rate 1: Free Normal x Rate 1: £5 Same Day x Rate 1: £15 Normal x Rate 2: £8 Etc.. Options: a more complex combination: product class rates Delivery Methods Standard Express Product-class Rates + Applies To Small items: quantity-based rate charged per item-group Fragile items: flat rate charged per item Heavy goods: weight-based rate charged per item Rates Small items quantity less than 5: $0.75 Small Items quantity more than 5 but less than 15: $0.50 Heavy goods: less than 30 kg: €10 per product Shipping options will be a matrix of the above methods and rates each with its own shipment fee, e.g. Standard delivery of Small Items if less than 5: $0.75 per item-group Express delivery of Heavy Goods if item < 30 kg: €10 per product You get the idea ? Maximum shipping Cost and Handling Fee Padloper 1 included a maximum shipping cost. This is retained in version 2. In version 2, you can add a handling fee which will be applied PER EACH shipment calculation. So, if shipping applies per order, a handling fee is applied once. If shipping applies once per product class in an order, handling fee will be charged x the number of eligible product classes in the shipment (e.g. once for Small items, once for Heavy Goods, ETC). Merge Shipping This is work in progress and applies to Product Class-based shipping. It allows the shop admin to specify what shipping rates can be merged in order to use one shipping rate instead of two. For instance, a shopping cart/basket could contain 5 Small Items and 3 Medium-Sized Items. Rather than charge for and ship these separately, the items could be merged and shipped together using the Medium-Sized items rate. Shipping Package This is a planned feature that may not make it in the first release. It is useful where shop owners use delivery services like Fedex where package dimensions and weight or volumetric weight are important factors. I think that's it. I could have forgotten something or could have expressed something better. I'll edit this post if such needs arise.
    2 points
  19. Thanks Robin S, your reply was exactly what I needed in this particular use case ? And to everyone else who replied many thanks as well. I now realize my question was kinda vague, next time I'll try and be a bit more descriptive.
    1 point
  20. I'm wondering what (web?)-app this is. Care to share? Congrats. Very nice, quick loading and slick site. I like the thorough use of sliders/carousels.
    1 point
  21. It's okay to use a page/template for an AJAX response, but if you're using a delayed output approach with an auto-appended main.php file then remember to disable that for your AJAX template: Edit Template > Files > Disable automatic append of file...
    1 point
  22. [joke]You're gonna hate underscore.js then[/joke] ?
    1 point
  23. You can read more about wireCache here: https://processwire.com/blog/posts/processwire-core-updates-2.5.28/#wirecache-upgrades
    1 point
  24. @ripper2600 For sure you can use WireCache to cache number of matches. $cache->preloadFor('matches'); $today_matches = $cache->getFor('matches', 'today'); if(!$today_matches) { $latest_matches = $api->getLatestMatches('today'); $cache->saveFor('matches', 'today', $today_matches, 3600) } $yesterday_matches = $cache->getFor('matches', 'yesterday'); if(!$yesterday_matches) { $latest_matches = $api->getLatestMatches('yesterday'); $cache->saveFor('matches', 'yesterday', $yesterday_matches, 3600) } // setting setting([ 'matches' => WireArray([ 'today' => $today_matches, 'yesterday' => $yesterday_matches ]) ]);
    1 point
  25. Thx Ivan, I was actually more interested in his/her/its response to my statement than in yours ? But yours is a very good one and shows the high quality of this community. Thx - I'm happy to be here and improve! Let's see what he/she/it found out about ecommerce with processwire so far - maybe he/she/it has some valuable information for us... ?
    1 point
  26. hey @kongondo thx for updates some years ago i built an multilang-shop with padloper. there is always an issue with prices based on user-lang or user-location. some shopowner want to force their users to see the a different product-price based on their location. example: user from germany pays: 20€ user from poland pays: 18€ i know this not depends on padloper. but maybe there is a possibility for force-switch the language in the new product-template. or maybe update the price at the checkout if the user enter in the final billing-address. i hope you understand what i mean ?
    1 point
  27. Thanks for your replies clsource and szabesz! I'm glad there's plenty of material to study. This looks like a great CMS! Regards, Carel.
    1 point
  28. Hi Adrian, just wanted to let you know that I just upgraded to latest TracyDebugger and received the following error on the API Explorer Panel: Error: Call to a member function convertNamesToUrls() on null in /Users/glenn/websites/pwmlec/wwwroot/site/assets/cache/FileCompiler/site/modules/TracyDebugger/panels/ApiExplorerPanel.php:205 Stack trace: #0 /Users/glenn/websites/pwmlec/wwwroot/site/assets/cache/FileCompiler/site/modules/TracyDebugger/panels/ApiExplorerPanel.php(108): ApiExplorerPanel->buildTable('adminTheme', Array) #1 /Users/glenn/websites/pwmlec/wwwroot/site/assets/cache/FileCompiler/site/modules/TracyDebugger/tracy-master/src/Tracy/Bar.php(161): ApiExplorerPanel->getPanel() #2 /Users/glenn/websites/pwmlec/wwwroot/site/assets/cache/FileCompiler/site/modules/TracyDebugger/tracy-master/src/Tracy/Bar.php(110): Tracy\Bar->renderPanels() #3 /Users/glenn/websites/pwmlec/wwwroot/site/assets/cache/FileCompiler/site/modules/TracyDebugger/tracy-master/src/Tracy/Debugger.php(294): Tracy\Bar->render() #4 [internal function]: Tracy\Debugger::shutdownHandler() #5 {main} Thanks for the all the improvements! They look really helpful.
    1 point
  29. Typically for ajax calls I host a PW bootstrapped php file at the root of the site (ie above /site/).
    1 point
  30. I don't know what's so confusing about it. I think it's rather this doc: https://processwire.com/api/multi-language-support/code-i18n/
    1 point
  31. I guess the safest approach would be to create a new PageArray Array 3, iterate over Array 1, compare with each item in Array 2 and if not matched, add the Page to Array 3. Or, in code: $array3 = new PageArray(); $array1->each(function($pg) use($array2) { if(! $array2->has($pg)) { $array3->add($pg); } });
    1 point
  32. Last night I did some researches for a client with an old WordPress site. I was looking for german web articles about Processwire, cause the client is from Berlin. I found this article: https://www.sputnika.de/dresden/news/campus-halensis-de-das-neue-online-magazin-der-martin-luther-universitaet-gid-5976 It's about the redesign of this site: https://www.campus-halensis.de/ The old version was a WordPress site, now it runs with ProcessWire.
    1 point
  33. Here the answer to your question :)
    1 point
  34. Welcome to the PW forum, We have similar discussions, you might want to learn tips and tricks there: https://processwire.com/talk/topic/14587-mixing-pages-and-fields-in-a-page-list/ https://processwire.com/talk/topic/16886-linking-pages-to-each-other-without-being-parents/ https://processwire.com/talk/topic/15001-solved-reciprocal-fields/ I recommend reading @clsource's blog post, found here: https://medium.com/@clsource/understanding-processwire-templates-fields-and-pages-201aecd0a1a4
    1 point
  35. Awesome! Glad you found one of my tutorials useful ☺️ also loving the map
    1 point
  36. Thanks @Robin S - really appreciate the quick fix. I actually have ~5000 siblings and it still seems to be working ok.
    1 point
  37. @adrian, I've added a substitute getPageIndex() method to the module so the issue should be fixed in v0.1.12. Probably not as efficient as the core Traversal methods but should be okay unless you have a massive number of siblings.
    1 point
  38. Hello you can use the translation module to create a page for users to translate https://processwire.com/api/multi-language-support/code-i18n/ ?
    1 point
  39. Thanks for the write up, it looks promising indeed! In the long run, my client would be interested in such a feature for sure.
    1 point
  40. So imagine my shock today when i tried to d($var) and nothing happened (well, apart from a few crumbs of debug on the page)! Not again!, I groaned. I scanned Tracy's settings and nothing jumped at me. Everything seemed OK. I even tried debugInfo=>false/true, but no joy. A quick look at the docs and eureka! @options: DETECT | DEVELOPMENT | PRODUCTION @default: DETECT A docker container's IP address would be treated differently! It was being read as site is in 'PRODUCTION'!. A quick change to that setting and normal Tracy was back! You gave me a fright Tracy! I've never seen this side of you. We've only ever met 'locally' ?.
    1 point
  41. https://github.com/processwire/processwire-issues/issues/704
    1 point
  42. Each students and pets table is its own template, each row a page. For a limited set of repeating information like your pet type, a page reference field is most often the way to go. The "pet type" pages can live anywhere in the page tree and can optionally have a PHP template to list all pets of that type. The pets-students relationship can be defined as a page reference field linking from the pet to the student, from the student to the pet or, if you plan to implement complex selectors later on, using the Connect Page Fields module also both ways. The page reference fields can be limited to only show pages with a certain template (student, pet, pet type). Names and age should be regular (text, integer) fields. You could re-use the mandatory title field for the student and pet name and just change the label for it in the individual template's settings. I'd probably also create a "pets" and "students" template (plural) under which the individual entries live in the backend to keep things sorted and allow outputting lists. Home (or another common parent page) Students Heather Rachel ... Pets Rex Thomas ... Pet Type Dog Cat ...
    1 point
  43. https://www.centura.ca We finally put Centura's national website online. A whole journey of a year and a half that began with an important phase of analysis. Centura is one of Canada's leading distributors of floor and wall coverings. Seven administrative regions with different prices, four user profiles each with their privileges, daily synchronization with an inventory system, an ElasticSearch search and a concern for speed, these are the main challenges in the development of this site. And of course, everything must be adapted to the mobile. It is a smooth launch, without drum or trumpet, to stabilize the site, to know the reactions of the administrators, the pleasures and frustrations of the users. The primary goal was to provide a national showcase for this important distributor. Centura has an inventory system that includes tens of thousands of products, the SKUs. However, this large database does not contain descriptions or groupings by collections. So we used ProcessWire as a central data management point, where images and text meet inventory. Since Centura is divided into seven administrative regions with 14 distribution centres, it was essential to leave control over the local aspects of these divisions, such as opening hours, collections sold in the region, promotions, etc. In addition, one region, London and Windsor, is going it alone. It was important to add it to the list and direct the visitor to the right region (and another CMS). As a distributor, Centura deals with retailers to sell its products. He also negotiates at a higher level with architects and construction contractors. Four types of users were identified. The consumer who can keep favorites in his profile. The retailer who can query the central inventory and who has access to promotions and other documents. The architect or designer who can order free samples. The installer interested in ordering tools or installation products. Apart from consumers, the remaining types of members are managed (approved) by each branch. The branch administrators therefore had to be able to "visualize", to locate this information in the tree structure. This involved creating four types of profiles, four main forms, allowing everyone to have a custom dashboard. People registered in these profiles are registered "under" each branch. Let us not forget the daily synchronization, done at night, in order to reflect new products, price changes, etc.. Since the new SKUs are "orphaned" before being associated to a collection, the import mechanism, inspired at the base of the module the Ryan Cramer, Import Pages from CSV, allows to identify what has been changed from what is new. This last category is placed in the tree structure under a page called Orphaned Skus. The site managers can create collections in the Products section and associates through a Page reference field the SKUs. When saving, since SKUs now have an association, they are pushed under the collection, so administrators can easily spot what belongs to what. This large site required the intensive use of various fields, including Page reference, Repeater Matrix. The ProFields collection was a great help, as was ListerPro, widely used by both us and the administrators. What a choice to create batch actions with custom actions! This last module would however deserve a little more love, because it is a little more difficult to understand for the administrator user. We also used Form Builder, but the programmers preferred to build the four main forms themselves, as too much interaction was needed with other components of the site. The documentation for this module is anemic. If the use of the module speaks for itself, its use by API is immediately more cryptic. However, we promise to explore it further in order to leave as much control as possible to the administrator. Another aspect that could be improved in ProcessWire is language management. The internationalization system is fantastic and we have nothing to say about it, but the translation of coded texts is rather hell. For the administrator user, finding a translation and modifying it is very complicated and sometimes incomprehensible. Indeed, what is marked by the __("token") function can be changed in two places for each translation (English and French in our case). But when the time comes to modify these translations again, you have to remember that it is the "token" that governs the search... Moreover, unlike Drupal, if identical __("token") commands are found in different PHP files, you have to translate them in several places! There are ways to use pages inside the site to feed all translations of the site, this takes time and good planning. You can also use a central translation file, but with large sites, it can become cumbersome. There are two types of search in this site. The search by facets of coating products required the use of Elasticsearch which allowed an almost instantaneous display of products. For tools and installation products, we used a more standard model, built with ProcessWire. Why two search systems? There was a questions of time and budget here. The second search is quite different in practice. Caching the site was of course important. Several obstacles to this one: the use of numerous cookies, in particular those of the choice of region and language by the user, the regional variation of the prices, the variation also of the collections to show. Finally, nginx in node balancing. Unfortunately, we could not use the ProCache module, depending on the htaccess file. It is possible to translate ProCache rules into nginx rules. We were, however, reluctant to depend on something over which we had little control. So we opted instead to pull the strings of the cache, as we saw fit, with the features of ProcessWire. It should also be remembered that nginx also has very powerful caching features. In the near future, however, we will revisit ProCache. We, at Spiria, love working with ProcessWire. The more we create sites with this CMS, the more we develop reusable components, which greatly speeds up the creation process. At the moment, we are setting up four "microsites" of products for another company. These sites will then be linked to a central site for "information exchange". In another case, we have to create two sites that operate in parallel. The customer wants to manage the information in a single site while pushing to a second certain information. Our main challenge remains the notoriety of ProcessWire which is sometimes difficult to sell against giants such as Wordpress and Drupal. However, by creating sites the size of Centura, we are confident that this great CMF/CMS will take its rightful place. Speaking of Centura, the journey continues. An online shop will be created, as well as a blog. ProcessWire makes it possible to create solutions adapted to each company without escalating costs. In other words, you can easily do without Wordpress plugins or Drupal modules, because a customized solution is always more profitable than trying to deconstruct what others have done. Translated with www.DeepL.com/Translator
    1 point
  44. If you're looking to do an elaborate a page builder with ProcessWire and don't want to pull your hair out, I highly recommended viewing this video: A couple notes: with the css grid specification, you can assign multiple blocks to the same grid-area but they will overlap each other. I've "overcome" this by combining multiple blocks into a parent div and assigning that instead. pretty easy to do. i didn't demonstrate it, if your blocks have a grid structure within them (like built with flexbox), you can still assign that block to a grid-area. so if your blocks themselves have a grid structure, that's ok. for example, if your css grid layout is 6 columns, but you have a block that has a grid inside of it (built with like uikit's grid that's 5 columns), you can assign that block to the grid-area. with the css grid specification, the flow of the blocks does not have to match the flow of the grid-areas. this is insanely powerful. Enjoy.
    1 point
  45. Sometimes you want to add a specific markup to the values inside a pagetable field. Fe. you want to add a FontAwesome icon in front of the value or you want to add a background-color. In this little tutorial I want to show you how you can do that with only a view lines of code. In the following example I will add the background color red to a specific field inside the page table. This is what i looks like at default: After the manipulation it will looks like this: You can see the difference in the second field of the pagetable. This manipulation makes not really sense and it is only to show you what can be done. You can manipulate it also in other ways. Fe to add different colors to different status of orders and so on. To achive this we use the following type of hook: Fieldtype::markupValue (read more at https://processwire.com/api/ref/fieldtype/markup-value/) In this case we put the code inside the init.php and not in the ready.php. The reason for this is, that if the page table will be updated via Ajax, the default values will be displayed again if the code is in ready.php. So here we go: $this->addHookAfter('Fieldtype::markupValue', function($event) { $page = $event->arguments(0); $field = $event->arguments(1); $value = $event->arguments(2); if($field->name == 'nameofthefield' && $page->template->name == 'childrentemplatename') { $event->return = '<span style="background:red;color:#fff;">'.$value.'</span>'; } }); First we define all the variables needed for the manipulation ($page, $field and $value). $value returns the formatted value of the field. If you need the unformatted value (fe. if it is a date field and you want the timestamp) you get the unformatted value by using this line of code: $value = $page->getUnformatted($field->name); We restrict it to a special field of the pagetable called "nameofthefield" - rename it to the name of your field where you want the manipulation to take place. So this field of the pagetable is a field of a child template. In this case the child template is called "childrentemplatename". You have to rename it to your child template name. Only to clearify: each field inside the pagetable is part of a child page. "$event->return" returns the output of the field (usually the value). In this example the value of the field should be between two <span> elements with a special CSS styling to add a red background and turn the color of black to white. Here is a usecase where I use this technique: You can see that after the end date there is no time (in opposition to the start date). Instead of the time the text "no specific endtime" is displayed. This is because this event has no specific end time (it is open end). Only to mention: The editor can choose if the event is all day long or starts and ends on specific date and time or it is open end. Thats all and happy coding with your own manipulations!
    1 point
  46. Great, @mel47! But I was not actually advising to add an extra field to the template. I thought it would be enough just to write $page->sizeColumn = 2; before the render call. It would add a temporary property to the current $page object, which will be passed to field template (but will not write this field to database).
    1 point
  47. Hey, @mel47! Here is the code for the method. Seems like the 3rd parameter is to override default field value, not to provide additional variables to the template: @param mixed|null $value Optionally specify value to render, otherwise it will be pulled from this $page. Did not try this, but you probably can do this to have additional variable in the field template context: // main template $page->$sizeColumn = 2; $content .= $thanks_page->render('images', '/images_column'); // images_column.php foreach($value as $image) { echo " <article class='column is-{$page->sizeColumn}'> .... }
    1 point
  48. Wire Queue Wire Queue is a module that allows easy creation and usage of Queues in ProcessWire. It is based upon a basic parent module (WireQueue) that should have one or multiple StorageHandler modules installed too. This beta release contains a simple plain text storage module, WireQueueTextfile, and a simple Sqlite3-DB storage module, WireQueueSqlite3. The base module creates the needed: FIELDS (wire_queue_type, wire_queue_state, wire_queue_storage_options) TEMPLATES (wire-queue-container, wire-queue-storage, wire-queue-tools) PAGES (wire-queues = container for all Queuepages, wire-queue-storages = container for StoragetypePages) ROLE (wire-queue-admin) Each storage module creates one page under wire-queue-storages. New Queues can be created in the backend by adding a new page under "Wire Queues". After creation one have to select a Storage type from availables list and publish the page. After that, there are some buttons available to start / pause / and close the queue. Putting and getting data to and from the queue is done via API calls. First you have to get the page that holds the queue object. // get and validate the queue handle if($queue = $pages->get(SELECTOR_TO_DESIRED_PAGE)->wireQueue()) { // wrap your data into an array and pass it into the queue $success = $queue->addItem($data); ... } // get and validate the queue handle if($queue = $pages->get(SELECTOR_TO_DESIRED_PAGE)->wireQueue()) { $data = $queue->getItem(); ... } Thats basically all what you want from a queue. Following are a few conveniences: $queue->getPage()->title gives you the title of the queue, ($queue->getPage() returns the page object) $queue->className() shows the StorageType of the queue $queue->getState() and $queue->getStateStr() returns the current state of a queue: 1 = new / empty 2 = enabled / running 3 = paused 4 = closed / archived $queue->itemCount() gives the total number of all pending items in the queue Here is code that gives an overview of all Queues in the system: $container = $pages->get('template=wire-queue-container'); $bodyContent = "<h1>$container->title</h1>"; $bodyContent .= "<p>There are currently {$container->numChildren} Queues defined:</p>"; $bodyContent .= "<ul>"; foreach($container->children() as $p) { if(! ($queue = $p->wireQueue())) continue; $bodyContent .= "<li>{$queue->getPage()->title}<ul>"; if(!$queue->ready2use()) { $bodyContent .= "<li>{$queue->className}</li>"; $bodyContent .= "<li>This Storagetype is not ready to use! The System seems not to provide all requirements.</li>"; $bodyContent .= "</ul></li>"; continue; } $bodyContent .= "<li>{$queue->className}</li>"; $bodyContent .= "<li>{$queue->getStateStr()} ({$queue->getState()})</li>"; $bodyContent .= "<li>Currently are {$queue->itemCount()} items pending!</li>"; $bodyContent .= "</ul></li>"; } $bodyContent .= "</ul>"; Following is a screenshot of the backend The module is available in the modules directory: http://modules.processwire.com/modules/wire-queue/ Or you get it from Github: https://github.com/horst-n/WireQueue. . . . The Sqlite3 storage handler not only let you push and pull data items to and from it, it also can keep track of the current state of a record. If you use multiple / different workers for pulling and processing the data, you can store an ID for them too. This is how the DB Table looks like: The Wire Queue Sqlite3 storage handler provides the methods addItem($arrayData) // same as WireQueueTextfile getItem($worker = null) // same as WireQueueTextfile, (but the textfile storage cannot support $worker!) updateItemState($id, $state, $worker = null) // this you can use for further processing, if you want . addItem($arrayData) $arrayData is a, maybe associative, array containing all your data for one record. The method stores it under the next free id under data in the Sqlite-DB-file and sets the state to 0. The field worker is empty for new added records. Following is a basic working example for pushing data records into a queue: // you have created a queue in PW, the ID of the page is 1420 for example // here is some skeleton code for an importer that uses this queue // get and validate the queue handle if(! ($queue = $pages->get('id=1420')->wireQueue())) exit(); // we could not get the Queuepage // now start to scan / read data for your imports, wrap each record into an array and put it into the queue foreach($pages->find(YOURSELECTOR) as $p) { $data = array($p->field1, $p->field2); $queue->addItem($data); } . . getItem($worker = null) $worker is an integer that you can define to suite your needs. If you don't use or don't want identify multiple workers, just ommit it. The method pulls one pending record from the queue, changes the state from 0 to 1, and returns an associative array with the keys id and data. array('id' => integer, 'data' => array) You will need the id if you further want to use the queue to keep track of processing steps. You must pull your stored $data from $array['data'] and use the id for further storing the state. . updateItemState($id, $state, $worker = null) $id identifys the record a worker has processed, for $state you can define and use every integer you like, but not 0 or 1. If you also want to store altered data, and not only different states, you can use updateItem($id, $state, $worker = null, $data = null) instead of updateItemState(). . Here is a working example with a bit pseudo code how workers can get a pending record to process it further and store back the result of the process: // you have created a queue in PW, the ID of the page is 1420 for example // here is some skeleton code for an importer that uses this queue // get and validate the queue handle if(! ($queue = $pages->get('id=1420')->wireQueue())) exit(); // we could not get the Queuepage // we do not use different workers here in that example, so we do not pass an worker id here $tmp = $queue->getItem(); // get a record from the queue $record_id = $tmp['id']; // store the record id $data = $tmp['data']; // get the data array // process the $data ... // and get the result of the process, in this example true or false $result = processMyRecord($data); // as new records have a state = 0, fetched records have a state = 1, // you may define a state of 2 for successful processed records, and higher ones for failed records $state = true === $result ? 2 : 3; $queue->updateItemState($record_id, $state); . . getItem4FurtherProcessing($state, $worker = null) The $state you pass to the method is the state you want get the record for. If there is one pending, its state will be set +1 and the id and data is passed back to you in an associative array: array('id' => integer, 'data' => array). --------- Here is a pseudo code example how (multiple) worker scripts may batch process queue records with the sqlite storage handler // on the server in this example, everyscript will timeout / die after 180 seconds // we start a timer $time = Debug::timer(); // we use different instances of workers that pull and process records from the queue, // so additionally to the processings states, we also want to store the worker ids $worker = 2000; // now start to process items by pulling one after the other from the queue while(150 > Debug::timer($time)) { $tmp = $queue->getItem($worker); // get a record from the queue if(!$tmp) continue; // maybe currently are no pending records available $record_id = $tmp['id']; // store the record id $data = $tmp['data']; // get the data array $result = processMyRecord($data); // process the data and get back the result $state = true === $result ? 2 : 3; // define an integer for the new state, according to the result of processing the data $queue->updateItemState($record_id, $state, $worker); } // we are close to the timeout for the script, so finish it with calling it itself to get a fresh run $session->redirect('./');
    1 point
×
×
  • Create New...