The drupal.org redesign is anticipating using Flag for bookmarking of pages on drupal.org. Similar to this module, we want to be able to bookmark any page, not just ones that are nodes. This module looks exactly like what we want, except that I'm worried about the creation of URLs in the database for every page on the entire site. I developed a similar module (called flag_urls) that is nearly identical to flag_page with the following differences:

  • It creates its own Flag link type and menu callbacks for flagging.
  • This menu callback makes entries in the database the first time a page is flagged.
  • It's not as developed (no Views support, no hook_block).

Unfortunately making our own link type significantly increases the size of the module, plus I'm not sure how well it will work with anonymous flagging in the 2.x version of Flag. Would you be interested in collaborating on these goals so that we could use flag_page for drupal.org?

I've attached my in-progress module, of course I'll convert this to patches to flag_page if you're interested in collaborating. Thanks!

Comments

alexpott’s picture

I'm more than interested in collaborating!!!! (Nearly fell off the proverbial chair reading your message)

Patches welcome and if you want to be a co-maintainer I'd have no issue with that either (how could I - the flag module's your work ;) )

Perhaps we'll chat on IRC and sort out what needs to be done to get flag_page to a state where drupal.org can use it?!?

quicksketch’s picture

Fantastic! I'll work on rolling this as a patch to your module. Just a warning: I'm fairly sure the changes are going to be fairly substantial, since flagging a non-existent item is quite an unusual challenge. We'll probably need to modify your flag_page.inc class a bit.

There's also a mostly superfluous change adding a "hash" column to the flag_page_data table, this is a sha1 hash of the URL (which I let go up to 1024 characters) which is for performance, since we can lookup a sha1 hash on a 40-character column much faster than a 1024 character varchar. This should help with the large number of URLs that we anticipate on drupal.org.

I'm super-excited to be working with another project maintainer on this, I'll post an update when I get a chance to merge my work into flag_page.

drumm’s picture

alexpott’s picture

Here's a patch that adds:

1. Adds and uses the hash column for performance
2. Lazy loads the flag_page_data table so only flagged pages and entered
3. It doesn't add any extra menu items - it uses a content id of -1 to indicate it's a new url

alexpott’s picture

Improved the way titles are handled by passing in the page title on the url

Still to do - break up the flag_page_data into two tables (flag_page_data_url and flag_page_data_user) - so that if users can't override titles we don't create unnecessary rows in the flag_page_data and the overridden data will be stored in flag_page_data_user.

This should be mean that on d.o flag_page_data_url is kept to a minimum size - i.e. maximum row count would be number of urls on the site rather than urls * users

alexpott’s picture

This patch breaks flag_page_data into two tables flag_page_data_url and flag_page_data_user and makes the necessary changes to the views integration and contains all the improvements from #4 and #5.

Still to see what happens when anonymous flagging is enabled...

quicksketch’s picture

Still to see what happens when anonymous flagging is enabled...

I like this approach but I'm certain it's not going to work with anonymous flagging. Anonymous flags work by storing the content ID in a cookie. All the links are always shown in the unflagged state, then JavaScript goes through all the Content IDs in the cookie and replaces them with the flagged state. Since we're using a content ID of -1 for all unflagged content, anonymous users (in theory) would flag one item and then all content would appear flagged also.

alexpott’s picture

I've not read much into anonymous flagging... but I enabled session_api... set the page_flag to allow anon's... and it appears to work... have a look... http://dev.vit-al.co.uk/flag_page

quicksketch’s picture

