marcus Posted July 20, 2014 Posted July 20, 2014 (edited) Hi there, remember my Knowledge Base Site Profile? In there, I implemented a simple "internal bookmarking system", but hard-wired it into the templates then. In order to start rewriting this mentioned profile I made a module out of this page flagging thingy: https://github.com/marcus-herrmann/FlagPages/ With this module, a logged in user can flag/unflag pages, on whose template the flag link (see below) has been placed. These flags serve as "personal bookmarks" and are not a site-wide sticky setting. It's a proof of concept and not heavily tested, still! So please use with caution. Usage On the module's config page you can set the user roles allowed to flag pages. Per default this ability is restricted to "superuser". If you are adding further roles, please seperate the entries with whitespace: superuser editor foorule On the markup side, FlagPages module consists of two parts: Rendering the link and rendering a list with your current bookmarks. First of all, load the module via: $flag = $modules->get("FlagPages"); Rendering flag toggle linkThen, use the renderLink method and place the output in your template. By adding the link to all or just certain templates, you can control which templates can be flagged and which can't. echo $flag->renderLink(); The default output will be "Add {Name} to flags", or "Remove {Name} from flags". You can override these labels with parameters. For example: echo $flag->renderLink("Add %s to my bookmarks", "Remove %s from my bookmarks"); Rendering the flagged pages list echo $flags->renderList(); This will output a simple unordered list with links to pages the currently logged-in user has set a flag to. Installation Requirement: ProcessWire 2.4 (although I haven't tested it in older versions, frankly) Manually Download or git clone, rename the module folder to "FlagPages" and put it into your site/modules folder Go to modules overview page and click "Check for new modules" Install the module. The module will appear under "Flag" section. Download here Have fun! edit: Simplified renderLink param logic according to teppo's advice Edited October 10, 2014 by marcus 9
teppo Posted July 20, 2014 Posted July 20, 2014 Looks great Marcus, I'm looking forward to trying this out! Basically, the link label structure is {prefix} {page->title} {suffix}. Just thinking out loud, but you could, perhaps, simplify this a bit without sacrificing any flexibility: // the sprintf way echo $flags->renderLink("Add %s to flags", "Remove %s from flags"); // the placeholder way echo $flags->renderLink("Add {flag} to flags", "Remove {flag} from flags"); 1
Ivan Gretsky Posted July 20, 2014 Posted July 20, 2014 Was thinking about the possible implementations of this outside your KB site profile... Could it be used in online-stores for something like "wishlist" or "to see later"? 2
marcus Posted July 20, 2014 Author Posted July 20, 2014 (edited) Was thinking about the possible implementations of this outside your KB site profile... Could it be used in online-stores for something like "wishlist" or "to see later"? Absolutely. The module should offer generic flagging ability to every site or app with a user system and maybe a large number of data entities. edit: Since user are pages this might work with little tweaks on user profiles as well Edited July 20, 2014 by marcus 1
Beluga Posted July 21, 2014 Posted July 21, 2014 Well, this looks like another piece in the puzzle for anyone interested in building a forum in PW: flag spam posts and users
marcus Posted July 21, 2014 Author Posted July 21, 2014 Well, this looks like another piece in the puzzle for anyone interested in building a forum in PW: flag spam posts and users Yeah, although the flags set by the module are "personal" ones (as in: associated with a user account). In my site profile, I solved the "public" flag situation by adding a boolean-like field to pages (with template wiki-page in this case), calling them "Sticky pages". Maybe an idea for another module (but I'm quite certain that such a modue already exists). PS: I chose the sprintf way Setting labels is much easy now, see updated post above.
marcus Posted July 24, 2014 Author Posted July 24, 2014 Just released version 0.2.0 and added setting to limit flagging ability to certain user roles
adrian Posted July 24, 2014 Posted July 24, 2014 Hi marcus - sounds like a great addition. How about making it an ASM field that grabs all the available roles?
marcus Posted July 24, 2014 Author Posted July 24, 2014 That's what I am aiming for, but I'm not quite sure how to do it (sure - using $this->modules->get("InputfieldAsmSelect") - but then, no idea, really). Frankly speaking I'm not sure if even the comparison of allowed roles array vs. current user roles array is done elegantly. It works, though. Since this is my first module, I wanted to take only tiny steps forward But if anyone point me to the right direction I would be very thankful. 1
adrian Posted July 24, 2014 Posted July 24, 2014 This should get you started: $f = wire('modules')->get("InputfieldAsmSelect"); $f->required = true; $f->attr('name', 'allowedRoles'); $f->attr('value', $data["allowedRoles"]); $f->label = __('User roles allowed to flag (use spaces to separate entries)'); $f->setAsmSelectOption('sortable', false); // populate with all available roles foreach(wire('roles') as $roleoption) { $f->addOption($roleoption->name); } if(isset($data['allowedRoles'])) $f->value = $data['allowedRoles']; $wrapper->add($f); I see you are using owzim's config helper, which I haven't used, so let us know if you have any trouble incorporating it into your module. 2
horst Posted July 24, 2014 Posted July 24, 2014 @adrian: doesn't you populate two times the value here? $f->attr('value', $data["allowedRoles"]); and: if(isset($data['allowedRoles'])) $f->value = $data['allowedRoles']; Which one should be used with the ASM ? 1
adrian Posted July 24, 2014 Posted July 24, 2014 @adrian: doesn't you populate two times the value here? Oops - nice catch horst. That's what you get for copying and pasting from different things in a hurry I would recommend removing the first one. The approach in the second one insures you won't get an "Undefined index: allowedRoles" error before any roles have been chosen. 1
horst Posted July 24, 2014 Posted July 24, 2014 ... The approach in the second one insures you won't get an "Undefined index: allowedRoles" error before any roles have been chosen. Ah yes, this one I have had sometimes with different text: "Undefined index: xyz". I've got this with modules on their first run.
marcus Posted July 25, 2014 Author Posted July 25, 2014 This should get you started: $f = wire('modules')->get("InputfieldAsmSelect"); $f->required = true; $f->attr('name', 'allowedRoles'); $f->attr('value', $data["allowedRoles"]); $f->label = __('User roles allowed to flag (use spaces to separate entries)'); $f->setAsmSelectOption('sortable', false); // populate with all available roles foreach(wire('roles') as $roleoption) { $f->addOption($roleoption->name); } if(isset($data['allowedRoles'])) $f->value = $data['allowedRoles']; $wrapper->add($f); I see you are using owzim's config helper, which I haven't used, so let us know if you have any trouble incorporating it into your module. Thank you very for the starting point. Unfortunately, prepopulation of the roles seems not to work. At first, I removed owzim's config helper again since I will care about config defaults later. Then I did the following: public function __construct() { $this->allowedRoles = array(); } public static function getModuleConfigInputfields(array $data) { $wrapper = new InputfieldWrapper; $f = wire('modules')->get("InputfieldAsmSelect"); $f->required = true; $f->attr('name', 'allowedRoles'); $f->label = __('User roles allowed to flag'); $f->setAsmSelectOption('sortable', false); if(isset($data['allowedRoles'])) $f->value = $data['allowedRoles']; // populate with all available roles foreach(wire('roles') as $roleoption) { $f->addOption($roleoption->name); } $wrapper->add($f); } But in the module's settings I get the warning: Warning: Invalid argument supplied for foreach() in /sites/processwire/flagpages/wire/modules/Process/ProcessModule/ProcessModule.module on line 730 So wire('roles') is not available in the getModuleConfigInputfields method? Hard to believe, since wire('modules') is :-/
adrian Posted July 25, 2014 Posted July 25, 2014 I don't think that error is because of wire('roles'). If you take a look at line 730 of that file (https://github.com/ryancramerdesign/ProcessWire/blob/master/wire/modules/Process/ProcessModule/ProcessModule.module#L730) it's about foreaching through all the fields in your modules config setup. I have tested that code and it works fine, so I think you are likely missing something else in your config setup. There are different ways to set up the config, but this should work: protected static $configDefaults = array( // global "allowedRoles" => "" ); protected $data = array(); public static function getModuleConfigInputfields(array $data) { foreach(self::$configDefaults as $key => $value) { if(!isset($data[$key]) || $data[$key]=='') $data[$key] = $value; } $wrapper = new InputfieldWrapper; $f = wire('modules')->get("InputfieldAsmSelect"); $f->required = true; $f->attr('name', 'allowedRoles'); $f->label = __('User roles allowed to flag'); $f->setAsmSelectOption('sortable', false); if(isset($data['allowedRoles'])) $f->value = $data['allowedRoles']; // populate with all available roles foreach(wire('roles') as $roleoption) { $f->addOption($roleoption->name); } $wrapper->add($f); return $wrapper; } Or if you are still having problems, feel free to post your entire code so we can take a look. 2
marcus Posted July 25, 2014 Author Posted July 25, 2014 I don't think that error is because of wire('roles'). If you take a look at line 730 of that file (https://github.com/ryancramerdesign/ProcessWire/blob/master/wire/modules/Process/ProcessModule/ProcessModule.module#L730) it's about foreaching through all the fields in your modules config setup. I have tested that code and it works fine, so I think you are likely missing something else in your config setup. There are different ways to set up the config, but this should work: protected static $configDefaults = array( // global "allowedRoles" => "" ); protected $data = array(); public static function getModuleConfigInputfields(array $data) { foreach(self::$configDefaults as $key => $value) { if(!isset($data[$key]) || $data[$key]=='') $data[$key] = $value; } $wrapper = new InputfieldWrapper; $f = wire('modules')->get("InputfieldAsmSelect"); $f->required = true; $f->attr('name', 'allowedRoles'); $f->label = __('User roles allowed to flag'); $f->setAsmSelectOption('sortable', false); if(isset($data['allowedRoles'])) $f->value = $data['allowedRoles']; // populate with all available roles foreach(wire('roles') as $roleoption) { $f->addOption($roleoption->name); } $wrapper->add($f); return $wrapper; } Or if you are still having problems, feel free to post your entire code so we can take a look. Thanks a lot - this works like a charm! My next step is to compare the current user's roles to the ones in the allowedRoles array. Currently, the solution is to extremly simplify the user roles output in order to compare it to another simple array (the one coming from the original approach, exploding a string) via array_intersect. Although this works it feels wrong somehow. Will be checking the output of $this->allowedRoles now!
adrian Posted July 25, 2014 Posted July 25, 2014 I think this approach is reasonable: if(wire('user')->roles->has("name=".implode("|",$this->data['allowedRoles']))) { I think a little cleaner than array_intersect in this situation. 2
marcus Posted August 4, 2014 Author Posted August 4, 2014 Thank you for this, much more elegant approach! Unfortunately, my dev version of the module stopped working, since the install() method is not being fired anymore since, well, install. Neither field creation nor messaging via $this->message("Module is installed") works - while uninstall() goes smoothly everytime. Is this a common module beginners error caused by something else? The - inoperable - source code of this is here https://gist.github.com/marcus-herrmann/9664626db17524fa7338 - if anyone would be so nice to have a look Thanks! Nevermind Used in in new version. By the way: That this module and the Like Fieldtype share some functionality was never intended. I'm not aiming to substitute a commercial module at all. Even if, I would fail badly. It was only an existing functionality of the Knowledge Base Site Profile that needed a proper module wrapper.
marcus Posted August 24, 2014 Author Posted August 24, 2014 Added two little tweaks (because I noticed I needed them in the Knowledge Base Site Profile Update): Return the count of currently flagged pages with ->showFlagCount(); Supply some HTML classes to the rendered <ul> via parameter ->renderList("classname foo bar")
cstevensjr Posted October 12, 2014 Posted October 12, 2014 I just wanted to say that this is an outstanding and essential module! I appreciate all that you have done with this module. 1
marcus Posted October 12, 2014 Author Posted October 12, 2014 I just wanted to say that this is an outstanding and essential module! I appreciate all that you have done with this module. Wow, thanks! I'm curious - in which contexts/on what type of sites are you using this module?
cstevensjr Posted October 12, 2014 Posted October 12, 2014 (edited) SomeDay Tool I'm working on a series of restricted collaborative websites that are geared towards me working with my clients. FlagPages allows the client or myself to bookmark what's important to each person (i.e. a particular project, task, sub-task, subscription, configuration control item, note, contact, etc...). FlagPages allows you to have favorites or shortcuts within a highly structured system. I'm still working on documentation of the SomeDay Tool and intend on talking about it on this PW Forum, when I get some free time. Edit: I took some time to write a little bit up about the SomeDay Tool https://processwire.com/talk/topic/7919-someday-tool-project/#entry76508 Edited October 12, 2014 by cstevensjr 3
marcus Posted December 2, 2014 Author Posted December 2, 2014 Fixed module versioning after feedback here, tested under 2.5, added small improvements. 4
kreativmonkey Posted August 26, 2015 Posted August 26, 2015 Hi Marcus, first thank you for this Module. I use it with PW 2.6 and it works perfect. In my case i have creat an Bookmark Icon to display if the page is flagged or not.I display this icon on a list of Pages like the following image: For this case i do a little modification on the renderLink function at your module: public function renderLink(Page $page, $add = "<i class='fa fa-bookmark fa-lg' title='Setze ein Lesezeichen für %s'></i>", $remove = "<i class='fa fa-bookmark fa-lg bookmark-activ' title='Entferne das Lesezeichen für %s'></i>") { /* * Render link switch to flag/unflag pages */ if (!$this->allowedRoles) { echo("You need to grant flagging permissions first!"); }; $user = $this->user; $input = $this->input; if(!$page->id) $page = $this->page; $currentPage = $this->page; $pages = $this->pages; $session = $this->session; $userFlaggedPages = $this->user->flaggedpages; $user_role_stack = array(); now i can call this function with: echo $flags->renderLink($page); so i can use this on a list of pages: foreach($pages as $p){ echo $flags->renderLink($p); } i don't know how a batter solution than this.
Outward Posted March 11, 2017 Posted March 11, 2017 It's a long shot, but has anyone managed to get this work on 3.0? I've tested both this and the Page Bookmarks module (changing selectors to PageCollections thinking it was just a conflict since the PW core bookmark features were added) and haven't had any luck. I'm able to select roles with flagging permissions, however the Add/Remove flag render link doesn't display. Any advice would be appreciated!
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now