This page has been deprecated, as this application has been repackaged as a plugin for Wikka 1.1.6.6. Please visit the new BookmarkMgr page for updated information. WikkaCase is down indefinitely. This is the latest info on BookmarkManager. Also, please note the Freetag project has mysteriously disappeared from Google Code and has shown up here.
 


Bookmark Manager

An integrated bookmark manager/social bookmarking framework in the spirit of de.lirio.us. Note: Scroll down to the import section for scripts to import de.lirio.us data and other bookmarks into the BookmarkManager.

Screenshots

Main page
BookmarkManager main page
 


Add bookmark page
BookmarkManager add page
 


Design ideas

Progress reports

TODO
Indicates issues that have been addressed
Installation
include_once('libs/Wikka.freetag.class.php');
include_once('libs/Wikka.freetag.lib.php');
$freetag_options = array(
'db_user' => $this->config['mysql_user'],
'db_pass' => $this->config['mysql_password'],
'db_host' => $this->config['mysql_host'],
'db_name' => 'freetag',
'PCONNECT' => true,
);
$freetag = new wikka_freetag($freetag_options);
$wikka_id = $this->GetUserName();
$tagger_id = wikka_id_to_tagger_id($wikka_id, $this, $freetag_options);
$all_obj_count = count_objects(NULL, $this, $freetag_options);
$your_obj_count = count_objects($wikka_id, $this, $freetag_options);
$search_type = ; if(isset($_REQUEST['action'])) { if($_REQUEST['action']=="list_yours") { $search_type = "search_yours"; } else { $search_type = "search_all"; } } echo ".$this->href()."&action=list_all"."\">List all</a> (".$all_obj_count.") | ".
"<a href=\.$this->href()."&action=list_yours"."\">List yours (".$your_obj_count.") | ". ".$this->href()."&action=add"."\">Add</a>".
$this->FormOpen().
"<input type=\"hidden\" name=\"action\" value=\.$search_type."\" />". "Search: ". "". "Tags". "Title/Desc". $this->FormClose(); if(!isset($_REQUEST['action'])) { // Display all tags at start of session $this->Redirect($this->href()."&action=list_all"); } // Search by tags, titles, or descriptions // Action name: search // Search field name: search // Radio buttons: search_type: "tags" or "title_desc" if(isset($_REQUEST['action']) && ($_REQUEST['action']=="search_yours") || ($_REQUEST['action']=="search_all")) { if(isset($_REQUEST['search_type']) && ($_REQUEST['search_type']=="tags") && isset($_REQUEST['search'])) { if($_REQUEST['action'] == "search_yours") { $this->Redirect($this->href()."&search_term=".$_REQUEST['search']."&action=list_yours"); } else { $this->Redirect($this->href()."&search_term=".$_REQUEST['search']."&action=list_all"); } } } // Add new entry to DB if(isset($_REQUEST['action']) && ($_REQUEST['action']=="add")) { $values = array(); // Check for request from a bookmarklet...check to see if user // is logged in...if not, redirect to login page and come back // here when done if(isset($_REQUEST['wikka_bookmarklet'])) { $values['uri'] = mysql_real_escape_string($_REQUEST['uri']); if(isset($_REQUEST['title'])) { $values['title'] = mysql_real_escape_string($_REQUEST['title']); } $values['referrer'] = $_SERVER['HTTP_REFERER']; $values['when_done'] = "add_link"; save_to_current_session($values); //session_write_close(); if(!$this->GetUser()) { $_SESSION['go_back'] = $this->Href(null, $this->MiniHref()."&action=add"); //session_write_close(); $this->Redirect($this->config['base_url']."UserSettings"); } } // The next clause checks to see if we're returning from a // login request while trying to execute a bookmarklet if(isset($_SESSION['when_done']) && ($_SESSION['when_done']=="add_link")) { $values['uri'] = $_SESSION['uri']; $values['title'] = $_SESSION['title']; $_SESSION['when_done'] = "return"; //session_write_close(); } if(trim($_REQUEST['tags']) != ) {
} else {
Display add form
print($this->FormOpen());
?>
<input type="hidden" name="action" value="add" />
<table>
<tr>
<td align="right">Title:</td>
<td><input name="title" size="40" value="<?php echo $values['title']; ?>" /></td>
</tr>
<tr>
<td align="right">URI:</td> <td><input name="uri" size="40" value="<?php echo $values['uri']; ?>" /></td> </tr>
<tr>
<td align="right">Description:</td>
<td><input name="desc" size="40"/></td>
</tr>
<tr>
<td align="right"></td>
<td><?php echo $this->Format("Use blank space between tags, include \"@private\" for private bookmark"); ?></td>
</tr>
<tr>
<td align="right">Tags:</td>
<td><input name="tags" size="40"/></td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="Add" size="40" /></td>
</tr>
</table>
<?php
print($this->FormClose());
}
}
Edit entry
if(isset($_REQUEST['action']) && ($_REQUEST['action']
"edit") && (isset($_REQUEST['object_id']))) {
$object_id = mysql_real_escape_string($_REQUEST['object_id']);
$obj = $this->
LoadSingle("select bookmark_id,title,uri,description,wikka_id from ".$freetag_options['db_name'].".freetag_bookmarks where bookmark_id = ".$object_id.";");
Is the logged-in user the owner?
if($wikka_id != $obj['wikka_id']) {
$this->Redirect($this->href()."&action=list_all");
}
if(isset($_REQUEST['modify']) && trim($_REQUEST['tags']) != ) { // Delete all tags, then re-tag $freetag->delete_all_object_tags_for_user($tagger_id,$object_id); // Private bookmark? $private = 0; if(strpos(trim($_REQUEST['tags']), "@private") !== false) $private = 1; $this->Query("update ".$freetag_options['db_name'].".freetag_bookmarks set ". "title = '".mysql_real_escape_string($_REQUEST['title'])."', ". "uri = '".mysql_real_escape_string($this->cleanUrl($_REQUEST['uri']))."', ". "wikka_id = '".mysql_real_escape_string($wikka_id)."', ". "private = '".$private."', ". "description = '".mysql_real_escape_string($_REQUEST['desc'])."' ". "where bookmark_id = ".mysql_real_escape_string($object_id)." ". "limit 1;"); $freetag->tag_object($tagger_id, $obj['bookmark_id'], $_REQUEST['tags']); $this->Redirect($this->href()."&action=list_yours"); } // Display add form print($this->FormOpen()); ?> get_tags_on_object($object_id); $taglist =
Title:
URI:
Description:
Format("Use blank space between tags, include \"@private\" for private bookmark"); ?>
Tags:
;
foreach($tags as $idx=>$res) {
$taglist .= trim($res['raw_tag'])." ";
}
?>
<td><input name="tags" size="40" value="<?php print $taglist?>"/></td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="Edit" size="40" /></td>
</tr>
</table>
<?php
print($this->
FormClose());
}
Delete entry
if(isset($_REQUEST['action']) && $_REQUEST['action']
"delete") {
$object_id = mysql_real_escape_string($_REQUEST['object_id']);
$obj = $this->LoadSingle("select bookmark_id,wikka_id from ".$freetag_options['db_name'].".freetag_bookmarks where bookmark_id = ".mysql_real_escape_string($object_id).";");
Is the logged-in user the owner?
if($wikka_id != $obj['wikka_id']) {
$this->Redirect($this->href()."&action=list_all");
}
Delete all tags first...
$freetag->delete_all_object_tags_for_user($tagger_id,$object_id);
...then delete object
$this->Query("delete from ".$freetag_options['db_name'].".freetag_bookmarks where bookmark_id = ".mysql_real_escape_string($object_id)." limit 1;");
$this->Redirect($this->href()."&action=list_yours");
}
Output tag cloud
$url_opts = $this->href()."&action=list_all";
$search_term = NULL;
$header = "All tags";
if(isset($_REQUEST['search_term'])) {
$search_term = mysql_real_escape_string($_REQUEST['search_term']);
$header = "All tags by search";
}
output_tag_cloud($freetag,$url_opts,NULL,$header,$tag_offset,$tag_limit,$search_term);
Display pagination links
$obj_count = 0;
$tag_url = ; if($tag != ) {
$obj_count = count_tagged_objects(NULL, $tag, $this, $freetag_options, $freetag);
$tag_url = "&tag=".$tag;
} else {
$obj_count = count_objects(NULL, $this, $freetag_options);
}
$prev_offset = $offset - $limit;
$prev_url = ; if($prev_offset < 0) { $prev_offset = 0; } if($offset > 0) { $prev_url = "
.$this->href()."&action=list_all&offset=".$prev_offset."&limit=".$limit.$tag_url."\">
List user bookmarks (ordered by most recent)
if(isset($_REQUEST['action']) && ($_REQUEST['action']
"list_yours")) {
$offset = 0;
if(isset($_REQUEST['offset'])) {
$offset = mysql_real_escape_string($_REQUEST['offset']);
}
$limit = 25;
if(isset($_REQUEST['limit'])) {
$limit = mysql_real_escape_string($_REQUEST['limit']);
}
$tag_offset = 0;
if(isset($_REQUEST['tag_offset'])) {
$tag_offset = mysql_real_escape_string($_REQUEST['tag_offset']);
}
$tag_limit = 100;
if(isset($_REQUEST['tag_limit'])) {
$tag_limit = mysql_real_escape_string($_REQUEST['tag_limit']);
}
Output tag cloud
$url_opts = $this->href()."&action=list_yours";
$search_term = NULL;
$header = "Your tags";
if(isset($_REQUEST['search_term'])) {
$search_term = mysql_real_escape_string($_REQUEST['search_term']);
$header = "Your tags by search";
}
output_tag_cloud($freetag,$url_opts,$tagger_id,$header,$tag_offset,$tag_limit,$search_term);
$ids = ; $tag = ;
if(isset($_REQUEST['tag']) && $_REQUEST['tag'] != ) { $ids = $freetag->get_most_recent_objects($tagger_id, $_REQUEST['tag'], $offset, $limit); $tag = mysql_real_escape_string($_REQUEST['tag']); } else { $ids = $freetag->get_most_recent_objects($tagger_id, NULL, $offset, $limit); } foreach($ids as $key=>$val) { $tags = $freetag->get_tags_on_object($val['object_id']); $obj = $this->LoadSingle("select title,uri,description,wikka_id from ".$freetag_options['db_name'].".freetag_bookmarks where bookmark_id = ".$val['object_id'].";"); print "
\n"; print "
.$obj['uri']."\">".$obj['title']."</a></div>\n";
print "<div class=\"bookmark_box\"><div class=\"bookmark_desc\">".($obj['description'] ? $obj['description'] : " ")."</div>\n";
print " <a href=\.$this->href()."&action=edit&object_id=".$val['object_id']."\">edit"; print "|"; print ".$this->href()."&action=delete&object_id=".$val['object_id']."\">delete</a><br/>";
$taglist = ; $link = $url_opts."&tag_offset=".$tag_offset."&tag_limit=".$tag_limit; foreach($tags as $idx=>$res) { $normtag = trim($res['tag']); $rawtag = trim($res['raw_tag']); $taglist .= ".$link."&tag=".$normtag."\">".$rawtag."</a> ";
}
print $taglist."by ".$obj['wikka_id']." (created: ".$val['tagged_on'].")\n";
print "<div class=\"clear\"> </div><hr/>";
print "</div></div>\n";
}
Display pagination links
$obj_count = 0;
$tag_url = ; if($tag != ) {
$obj_count = count_tagged_objects($wikka_id, $tag, $this, $freetag_options, $freetag);
$tag_url = "&tag=".$tag;
} else {
$obj_count = count_objects($wikka_id, $this, $freetag_options);
}
$prev_offset = $offset - $limit;
$prev_url = ; if($prev_offset < 0) { $prev_offset = 0; } if($offset > 0) { $prev_url = ".$this->href()."&action=list_yours&offset=".$prev_offset."&limit=".$limit.$tag_url."\">
</a>";
}
$next_offset = $offset + $limit;
$next_url = ; if($next_offset < $obj_count - 1) { $next_url = ".$this->href()."&action=list_yours&offset=".$next_offset."&limit=".$limit.$tag_url."\"
></a>";
}
print "<div class=\"center\">";
print $prev_url." ".$next_url;
print "</div>";
}
?>
	- Save the following file as ##libs/Wikka.freetag.lib.php##:

**Wikka.freetag.lib.php**
(php)
<?php
function wikka_id_to_tagger_id ($wikka_id, $obj, $freetag_options) {
if(!isset($wikka_id)) {
return NULL;
}
$wikka_id = mysql_real_escape_string($wikka_id);
$res = $obj->LoadSingle("select tagger_id from ".$freetag_options['db_name'].".freetag_wikka_id_map where wikka_id = '".$wikka_id."';");
$tagger_id = $res['tagger_id'];
if(!$tagger_id) {
$obj->Query("insert ".$freetag_options['db_name'].".freetag_wikka_id_map set "."wikka_id = '".$wikka_id."';");
$res = $obj->LoadSingle("select last_insert_id();");
$tagger_id = $res['last_insert_id()'];
}
return $tagger_id;
}
function output_tag_cloud($freetag,$tag_page_url,$tagger_id=NULL,$header=NULL,$tag_offset=0,$tag_limit=100,$search_term=NULL) {
Output tag cloud
$tag_count = $freetag->count_unique_tags($tagger_id,$search_term);
print "<div class='bookmark_floatr'>";
if($header) {
print $header." (".$tag_count."):<br/>";
}
print $freetag->get_tag_cloud_html_with_limits($tag_offset,$tag_limit,10,20,'px','cloud_tag',$tag_page_url,$tagger_id,$search_term);
Display pagination links
$tag_url = ; $prev_offset = $tag_offset - $tag_limit; $prev_url = ;
if($prev_offset < 0) {
$prev_offset = 0;
}
$search_tag = ; if(isset($search_term)) { $search_tag = "&search_term=".$search_term; } if($tag_offset > 0) { $prev_url = ".$tag_page_url."&tag_offset=".$prev_offset."&tag_limit=".$tag_limit.$search_tag."\">
</a>";
}
$next_offset = $tag_offset + $tag_limit;
$next_url = ; if($next_offset < $tag_count - 1) { $next_url = ".$tag_page_url."&tag_offset=".$next_offset."&tag_limit=".$tag_limit.$search_tag.$tag_url."\"
></a>";
}
print "<div class=\"pagination\">";
print $prev_url." ".$next_url;
print "</div>";
print "</div>";
print "<div class=\"clear\"> </div>";
}
function count_objects($wikka_id, $obj, $freetag_options) {
if(isset($wikka_id)) {
$wikka_sql = "AND wikka_id = '".mysql_real_escape_string($wikka_id)."'";
} else {
$wikka_sql = "AND private = 0";
}
$res = $obj->LoadSingle("select COUNT(*) as count from ".$freetag_options['db_name'].".freetag_bookmarks where 1 ".$wikka_sql.";");
$count = $res['count'];
return $count;
}
function count_tagged_objects($wikka_id, $tag, $obj, $freetag_options, $freetag) {
$num_objs = count_objects($wikka_id, $obj, $freetag_options);
$tagger_id = wikka_id_to_tagger_id($wikka_id, $obj, $freetag_options);
$ids = $freetag->get_most_recent_objects($tagger_id, $tag, 0, $num_objs);
return count($ids);
}
function save_to_current_session($values) {
foreach($values as $tag=>$value) {
$_SESSION[$tag] = $value;
}
return $_SESSION['name'];
}
?>
	- Save the following file as ##libs/Wikka.freetag.class.php##:

**Wikka.freetag.class.php**
(php)
<?php
Check for freetag library
$freetag_lib = '3rdparty/plugins/freetag/freetag.class.php';
if(!is_file($freetag_lib)) {
print("<br/><br/><div class=\"error\">".$this->Format("Can't find $freetag_lib!")."<br/><br/>\n");
die();
}
include_once($freetag_lib);
class wikka_freetag extends freetag {
function wikka_freetag($freetag_options) {
parent::freetag($freetag_options);
$this->_normalized_valid_chars = '@_a-zA-Z0-9';
}

function get_tag_cloud_html_with_limits($offset = 0, $limit = 100, $min_font_size = 10, $max_font_size = 20, $font_units = 'px', $span_class = 'cloud_tag', $tag_page_url = '/tag/', $tagger_id = NULL, $search_term = NULL) {
$tag_list = $this->get_tag_cloud_tags_with_limits($offset, $limit, $tagger_id,$search_term);
Get the maximum qty of tagged objects in the set
$max_qty = max(array_values($tag_list));
Get the min qty of tagged objects in the set
$min_qty = min(array_values($tag_list));
For ever additional tagged object from min to max, we add
$step to the font size.
$spread = $max_qty - $min_qty;
if (0
$spread) { Divide by zero
$spread = 1;
}
$step = ($max_font_size - $min_font_size)/($spread);
Since the original tag_list is alphabetically ordered,
we can now create the tag cloud by just putting a span
on each element, multiplying the diff between min and qty
by $step.
$cloud_html = ;
$cloud_spans = array();
foreach ($tag_list as $tag => $qty) {
if(strpos($tag, "private") ! false) continue; $size = $min_font_size + ($qty - $min_qty) * $step;
$search_tag = ; if(isset($search_term)) { $search_tag = "&search_term=".$search_term; } $cloud_span[] = '' . htmlspecialchars(stripslashes($tag)) . ''; } // Remove
from the following line to get a "tag // cloud" rather than a "tag list" $cloud_html = join("
\n ", $cloud_span); return $cloud_html; } /* * get_tag_cloud_tags_with_limits * * Extension to get_tag_cloud_tags() that permits specifying * an offset and limit (useful for presenting more tags than * can be displayed at one time). Also, tags are returned * in true alphanumeric sequence across the entire record set, * rather than across the most popular tags. * * See get_tag_cloud_tags() comments for description of this * function. * @param int Specify starting record (default: 0). Will be set to * offset of first matching tag if $search_term is not NULL. * @param int The maximum number of tags to return (default: 100) * @param int Tag owner (default: NULL) * @param string Optional search term (default: NULL) * * @return array Returns an array where the keys are normalized * tags, and the * values are numeric quantity of objects tagged with that tag. */ function get_tag_cloud_tags_with_limits($offset = 0, $limit = 100, $tagger_id = NULL, $search_term = NULL) { $db = $this->db; $limit_sql = "LIMIT ".$offset.", ".$limit; if(isset($tagger_id) && ($tagger_id > 0)) { $tagger_sql = "AND tagger_id = $tagger_id"; } else { $tagger_sql = ;
}
$search_sql = ; if(isset($search_term)) { $search_sql = "AND tag LIKE \.$search_term."%\; //$limit_sql = "LIMIT ".$limit; //$offset = 0; } $prefix = $this->_table_prefix; $sql = "SELECT tag, COUNT(object_id) AS quantity FROM ${prefix}freetags INNER JOIN ${prefix}freetagged_objects ON (${prefix}freetags.id = tag_id) WHERE 1 $tagger_sql $search_sql GROUP BY tag ORDER BY tag ASC $limit_sql "; $rs = $db->Execute($sql) or die("Syntax Error: $sql"); while(!$rs->EOF) { $retarr[$rs->fields['tag']] = $rs->fields['quantity']; $rs->MoveNext(); if($search_term) { //$offset++; } } ksort($retarr); return $retarr; } /** * count_unique_tags * Returns the total number of unique tags in the system. * * @param int The unique ID of the person to restrict results to. * @param string Optional search string (default: NULL) * * @return int Returns the count */ function count_unique_tags($tagger_id = NULL, $search_term = NULL) { $db = $this->db; if(isset($tagger_id) && ($tagger_id > 0)) { $tagger_sql = "AND tagger_id = $tagger_id"; } else { $tagger_sql = ;
}
$search_sql = ; if(isset($search_term)) { $search_sql = "AND tag LIKE \.$search_term."%\; //$limit_sql = "LIMIT ".$limit; //$offset = 0; } $prefix = $this->_table_prefix; $sql = "SELECT DISTINCT tag, tag_id, COUNT(*) as count FROM ${prefix}freetags INNER JOIN ${prefix}freetagged_objects ON (id = tag_id) $tagger_sql $search_sql GROUP BY NULL "; $rs = $db->Execute($sql) or die("Syntax Error: $sql"); if(!$rs->EOF) { return $rs->fields['count']; } return false; } } /********************************************************************/ ?> %% - Adjust the DB connection parameters for your own installation (only necessary if you deviate from the DB instructions above) - Create the CSS stylesheet for the action, ##css/bookmarkmgr.css##: %%(php) .bookmark_floatr { float: right; text-align: left; width: 10%; margin-left: 15px; padding: 4px; background: #EEE; border: 1px solid #CCC; line-height: 95%; } .bookmark_box { width: 85%; font-size: 85%; padding: 0px 5px; color: #666; background: #EEE; border: 1px solid #CCC; } .bookmark_block { width: 85%; border-bottom: 3px solid #DDD; padding: 8px; } .bookmark_desc { font-size: 100%; font-weight: bold; margin: 0px; padding: 0px; } .bookmark_title { font-size: 150%; font-weight: bold; padding: 5px 0px; } .pagination { text-align: center; margin: 20px auto; } %% - In order to use the stylesheet, add the following lines to the files indicated (click [[http://wush.net/trac/wikka/ticket/246 | here]] for a justification as to why these changes are necessary to the core): **wikka.config.php:** %%(php) 'additional_stylesheets' => 'bookmarkmgr.css', %% **actions/header.php:** %%(php) // Locate this line... " /> // ...and add the following codeblock: GetConfigValue("additional_stylesheets") != NULL) { $sheets = preg_split("/[\s]+/", $this->GetConfigValue("additional_stylesheets")); foreach($sheets as $sheet) { echo "\n"; } } ?> %% - Add the Unknown action ""bookmarks"""" action code to a new page

Bookmarklet support

BookmarkManager now supports "bookmarklets," which is a small snippet of Javascript saved as a browser bookmark that allows you to bookmark a webpage "on the fly." The following patch must be applied to actions/usersettings.php if you want to restrict bookmarklets to registered users (recommended):

===================================================================
RCS file: RCS/usersettings.php,v
retrieving revision 1.1
diff -u -r1.1 usersettings.php
--- usersettings.php    2006/06/18 06:03:03     1.1
+++ usersettings.php    2006/06/24 06:40:13
@@ -1,5 +1,7 @@
 <?php
 /**
+ * $Id: usersettings.php,v 1.5 2006/06/24 06:40:07 brian Exp brian $
+ *
  * Display a form to register, login and change user settings.
  *
  * @package            Actions
@@ -334,6 +336,13 @@
		// is user trying to log in or register?
		if (isset($_POST['action']) && ($_POST['action'] == 'login'))
		{
+        // Login request was redirected from elsewhere...let's make
+        // sure to go back if requested by setting $referrer
+        $referrer = null;
+        if(isset($_SESSION['go_back'])) {
+            $referrer = $_SESSION['go_back'];
+        }
+
				// if user name already exists, check password
				if (isset($_POST['name']) && $existingUser = $this->LoadUser($_POST['name']))
				{
@@ -349,7 +358,11 @@
										break;
								default:
										$this->SetUser($existingUser);
-                                       $this->Redirect($this->href());
+                    if($referrer == null)
+                                       $this->Redirect($this->href());
+                    else {
+                        $this->Redirect($referrer);
+                    }
						}
				}
				else // otherwise, proceed to registration
@@ -417,7 +430,11 @@
 
										// log in
										$this->SetUser($this->LoadUser($name));
-                                       $this->Redirect($this->href('', '', 'registered=true'));
+                    if($referrer == null) 
+                                       $this->Redirect($this->href('', '', 'registered=true'));
+                    else {
+                        $this->Redirect($referrer);
+                    }
						}
				}
		}
@@ -542,4 +559,4 @@
 <?php
		print($this->FormClose());
 }


Save the following as a bookmark in your browser:

javascript:location.href='http://your.site.com/wiki/wikka.php?wakka=BookmarkPage&action=add&uri='+escape(location.href)+'&title='+encodeURIComponent(document.title)+'&wikka_bookmarklet=1'
(Don't forget to change "your.site.com" as appropriate for your installation!)


TODO: Implement as a popup:

post bookmarklet (with popup): javascript:void(open('http://de.lirio.us/rubric/post?uri='+escape(location.href)+'&title='+encodeURIComponent(document.title)+'&when_done=close','Rubric','toolbar=no,width=700,height=325,scrollbars'));


Other stuff


Category
CategoryDevelopmentDiscussion
CategoryDevelopmentActions
CategoryUserContributions
There are 39 comments on this page. [Show comments]
Valid XHTML :: Valid CSS: :: Powered by WikkaWiki