Do you have page caching enabled? Flag only uses that behavior if it needs to deliver the same page to all anonymous users (since you can't put the link in a flagged state into the page cache).

alexpott’s picture

Just enabled block level and normal caching mode... everything appears to work...

Tried it on aggressive and things fell apart.

alexpott’s picture

Oops... nope it's not working when caching is enabled... however... the error is not that it thinks everything page is flagged - it never seems to get the remove flag action - and once you've added one url it's the only url you can add...

Will see what I can do...

quicksketch’s picture

Yeah the "everything shows up as flagged" would probably only exist if you were using JavaScript toggles instead of the confirmation form, check your cookies to see how Flag keeps track of flagged content by ID. In any case the goal is to have the cookie content ID and the link content ID match, which is why I took the approach of my own link type with a sha1 of the URL as the ID, since it gave a unique ID that the JavaScript could use (though a numeric ID was still used in the database).

alexpott’s picture

Just enabled the javascript toggle... and it's working... when page caching is enabled... but not when block caching is enabled... any ideas as to why? I think that the flag / unflag block can't be cached so I did this in flag_page_block:

function flag_page_block($op = 'list', $delta = 0, $edit = array()) {
  if ($op == 'list') {
    $flags = flag_get_flags('page');
    foreach ($flags as $flag) {
      $blocks[$flag->name] = array(
        'info' => t('Flag page block for ') . $flag->title,
        'cache' => BLOCK_NO_CACHE);
    }
    return $blocks;
  }
  elseif ($op == 'view') {
    $block = array();
    $flag = flag_get_flag($delta);
    //Check if user hass access to flag
    if ($flag->user_access()) {
      $block['subject'] = $flag->title;
      $url = (isset($_GET['q'])) ? $_GET['q'] : variable_get('site_frontpage', 'node');
      $content_id = $flag->get_content_id($url);
      if ($content_id) {
        $block['content'] = flag_create_link($flag->name, $content_id);
      }
      else {
        $block['content'] = flag_create_lazy_page_link($flag->name);
      }
    }
    return $block;
  }
}
alexpott’s picture

Can you guarantee the uniqueness of the sha1 generated id?

alexpott’s picture

'cache' => BLOCK_NO_CACHE does work - just need to delete the row from the block table to get it to rebuild...

Okay now http://dev.vit-al.co.uk/flag_page has page caching (normal - not aggressive) and block caching turned on. Everything appears to work apart from the javascript that replaces the flag action with the unflag action...

alexpott’s picture

Patch with latest changes... anonymous users and block caching...

Known bugs - for anonymous users the flag link will disable and not be replaced by an unflag link.

quicksketch’s picture

It'd be pretty unusual to get a collision: http://en.wikipedia.org/wiki/Universally_unique_identifier

In other words, only after generating 1 billion UUIDs every second for the next 100 years, the probability of creating just one duplicate would be about 50%.

That's not specific to SHA1, but it's a similar probability. Given any site's individual URLs, I think it's safe to say you'll never get a conflict.

drumm’s picture

Status: Active » Needs review
drumm’s picture

Any progress on this? What can be done to help remove blockers?

alexpott’s picture

I tracked the issue with anonymous flags to bug a in the flag module when you use an underscore in a flag name - I raised an issue and submitted a patch http://drupal.org/node/904144

As far as I know I've implemented all of quicksketch's ideas to make the module scale for d.o - I will do a dev release of 6.x-2.x branch when I get a sec.

alexpott’s picture

I've released a new version of the module 6.x-2.0 which has all the improvements recommended by quicksketch.

rickvug’s picture

I've been doing some testing with flag_page and flag_note. In the 1.x series this combination works fine but in the 2.x series flag_note is note is not compatible with flag_page. I believe that this is due to this patch as flag is no longer referring to a pre-existing content id. This is reflected in the URL structure for flagging a node vs. flagging a page. The link for flag_page is not overridden to flagnote's menu item. Here are the examples:

flagnote/action/flag/bookmarks/5?destination=node%2F5&token=b5eca6c1b9af4c81e0c193c512a17d0f
flag/flag/bookmark_page/-1?destination=node%2F5&token=24db852edcd2f6b011cf999eef537ce3&title=Test

Any ideas? Time depending I may dig into this further myself. I've filed the bug at #999152: Flag note 2.x does not work with flag page 2.x.

rickvug’s picture

As mentioned in #22 this patch introduced a compatibility problem between flag_note and flag_page. Removing the following code fixes the problem:

/*
 * Override get_link_type() function so flag_page module can create a
 * content_id only when necessary.
 */
function get_link_type() {
  $link_types = flag_get_link_types();

  // Override the default flag_flag_link by changing the module of ther selected link type.
  if (isset($this->link_type) && isset($link_types[$this->link_type])) {
    $link_types[$this->link_type]['module'] = 'flag_page';
    return $link_types[$this->link_type];
  }
  else {
    $link_types['normal']['module'] = 'flag_page';
    return $link_types['normal'];    
  }
}

From what I can see everything still works fine with this change but I can't be sure about other issues. Anyone care to comment on a way forward with this regression?

I'm moving to "needs work since this issue is still open despite the patch being committed. Let me know if any bugs should be new issues from here on out.

alexpott’s picture

Status: Needs review » Closed (fixed)

I think the best way to get round this issue will be to make the lazy-loading optional (and advisable on large sites). And check for the existence of the flag_note module and warn users. I've created a new issue to track this work #1017484: Compatibility problem between flag_note and flag_page 6.2

Setting this issue to fixed as the lazy loading capability works as expected in flag page 6.x-2.0

Ravi.J’s picture

Category: feature » bug
Priority: Normal » Critical
Status: Closed (fixed) » Active

By overriding get_link_type() and assingning flag_page as module for all link_types flag_page is overriding href and destination attributes used by other flag modules.
As a result modules such as flag_note don't work as they are suppose to be working.

Here is the hook_flag_link implementation of flag_note

function flag_note_flag_link($flag, $action, $content_id) {
  $token = flag_get_token($content_id);
  return array(
    'href' => "flag/note/$action/$flag->name/$content_id",
    'query' => drupal_get_destination() .'&token='. $token,
  );
}

and below is the hook_flag_link implementation by flag_page

function flag_page_flag_link($flag, $action, $content_id) {

  $title = isset($_GET['title']) ? $_GET['title'] : drupal_get_title();
  $token = flag_get_token($content_id);

  if (drupal_is_front_page()) {
    $title = variable_get('site_name', '');
  }
  
  return array(
    'href' => 'flag/'. ($flag->link_type == 'confirm' ? 'confirm/' : '') ."$action/$flag->name/$content_id",
    'query' => drupal_get_destination() . '&token='. $token . '&title=' . rawurlencode($title),
  );  
}

If you look closely the difference between the two implementations is the callback URL "href". flag_note uses "flag/note/%/%/%" and flag_page uses "flag/%/%/%" . As a result once flag_page is activated control never reaches menu path "flag/note/".

I spent quite a it of time in trying to identify a way for passing additional attributes to hook_flag_links, however it doesn't seem to be possible without considerable changes to flag_preprocess_page().

function get_link_type() in flag_page.inc is resulting in other modules not working as they should, hence this function could be taken off till there is a better way.

Ravi.J’s picture

Status: Active » Closed (fixed)
rickvug’s picture

Category: bug » feature
Priority: Critical » Normal

Fixing status. This is done.