Jump to content

Release: Flagpages


marcus
 Share

Recommended Posts

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 link

Then, 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
 
  1. Download or git clone, rename the module folder to "FlagPages" and put it into your site/modules folder
  2. Go to modules overview page and click "Check for new modules"
  3. Install the module. The module will appear under "Flag" section.
 
 
Have fun!
 
edit: Simplified renderLink param logic according to teppo's advice

post-912-0-84361200-1405837567_thumb.jpg

Edited by marcus
  • Like 9
Link to comment
Share on other sites

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");
  • Like 1
Link to comment
Share on other sites

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 by marcus
  • Like 1
Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

  • Like 1
Link to comment
Share on other sites

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. 

  • Like 2
Link to comment
Share on other sites

@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 ?

  • Like 1
Link to comment
Share on other sites

@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.

  • Like 1
Link to comment
Share on other sites

 ... 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. :)

Link to comment
Share on other sites

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 :-/

Link to comment
Share on other sites

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.

  • Like 2
Link to comment
Share on other sites

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! :)

Link to comment
Share on other sites

  • 2 weeks later...

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.

Link to comment
Share on other sites

  • 3 weeks later...

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")
Link to comment
Share on other sites

  • 1 month later...

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? 

Link to comment
Share on other sites

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  :rolleyes:

https://processwire.com/talk/topic/7919-someday-tool-project/#entry76508

Edited by cstevensjr
  • Like 3
Link to comment
Share on other sites

  • 1 month later...
  • 8 months later...

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:

flagged_pages.png

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.

Link to comment
Share on other sites

  • 1 year later...

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! 

Link to comment
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...