Jump to content


  • Posts

  • Joined

  • Last visited

  • Days Won


olafgleba last won the day on May 19 2022

olafgleba had the most liked content!

Profile Information

  • Location
    Cologne, Germany

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

olafgleba's Achievements

Sr. Member

Sr. Member (5/6)



  1. Hi @adrian, strange. I did exactly what you suggested,- add a new RM field, add just one type with one table(ProField) field. Even with the similiar values ;-). Also placed it on a template with no further RM field (prevent possible interfering). Same module/field/PW versions as posted before. On top with an upgraded PW (latest dev (3.0.237)). The views remains broken(s. screen). I suppose your RM version is set to latest (0.1.1.)? If so, maybe there is a change left where to look at. Otherwise i am running out of ideas i am afraid ;-( But regardless the result, i really appreciate your answers and willing to help! ?
  2. Hi again @adrian, it turned out the module just doesn't work well if the table field is part of a RepeaterMatrix (Pro)Field (at least with my version combi). Beyond this the table field + your module works flawlessly ;-). And because my abilities are still limited with module building, probably i'll can't fix it by myself and send a Repo PR, sorry. Processwire 3.0.221/224 dev, ProFields Table 0.2.4, ProFields Repeater Matrix 0.0.9, latest Table CSV Import/Export Module (2.0.16). Because i didn't mentioned it so far,- thanks and kudos to you for the module.
  3. Hi @adrian, thanks for your suggestion. I will try it on a clean install on sunday. And will try the module on other installations (with a vary set of modules) just for testing. I'll get back to this...
  4. Hi Adrian, i upgraded PW to latest dev in my local environment. No luck, same slightly broken view and behaviour. No suspicious logs (browser console, tracy). Hm, whats left to do is to renew my Profield package license to get the latest Profield Table version. Although i also doubt this causes the issue.
  5. Nothing really helpful, i am afraid. A forgotten console.log from jquery ("JQMIGRATE: Migrate is installed, version 1.4.1"), but no related warnings or errors. It not a browser issue also,- tested in Firefox, Safari, Chrome. I installed the module on the live server. Next i will install the module on my local project instance and upgrade PW to the latest dev to test it there. Thx for your answer, regards, Olaf
  6. Hi @adrian, maybe i doing something wrong? Logged in as superuser, i installed the module, published the permissions and tried to export data of a existing table as a test. The interface looks a bit broken (s. below). For example i cannot add or delete Talbe rows selectors and nothing happens when i click on "Export As CSV" also. Do you have any idea, what i am missing? Cleared cache several times, logged out/in. Maybe it is a jquery core issue? Thx in advance Olaf Processwire 3.0.221 dev, ProFields Table 0.2.4, Latest Table CSV Import/Export Module (2.0.16)
  7. Hi @kongondo, many thanks for you work/module. In a current project this would become very handy to use. My problem probably sits between my eyes,- i managed to define pages for rows and columns (s. Screenshot). But when i enter values/data in the matrix fields and save the page, no data is saved, e.g. i get blank matrix fields on page revisit. Tracy spits out following warnings: - Latest Matrix module version - PW 3.0.221 dev - PHP 8.0.27 Any idea what goes wrong here? Thx and regards Olaf P.S. I wonder if the admin view should be look like in the screenshot above? Seems a bit broken to me (s. Import/Export row), because it differs from other module screenshots i have seen.
  8. Hi @bernhard, oh, so far i was not aware of `$page->createdUser`,- stopped scrolling the $page API documentation at System section ;-). Might be rather helpful, as it possibly spares the need for the whole utility hook. I'll have a look... I tried to achieve this using hooks or segments as you suggested. But no luck, so i gave up at some point (time was already limited). Although i completely agree with you, the current approach seems to be rather stable. So i stick with it for now.
  9. Hi, What's it all about I'd like to share some thoughts and approaches i took to meet the customers requirements when i built the website. Maybe someone has (or will have) similiar needs and can benefit from it, hopefully. I will focus mostly on technical stuff. And because of that i am not sure, if the showcase section is the place to post it (?). Its more like a testimonial. About the organisation/website https://vdp-ev.de (german language only) The Verband Deutscher Puppentheater e.V. (VDP) is the cultural policy respresentation for professional puppet stages in Germany. The VDP gives advice, shares knowledge and represents currently 140 member stages. They publish a magazine frequently, giving awards, workshops and organize events for their members. As the VDP was founded many years ago, they have lots and lots of contents, each with individual structural needs . So the first goal was to pack the vast of content in a good structure and make sure visitors has access to it as straight as possible. Therefor the grafical presentation is rather plain, not really stand out or comes with fancy stuff ?. Some random screens of the public website: Some random screens of the (secured) members marketplace (explanation s. below): Seeing no way to set skip links, here is a short plain TOC: 1. Building a marketplace for VDP members 1.1 Requirements and conceptual thoughts 1.2 Building the menu 1.3 Keep it maintainable 2. Publish selected members content on the public website 2.1 Clients request 2.2 Starting, first approach 2.3 Solution 1. Building a marketplace for VDP members 1.1 Requirements and conceptual thoughts Before the relaunch, members (they have a special user role) yet could login into the backend and edit their contents (data for their stage + add/edit productions). With help of @adrians AdminRestrictBranch they only see their own stage and productions. Those users have no access to other menu trees. With the relaunch the VDP wants to have something different/more elaborated: A secured member area (e.g. marketplace) where all members can share and swap ideas, informations, leading discussion related to their profession, offer or search for profession related material (like puppets, technical gear, services etc.) and some more. Just like some kind of a simple forum (with maintaining their own entries, s. below), but without the overhead of a standalone forum software. There was a (only grafical) approach how to do this by the former developer. His idea was to establish a secured member frontend where the loggedin user have (frontend) presentations to manage their own marketplace content, their stage and productions and even their profile a least. Well, some kind of reinventing the backend... Beside the fixed budged, i thought of a better way to do this. First, i wanted to seperate the marketplace output from the input strictly. Without real advantage member users shouldn't be forced to learn something they're already familiar with (Backend login and add/edit their stage and productions). Second, all marketplace content should live within the PW ecosystem, e.g. are template base pages that lives in the page tree. At least for administrators to see/edit... (s. below also). So, where to establish the CRUD actions for the members content of the marketplace section for each member user (with that special user role)? It all leads to adapt the PW Backend Menu. The Module CustomAdminMenus by @Robin S seems to give a good start for this. The setup for static pages is rather simple. But for my use case i had to deal with a much more dynamic approach. Luckily the module is hookable... I wanted to have two Menus: Mein Markplatz which displays all CRUD actions for the sections (Figuren, Bühne, Material, Tipps & Tricks a.s.o., s. image above) and Zum Markplatz Portal which links to the members marketplace overview page. 1.2 Building the menu First we get the current user content, populate the dynamic menu and build two menus at least. /** * Build Marketplace Admin Menu * * Backend interface for vdp members to add/edit pages marketplace * sections. * * Each member only will see entries that they created. */ $wire->addHookAfter('CustomAdminMenus::getMenuChildren', function(HookEvent $event) { // The menu number is the first argument $menu_number = $event->arguments(0); // Handle Menu 1 if($menu_number === 1) { // Get the current user $user = $event->wire()->user; // Hide admin menu except for vdp members if ($user->isSuperuser() || !$user->hasRole('theater')) { // Do not show the menu $event->return = false; } else { // init $hints_children = []; $offers_children = []; $blog_children = []; $discussions_children = []; // Get all hints for current user $hints = $event->wire()->pages->findRaw("template=members-marketplace-hint, created_users_id=$user.id", ['title', 'url', 'id']); // Allow to add a new hint // Adapt values to fit your environment $hints_children = [ [ 'icon' => 'plus-circle', 'label' => 'Neuen Eintrag anlegen', 'url' => $event->wire()->config->urls->root.'vdpadm/page/add/?parent_id=4232', 'newtab' => false, ] ]; // Build from results // Adapt values to fit your environment foreach($hints as $hint) { $hints_children[] = [ 'icon' => 'file-text-o', 'label' => $hint['title'], 'url' => $event->wire()->config->urls->root.'vdpadm/page/edit/?id='.$hint['id'], 'newtab' => false, ]; } // ... repeat for other marketplace sections ... // Build the menu $menu1 = [ [ 'icon' => 'thumbs-up', 'label' => 'Tipps und Tricks', 'url' => '', 'newtab' => false, 'children' => $hints_children, ] // ... repeat for other marketplace sections ..., don't forget the `,` ;-) ]; // Return the menu $event->return = $menu1; } } // Handle Menu 2 (Link to marketplace portal) if($menu_number === 2) { // Get the current user $user = $event->wire()->user; // Hide admin menu except for vdp members if ($user->isSuperuser() || !$user->hasRole('theater')) { // Do not show the menu $event->return = false; } else { $menu2 = []; // Return the menu $event->return = $menu2; } } }); FYI: Code is part of a ready.php file which lives in /site (s. also https://processwire.com/docs/modules/hooks/) After that the PW top header looks like this: If you click on Mein Markplatz, you get the verbose list*. *) The member user for the demo has no existing entries, so you see none (only the link to add a new entry). 1.3 Keep it maintainable As an administrator i must be able to identify which entry is related to which member. So i prepend the user name to pages names programmatically. This is done by a little helper hook. All relevant templates has a hidden field named `creator`, which gets populated with the user name on page save. To avoid overriding the value when a adminstrator edits an entry, its packed in a condition. Very important, because otherwise the assignment (entry <> member) gets lost! All entries edited by a administrator would have a new owner ?. Definitely not what we want... Btw. the user role `theater` in this example represents the special role for members mentioned above. /** * Utility hook for `Build Marketplace Admin Menu` * * Field `creator` is a hidden page field, that is populated * on page save with the username of the created user. Only related * to members marketplace pages. * * Prevent superuser to change `created user` while edit/save * the page for administration purposes. * * We need this hook on page save, because we want to show * the username within the admin page sidebar list ($page * only provide the ID of the created user). */ if (!$user->isSuperuser() && $user->hasRole('theater')) { $wire->addHookBefore('Pages::saved', function($event){ $page = $event->arguments(0); if($page->hasField("creator")) { // Get the current user $user = $event->wire()->user; // Populate template field for use in the admin sidebar list $page->creator = $user->name; // Save back to $page $page->save('creator'); } }); } FYI: Code is part of a ready.php file which lives in /site (s. also https://processwire.com/docs/modules/hooks/) The complete ready.php vdp-marketplace-demo-ready.php So this is it, mainly. As there are two ways to go after a successful login (Backend view to edit/add content or Frontend view of the members marketplace overview page) the members must decide where they want to go first. Btw, after the user is logged in, the navigation changes within the member frontend scope. So the user can switch between frontend and backend view each with one single click. 2. Publish selected members content on the public website 2.1 Clients request After the relaunch was online, the customer had another request: They wanted to let members decide if their entries (of particular sections of the member marketplace) should also be integrated/viewable on the public website. Visually identical to the views within the member marketplace. Those (public) pages should be available in the navigation and also be indexable by search engines. While this seems not quite a too difficult task at first glance, it turns out to be a bit more tricky. The reason are the restrictions of page paths when dealing with the (secured)members and public scope. Note: All sections in the members marketplace are applied as blog pages, e.g. each has a parent page and associated children. 2.2 Starting, first approach First, i add a conditional checkbox to all related marketplace templates, which was needed in any case. But than it gets a bit annoying. Having kind of a (public) placeholder template (and page based on it) and then iterating over the member blog entries was a dead end. The `children->url` path of course leads to the member section (at that time i wrote a post about this because i hoped this could be solved somehow) As i have to have a real template/page(s) for this (s. 2.1 why), it isn't possible to hook paths here. I realized, i had to find a complete other approach... 2.3 Solution Finally i set up clones for all relevant members section templates (e.g. parent + child template), built public available pages from it and mirror allowed* member entries to the appropriate public pages on a regular base. To avoid disruption, those public pages are readonly for editors. *) Entries with a particular option set (e.g. Beitrag auch auf der öffentlichen VDP Website anzeigen?, s. picture above) The synchronisation between the members area and the public clones is done by cronjobs for each section. Example sync file for one section (VDP Blog): <?php namespace ProcessWire; /** * Synchronize members < > public blog pages * * This file is meant to get executed by a cronjob * * 1. Synchronize (add, update, delete) * 2. Synchronize (delete) * 3. Log, Mail */ // bootstrap processwire, adapt to your path include("../index.php"); // init $d = array(); $o = array(); $totalcountUpdate = ''; $totalcountNew = ''; $totalcountDeleted = ''; // Get member blog items $members_marketplace_blog_items = $pages->find('template=members-marketplace-blog-item'); // Iterate over member blog items, save `name` foreach($members_marketplace_blog_items as $item) { $d[] = $item->name; } reset($d); /** * 1. Synchronize (add, update, delete) * * Perfom synchronisation based on existing member blog items */ // Iterate over members blog items for ($i=0; $i < count($d); $i++ ) { // Get member blog item by given name from the `d` array. $members_blog_item = $pages->findOne("template=members-marketplace-blog-item, name=$d[$i]"); // Get the related public clone (if exists) $public_blog_item = $pages->findOne("template=blog-vdp-item, name=$members_blog_item->name"); /** * Conditional actions * * 1. if * Update present page * * Verbose: Is yet present in public section && is * allowed to be displayed * * 2. elseif * Add new page * * Verbose: Is not present in public section && is * allowed to be displayed * * 3. elseif * Delete page * * Verbose: Is yet present in public section && is * not allowed to be displayed */ if ($public_blog_item->name == $members_blog_item->name && $members_blog_item->options_members_marketplace_entry_publish_to_public == 1) { $public_blog_item->title = $members_blog_item->title; $public_blog_item->name = $members_blog_item->name; $public_blog_item->headline = $members_blog_item->headline; $public_blog_item->blog_date = $members_blog_item->blog_date; $public_blog_item->page_blog_tags_vdp_blog = $members_blog_item->page_blog_tags_vdp_blog; $public_blog_item->body = $members_blog_item->body; // Save page to get an ID for adding images, s.below $public_blog_item->save(); // Make sure we process an array $public_blog_item->of(false); // Delete images first to avoid duplication $public_blog_item->images_members_marketplace_entry->deleteAll(); // Add images foreach ($members_blog_item->images_members_marketplace_entry as $img) { $public_blog_item->images_members_marketplace_entry->add($img); } // Save page again (just for the sake of it) $public_blog_item->save(); // Save iteration for mail, s. below $totalcountUpdate = $i; } elseif ($public_blog_item->name != $members_blog_item->name && $members_blog_item->options_members_marketplace_entry_publish_to_public == 1) { // Add new page $public_blog_item_add = new Page(); $public_blog_item_add->template = 'blog-vdp-item'; $public_blog_item_add->parent = '/wissenswertes-ueber-puppentheater/vdp-blog/'; $public_blog_item_add->title = $members_blog_item->title; $public_blog_item_add->name = $members_blog_item->name; $public_blog_item_add->headline = $members_blog_item->headline; $public_blog_item_add->blog_date = $members_blog_item->blog_date; $public_blog_item_add->page_blog_tags_vdp_blog = $members_blog_item->page_blog_tags_vdp_blog; $public_blog_item_add->body = $members_blog_item->body; // Save page to get an ID for adding images, s.below $public_blog_item_add->save(); // Make sure we process an array $public_blog_item_add->of(false); // Add images foreach ($members_blog_item->images_members_marketplace_entry as $img) { $public_blog_item_add->images_members_marketplace_entry->add($img); } // Save page again (just for the sake of it) $public_blog_item_add->save(); // Save itereation for mail, s. below $totalcountNew = $i; } elseif ($public_blog_item->name == $members_blog_item->name && $members_blog_item->options_members_marketplace_entry_publish_to_public == 0) { // Delete page $public_blog_item->delete(); } } /** * 2. Synchronize (delete) * * Delete outdated public blog item pages, e.g. do not * exists in the member section any longer. */ // Get all member blog items $_members_marketplace_blog_items = $pages->find('template=members-marketplace-blog-item'); // Iterate over member blog items, save `name` foreach($_members_marketplace_blog_items as $_member_item) { $o[] = $_member_item->name; } reset($o); // Get all public blog items $_public_blog_items = $pages->find("template=blog-vdp-item"); // Delete public blog item if not found in members array. foreach ($_public_blog_items as $_public_blog_item) { if(!in_array($_public_blog_item->name, $o)) { $_public_blog_item->delete(); $totalcountDeleted++; } } /** * 2. Log, Mail (delete) * * Log results in PW and send status mail */ // $log->save("cron", "Update public blog posts - New: $totalcountNew, Updated: $totalcountUpdate, Deleted: $totalcountDeleted"); // $emailTo = '<your-email-address>'; // $emailFrom = '<your-email-address>'; // $emailSubject = "VDP Blog | Cron | balance public to member sections "; // $header = ''; // $header .= "From: ".$emailFrom. "\r\n"; // $header .= "X-Mailer: PHP/" . phpversion(); // $message = ''; // $message .= "Am: ".date('d.m.Y H:i')."\n"; // $message .= "Aktualisierte Blog Beiträge: ".$totalcountUpdate."\n"; // $message .= "Hinzugefügte Blog Beiträge: ".$totalcountNew."\n"; // $message .= "Gelöschte Blog Beiträge: ".$totalcountDeleted."\n"; // mail($emailTo, $emailSubject, $message, $header); die(); ?> FYI: This example codefile can live anywhere, just adapt the path where your PW instance is located Example cronjob sync file: vdp-cronjob-sync-blog.php.zip Have fun ?
  10. Solution: It is necessary to perform a $session->logout() before changing the pass of the current login user. After that, log in again. Btw: The `$u->force_passwd_change` section is only relevant for developers which uses the Force Password Change Module by @adrian. // Rough abstract... // get current (loggedin) user $u = $users->getCurrentUser(); // Important: logout current user BEFORE populating new pass $session->logout(); $u->of(false); $u->pass = 'valid-example-password'; // Probably because we end the session above, this checkbox // is not unchecked automatically. As at this point it // always should get unchecked anyway, we do it by hand, // regardless the actual status. It does no harm... $u->force_passwd_change = 0; $u->save(); $u->of(true); try { // log in with new credentials $u = $session->login($u->name, $pass); if ($u) { $session->redirect($config->urls->root."path/to/url/"); } } catch(WireException $e) { echo $e->getMessage(); }
  11. Hi, Info: I have a secured section on a website. Users with a certain user role has access to this section through a frontend login form. Summary: When i modify and try to save the current user (e.g. the user is logged in) by API, the `pass` field won't get populated with the new value. Instead the field `pass` is cleared, so the user page is set to `unpublished` on save. // Rough abstract... // Info: var `u` represents a instance of the current user $u->of(false); $u->pass = 'valid-example-password'; // DOES NOT get saved, instead the `pass` field gets cleared $u->save(); $u->of(true); As this was proven before my last PW update 3.0.210 > 3.0.221 some weeks ago, i wonder if this behavior relates to the update (?) If so (which would make sense to me for several reasons), the question is: How can i provide those users the ability to change their passwords within the frontend secured section while they are logged in? Re-login doesn't work (Error: Failed login for 'xxx' - Login not allowed) either. // log in with new credentials $u = $session->login($u->name, $pass); What do i miss here? ;-). Many thanks for your thoughts! Olaf
  12. Hi @adrian, At last i took a closer look at the module and fiddle around a bit. As far as i can see, there is no easy way to adding anchor links without some separator at least + preserving multilanguage paths and urls in the processwire context. I am sure, there is a way to do this (as the PW core do handle this quite well), but this demands some more time. And a bit short of time in the coming weeks i am afraid. The current solution is perfectly fine for me...
  13. Since it is not possible to get what i like to have, the final solution now is a complete different one. Referring to the OP: Member blog entries with the option set (e.g Publish to public site additionally?) will be mirrored to a clone of the blog which lives in the public available page tree. To avoid confusion, the public available blog is read only for editors. This happens: a. with a page hook while new member blog entries are applied or present entries are edited b. nightly cronjob to synchronize orphaned or/and deleted entries.
  • Create New...