Jump to content


  • Content Count

  • Joined

  • Last visited

  • Days Won


fruid last won the day on November 5 2020

fruid had the most liked content!

Community Reputation

24 Excellent

About fruid

  • Rank
    Sr. Member

Recent Profile Visitors

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

  1. OK, picking up where I left off months later but still confused. Still trying to figure out how to hook after the event of changing a field. $wire->addHookAfter('Pages::savedPageOrField(snipcart_item_image)', function($event) use($item) { doesn't work. I also tried with… $wire->addHookAfter('Pages::saveField(snipcart_item_image)', function($event) use($item) { which accords to what is described here: https://processwire.com/api/ref/pages/save-field/ but the hook is not triggered and It's not logic to me. For a different hooking-scenario I tried 'Pages::trashed' which doesn't work but also 'Pages::trash' which in turn does work. Why, I know not. Both hooks are described in the API documentation in different places however. (below is the related thread) Help is much appreciated!
  2. OK so I made some adjustments because I figured the best way to remove a row in the table of contents when that page is deleted is to make use of a addHookBefore, because at this point the page "still knows" its parents and siblings. However, if I go that way, updating the table only when saving the table of contents page gets in the way because the script would check the pages (siblings) and since it's a addHookBefore (hooked before it's deleted) it would still exist at this point so basically would be deleted with the hook but also re-added with the hook that runs when the table of contents page is saved. Pretty confusing for newcomers I guess but not for this community, I'm sure you had dilemmas like that yourself before. So I did the following instead: $wire->addHookAfter('Pages::saved', function($event) { if ($page->parent->template == 'magazine' && $page->template != 'tableofcontents') : $parent = $page->parent; $toc = $parent->child("template=tableofcontents"); $rows = $toc->tableofcontents_table; $contents = $page->siblings(true); $page_id = $page->id; $page_url = $page->url; $page_number = $page->page_number; $page_title = $page->title; $toc->of(false); // creating a new row for this page if ($rows->has("content_id=$page_id") == false) { $item = $rows->makeBlankItem(); $item->content_id = $page_id; $item->content_url = $page_url; $item->content_title = $page_title; $item->content_page = $page_number; $rows->add($item); $event->message("row created"); // updating an existing row for this page } else { $item = $rows->get("content_id=$page_id"); $item->content_url = $page_url; $item->content_title = $page_title; $item->content_page = $page_number; $event->message("row updated"); } // this is new: also loop through the rows and delete any which don't have a corresponding page foreach ($rows as $row) : $row_id = $row->content_id; if ($contents->has("id=$row_id") == false) { $rows->remove($row); $event->message("row deleted"); } endforeach; $toc->save("tableofcontents_table"); $toc->of(true); endif; }); and the other hook: $wire->addHookBefore('Pages::trashed', function($event) { $page = $event->arguments[0]; $parent = $page->parent; if ($parent->template == 'magazine') : $toc = $parent->child("template=tableofcontents"); $rows = $toc->tableofcontents_table; $page_id = $page->id; $toc->of(false); $item = $rows->get("content_id=$page_id"); $rows->remove($item); $event->message("row deleted"); $toc->save("tableofcontents_table"); $toc->of(true); endif; }); creating, unpublishing, hiding and editing pages do change the rows of the table of contents as intended but I can't get it to delete a row when trashed. Any ideas why?
  3. need to continue this thread, trying to improve my script but I'm out of ideas. What I want is a hook that adds a row to the table-of-contents-table with certain details when a page (sibling to the table-of-content) is saved and removed when it's changing status, i.e. trashed, hidden or unpublished. I tried in many ways already, I never get it to do what I need upon trashing a page plus the code was getting too complicated anyway so I tried something else. I just want the rows to be created or deleted when the table-of-contents-page itself is saved. Then, when I save any other siblingspage, just run a hook that saves the table-of-contents-page. Sounds like a plan? The code is quite short… But again, I just can't seem to hook the trash event. My guess is that when I'm trashing the page, the parent and siblings change and so I cannot refer to them this way? $wire->addHookafter('Pages::saved', function($event) { $page = $event->arguments[0]; if ($page->parent->template == 'magazine' && $page->template != 'tableofcontents') : $page->siblings("template=tableofcontents")->save("tableofcontents_table"); endif; if ($page->template == 'tableofcontents') : $parent = $page->parent; $page->of(false); $toc = $page->tableofcontents_table; $contents = $page->siblings(true); foreach ($contents as $content) : $content_id = (int)$content->id; $content_url = $content->url; $content_page = $content->page_number; $content_title = $content->title; if ($parent->child("id=$content_id") == false) { $item = $toc->get("content_id=$content_id"); $toc->remove($item); $event->message("deleted row"); } else { if ($toc->has("content_id=$content_id") == false) { $item = $toc->makeBlankItem(); $item->content_url = $content_url; $item->content_page = $content_page; $item->content_title = $content_title; $item->content_id = $content_id; $toc->add($item); $event->message("create row"); } else { $item = $toc->get("content_id=$content_id"); $item->content_url = $content_url; $item->content_title = $content_title; $item->content_page = $content_page; $event->message("update row"); } } endforeach; $page->save("tableofcontents_table"); $page->of(true); endif; });
  4. need to dig this thread. I have a setup where the page parents's and page parents parents's name and title are repetitive, so when filtering certain children of a specific page, I can never just filter for their parent's name or title, nor for their parent's parent name or title. Long story short, in order to uniquely identify a page, I would need something better than to filter by id, because that one is never apparent. So I tried with filtering "path" "contains", but obviously I need to install this PagePaths module as it says below the filter options. But that module is nowhere to be found… What to do?
  5. yes, again, the issue was something like that. Many thanks!
  6. I managed to accomplish all that I mentioned here and what I internded. I can now batch create pages with a csv file and on these pages batch import images and create a new repeater-item for each image and add a html caption for each image. One more small thing though, more out of curiosity than out of requirement in my case, how can I sort the repeater images according to the image-filename in use? page when iterating is $p repeater field is $image_and_caption image field on that repeater is $post_image Not sure if anyone else would need an AdminAction like that, if so, I could share it publicly. cheers
  7. @adrian you were exactly right, unlinking before returning was the issue. My bad, the reason it didn't work was a stupid mistake from my side not even worth mentioning. AdminActions is awesome, now that I get the hang of it I can make those actions that I code and that I change the arguments of manually in the code easily usable for the client, no need for them to ever look at code, just clicky-clicky. That said, I do still have a small issue that may or may not have to do with the module (probably not but I mention it anyway). When I do $img_field->deleteAll(); ($img_field being $p->images; see above) it deletes the images alright but leaves "orphaned images" (I think that's what it's called, i.e. images with 0KB in the page. Any ideas what that's about? I also tried ->removeVariations(); before ->deleteAll(); but no luck. Thanks a lot!
  8. I tried exactly that just now, doesn't work though, unfortunately the .csv file stays in /site/assets/files/1443/ folder
  9. protected function defineOptions() { return array( array( 'name' => 'csv_upload', 'label' => 'CSV upload', 'description' => 'Upload a CSV file.', 'type' => 'file', 'extensions' => 'csv txt', 'overwrite' => true, 'maxFiles' => 1, 'required' => true, ), array( 'name' => 'template', 'label' => 'Template', 'description' => 'Choose the template you want to import content to', 'type' => 'select', 'required' => true, 'options' => array( '1' => 'article', '2' => 'blog_repeater' ), ), array( 'name' => 'deleteimages', 'label' => 'delete current images', 'description' => 'delete current images', 'type' => 'checkbox' ), array( 'name' => 'addimages', 'label' => 'add new images', 'description' => 'add new images', 'type' => 'checkbox' ) ); } protected function executeAction($options) { $parent = ''; $parent_title = ''; $url = config()->paths->assets.'cache/AdminActions/'; if(count($options['csv_upload'])) { $n = 0; $file = $options['csv_upload']->first(); $fp = fopen($file->filename, 'r'); if ($options['template'] != 2) : // different actions for different templates while(($row = fgetcsv($fp)) !== false) { if(++$n === 1) : // skip the csv-file's first line continue; endif; if ($options['template'] == 1) : list($parent_title, $title, $author, $summary, $body, $page_number, $img_folder) = $row; $parent = pages()->get("template=magazine, title=$parent_title"); $name = sanitizer()->pageName($title, true); if ($parent->id != 0) : // check if parent exists if($parent->hasChildren("name=$name") == false) { // create the page if it doesn't exist $p = new Page(); $p->template = 'article'; $p->parent = $parent; $p->name = $name; $this->message("Creating new page: $p->path"); } else { // ...or merely update it $p = $parent->child("name=$name"); $this->message("Updating existing page: $p->path"); } else: $this->message("$parent_title has the wrong template"); endif; // set the field's values according to the csv-input $p->title = $title; $p->author = $author; $p->summary = $summary; $p->body = $body; $p->page_number = $page_number; $img_folder = sanitizer()->removeWhitespace($img_folder); $img_field = $p->images; if(!$parent->id) { $this->message("row $n: Sorry parent $parent_title does not exist"); continue; } $specs = array( // prepare the import of the images via ftp 'sourceFolder' => $url.$img_folder.'/', 'destinationPage' => "$p->id", 'deleteFolder' => 0, 'field' => fields()->get('images')->id, ); // optionally DELETE all images before importing (i.e. replacing the images) if ($options['deleteimages'] === 1) : $img_field->deleteAll(); $this->message("images deleted"); endif; // optionally ADD new images if ($options['addimages'] === 1) : if ($options['template'] === 1 || $options['template'] === 2) : modules()->get("ProcessAdminActions")->FtpFilesToPage($specs); $this->message("images added"); endif; endif; $p->of(false); $p->save(); $p->of(true); endif; } else : while(($row = fgetcsv($fp)) !== false) { if(++$n === 1) : // skip the csv-file's first line continue; endif; list($parent, $title, $date, $image, $caption, $img_folder) = $row; $parent = pages()->get("name=blog"); $name = sanitizer()->pageName($title, true); if($parent->hasChildren("name=$name") == false) { // create the page if it doesn't exist $p = new Page(); $p->of(false); $p->template = 'blog_repeater'; $p->parent = $parent; $p->name = $name; $this->message("Creating new page: $p->path"); $p->title = $title; $p->date = $date; $p->save(); $this->successMessage = 'post created'; } else { // ...or merely update it $this->message("Updating existing page: $p->path"); $p = $parent->child("name=$name"); $p->of(false); $p->title = $title; $p->date = $date; $p->save(); $this->successMessage = 'post updated'; } $p = $parent->child("name=$name"); if(!$parent->id) { $this->message("row $n: Sorry parent $parent_title does not exist"); continue; } // optionally DELETE all images before importing (i.e. replacing the images) if ($options['deleteimages'] === 1) : $p->of(false); $p->image_and_caption->deleteAll(); $this->message("images deleted"); $p->save(image_and_caption); $p->of(true); endif; // optionally ADD new images if ($options['addimages'] === 1) : $p->of(false); $img_folder = sanitizer()->removeWhitespace($img_folder); $sourceFolder = $url.$img_folder.'/'; $item = $sourceFolder.$image; $newItem = $p->image_and_caption->getNew(); $newItem->save(); $newItem->post_image->add($item); $newItem->ckeditor_caption = $caption; $newItem->save(); $p->save(image_and_caption); $p->of(true); $this->successMessage = 'image added'; endif; } endif; return true; fclose($fp); } else { $this->failureMessage = 'Please upload a CSV file'; return false; } fclose($fp); // the following are 3 of many attempts to solve the issue, none of them work :( $options['csv_upload']->unlink($file->getPathname()); $options['csv_upload']->unlink($file->filename, $limitPath=true); $this->wire('files')->unlink($file->filename); } } It's quite long (and it's even a bit shortened here in the post, because there's more elseif statement for very similar actions for different templates) and possibly redundant at this point but I'd rather get it all to work and then think about how to shorten it. As you can see I make use of another AdminAction within this AdminAction, namely FtpFilesToPage. I don't see an issue with that though, works fine. So either of these elseif subaction above face the same problem as described in the initial post of this thread. I'm actually a step further, I realised all the 65 (!) csv files that I used to test my AdminAction with are stored in the AdminAction's /site/assets/files/1443/ folder (1443 only in my case obviously). So when I delete those, everything works as it should, just don't know how to have the script delete it automatically. ->unlink() is not helpful. Maybe $pages->get('1443')->deleteAll(); or something? Best practice? Thanks for looking into that @adrian
  10. so long story short, I'm currently working on an AdminAction by which I can attach a .csv file, loop through the rows and do some more code on what I get from there. My problem is, I can never upload the same file again, i.e. cannot unlink the file it appears, which is quite annoying, particularly when developing (but also when actually using the AdminAction in the future), because it says it's already in the system and belongs to another field. I have to rename the file each time I did some changes and want to execute the action. Where is that file actually? In the session variable? in a cache folder somewhere? Anyways, my code starts somewhat like this $url = config()->paths->assets.'cache/AdminActions/'; if(count($options['csv_upload'])) { $file = $options['csv_upload']->first(); $fp = fopen($file->filename, 'r'); while(($row = fgetcsv($fp)) !== false) { ... and at the very end of the code, after all the different if-conditions and while-loops I do include… fclose($fp); unlink($file->filename); but that doesn't work. – hope you don't mind tagging you here @adrian
  11. but what about .csv files? why would they trigger that error? @horst
  12. OK great, that actually worked with a few adjustments. Firstly, I had to add FieldtypeRepeater to the fields selector for it to be a field option in the AdminAction dialogue. 'options' => $this->wire('fields')->find("type=FieldtypeFile|FieldtypeImage|FieldtypeRepeater")->getArray() Secondly, had to save the newItem before continuing… $newItem = $destinationPage->$fieldName->getNew(); $newItem->save(); // << this one $newItem->post_image->add($item->getPathname()); $newItem->save(); Now that the images are nicely distributed among different repeater items, I need to figure out a way to csv-import the captions. Not sure how to access a subfield of a repeater item and especially how to access them specifically, it seems like all the images of any of those pages are in one table cell and all the captions are in another. Thoughts?
  13. actually, this module is pretty cOoL, does what it promises, thank you. But, let's suppose I want to import over 3000 pages, a couple of images each. I tried to use @adrian's Admin Action FTP Files to Page (a process I also managed to automate before (i.e. batch-import for other images-fields)) in combination with @Robin S's repeater-images module to import images to my images field. It imports the images alright, but the repeater-items are not created automatically, I need to save the page manually in admin (an API call $page->save(); doesn't work either). I guess it's a problem with the hooks, one module is waiting for an action that the other module never triggers, so it cannot hook. And now let's not just suppose but just accept the fact that it is indeed that many pages. Normally, I wouldn't mind saving each page manually, cause it's a one-time-migration, but with that amount, it's undoable. Thanks for your ideas
  14. @teppo just some basic questions, no issues (yet) 😄 I have different pro-tables throughout the site and their content is distinct, thus should be indexed separately, is that at all possible? If not, we can stop right here. I set it up like this: Selected indexed fields: the table-field Indexed templates: the table-field's parent-template Select index field: search_index (already auto-added to the parent-template) sort and operator is unclear to me. I will need to look into adding custom operators using the aforementioned PW-doc on selectors. However, how do I make the sort direction and the operator optional depending on the user input? How can I select the subfields of the table (i.e. columns) to be indexed? Only when I check the "Index pages now?" option I can select those subfields. I indexed the table and/or the template, did a search and got a result. But since it is not pages that it's finding but say 6 matching rows on 1 page, it only shows 1 result instead of 6 and links to the parent page. Can I adjust the results to list the rows? I have the entire markup already (you may or may not have noticed, I've been trying to accomplish a search function without SearchEngine for some time now), any chance I can just use that markup somewhere? That's all the questions I have so far. Thanks for help, have a nice weekend.
  15. @Jonathan Dart can you use this module to search Profields Table as well?
  • Create New...