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']) != ) {
Private bookmark?
$private = 0;
if(strpos(trim($_REQUEST['tags']), "@private") !
false)
$private = 1;

Converts numeric tag "123" to "123_" to facilitate
alphanumeric sorting (otherwise, PHP converts string to
true integer).
$tags = preg_split('/\s+/', $_REQUEST['tags'], -1, PREG_SPLIT_NO_EMPTY);
$tags = preg_replace('/^([0-9]+)$/', "$1_", $tags);
$tags = implode(" ", $tags);

$this->Query("insert ".$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'])."';");
$freetag->tag_object($tagger_id, $last_id['last_insert_id()'], $tags);
Check to see if it's time to return to the page from which
the bookmarklet was called
if(isset($_SESSION['referrer'])) {
$referrer = $_SESSION['referrer'];
unset($_SESSION['go_back']);
unset($_SESSION['uri']);
unset($_SESSION['title']);
unset($_SESSION['referrer']);
unset($_SESSION['when_done']);
$this->Redirect($referrer);
}
}
Otherwise, go back to tag/link list view
$this->Redirect($this->href()."&action=list_yours");
} 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:
<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']);
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
}

Delete entry
$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");
}

List all bookmarks (ordered by most recent)
if(isset($_REQUEST['action']) && ($_REQUEST['action']
$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_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);

$ids = ; $tag = ;
print "<div class=\"bookmark_box\"><div class=\"bookmark_desc\">".($obj['description'] ? $obj['description'] : " ")."</div>\n";
}
print $taglist."by ".$obj['wikka_id']." (created: ".$val['tagged_on'].")\n";
print "</div><div class=\"clear\"> </div><hr/>";
print "</div></div>\n";
}

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;
}
$next_offset = $offset + $limit;
}
print "<div class=\"pagination\">";
print $prev_url." ".$next_url;
print "</div>";
}

List user bookmarks (ordered by most recent)
$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;
$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) {
$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
Comments
Comment by DarTar
2006-03-29 10:21:57
Interesting proposal, Brian - we could use something like this to keep track of Wikka powered sites too. Some ideas:

- you might want to add CategoryDevelopmentDiscussion to this page;
- I'd like to see something 100% modular, no modifications to the core please;
- beside tags, it would be nice to allow optional fields for notes and other custom values;
- it would be nice to integrate this with a function to perform a HTTP request so as to periodically check the validity of stored URLs. URL responding with an error code would be marked as broken or problematic - this requires two extra fields in the table: "HTTP_status" and "last_checked".
Comment by NilsLindenberg
2006-03-29 11:46:25
Have you seen scuttle (http://sourceforge.net/projects/scuttle/) ? It clones some of the features of del.icio.us and is written in php.
Comment by NilsLindenberg
2006-03-29 11:55:08
and perhaps http://wikkawiki.org/AddLinkAction could be worth a look
Comment by BrianKoontz
2006-03-29 12:12:47
DarTar--

Yes, I agree with it being 100% modular. One of the reasons I enjoy Wikka is that it's so damn small!

Nils--

Thanks for the heads-up on scuttle...I will check it out. I've been looking at AddLinkAction as well.
Comment by NilsLindenberg
2006-04-26 20:26:26
Sorry for disturbing you again, Brian ;) but I have found another thing: http://getluky.net/freetag/ which should be easier to include then scuttle, because it's a standalone class. Problem: more work since the functions for link-handling aren't there. Benefit: It could be used for tagging pages too.
Comment by BrianKoontz
2006-04-27 10:27:33
Your comment is timely...I've taken a break from pulling out all the functionality in scuttle (user authentication, output formatting, etc.) that isn't necessary in a Wikka module. I'll take a look at freetag...
Comment by NitramF
2006-05-12 12:14:13
Is it possible to see your work on scuttle ?
Comment by BrianKoontz
2006-05-12 17:41:35
NitramF, It's on an internal server (my iBook!) right now...I'll see what can be done about making it accessible to the outside world.
Comment by DarTar
2006-05-21 06:43:16
Interesting, Brian. As a general consideration it would be nice to provide this functionality as a handler. So far Wikka mainly uses page handlers, but in the future I'd like to see user handlers implemented, i.e. handlers that do things depending on the userpage they are appended to. If BrianKoontz is a userpage, then BrianKoontz/feedback might display a form to contact this user, BrianKoontz/watchlist display a list of pages he's keeping an eye on. BrianKoontz/bookmarks would display a list of his personal bookmarks. Obviously the behaviour of a user handler should depend on who's logged in, whether an anonymous user, a registered user, the user himself, or an admin, and have some dedicated ACLs.
Comment by BrianKoontz
2006-05-21 09:31:44
So in this case, where would a user point their browser if they wanted to simply see a list of current bookmarks (using this plugin as the example)? Can a handler be listed as a "page" in PageIndex? I can see doing both: Offering a handler as an action as well, so a plugin can have a page it can call its own. How would a handler work in this situation (as a "generic" home for a plugin, not tied to a specific user)?
Comment by DarTar
2006-05-21 09:39:32
It often makes sense to provide the same functionality both as a handler and as an action, a handler is by definition page-independent, since it takes a pagename as an argument (although appending /bookmarks to a generic page could just display a generic bookmark interface, or maybe merge all users' tags and bookmarks).
Comment by BrianKoontz
2006-05-21 14:25:05
I can see how the /bookmarks action would be convenient, but would you also expect additional functionality, such as /bookmarks_add, /bookmarks_list, etc.? Or would an action taking the user to the Bookmarks page be sufficient?
Comment by NilsLindenberg
2006-05-21 14:51:03
Some Comments:

- it would be nice to have a link for the add-form near list yours.
- how about something like my tags / all tags?
- what would be usefull: If I tag a link with the name of an existing wiki-page, this link could show up at the end of the page.
Comment by BrianKoontz
2006-05-21 16:33:25
>>>what would be usefull: If I tag a link with the name of an existing wiki-page, this link could show up at the end of the page.<<<

Nils, you lost me on this one...the other items are on my TODO list!
Comment by TormodHaugen
2006-05-21 17:41:16
I read that as a reverse link, kind of. Given a wiki page GreatContent; if you were logged in and had a bookmark to "wikkawiki.org" tagged with "GreatContent", you would see the bookmark to "wikkawiki.org" at the bottom of "GreatContent". Or... I might have misinterpreted it.

Doing that which I spelt out there seems slightly "expensive" at first look; for the page I'm in, look through users tags and link all (if any) bookmarks which is tagged with this page's WikiName. Would be a bit "cheaper" if tags are saved with pointers to their bookmarks.
Comment by TormodHaugen
2006-05-22 05:47:45
NilsLindenberg: I agree that the last one could be quite useful, if I undersood what you meant correctly.

(This should've been at the start of my last comment, but I... was tired, yes. :/ )
Comment by DarTar
2006-05-22 06:21:08
> would you also expect additional functionality, such as /bookmarks_add, /bookmarks_list
you just need a single /bookmarks handler and pass all the other options as URL parameters, e.g. /bookmarks?action=add, /bookmarks?action=delete etc.
Once the action is ready I can help you convert it to a handler and add the user-related functionality if needed.
Comment by NilsLindenberg
2006-05-22 16:42:44
TormodHaugen: You understood me perfectly. Depending on the table structure, it must not be very expensive.
Comment by BrianKoontz
2006-05-22 21:51:34
I'm sorry...I must be really dense (or really tired :) ). So basically we're talking about tagging a URL with a page name, and having that link show up at somewhere in the page? I guess I'm not looking at this the right way...give me an example of how this might be useful.
Comment by DarTar
2006-05-25 03:21:25
Agreed with Brian - what's the use of creating a tagging system that generates backlinks?
Comment by 134.76.184.134
2006-05-26 05:31:07
I do not want to create backlinks, I just want to automatically show on a page all links tagged similar like the page name. An Example would be the 'BadBehavior' page in my personal wiki. I store the different versions of the programm on it ({{files}}), have embedded the news from the developer ({{rss}}) . Only the links I have to store manually instead of simply tagging them with BadBehavior.
Comment by BrianKoontz
2006-05-26 12:33:19
OK, so basically the ability to tag (possibly multiple times) a page? Actually, the underlying codebase simply treats everything as objects, so I think it would simply be a matter of mapping page names to objects, just another table most likely. Shouldn't be difficult.
Comment by NilsLindenberg
2006-05-26 16:20:19
Ok, great. I have not much time at the moment, but after a short look: 1. please put the wikka_freetag class in a file like Wikka.freetag.class.php in the new \libs directory. 2. $freetag_options -> why not use the params provided by wikka ($this->mysql_host, etc.)?
Comment by DarTar
2006-06-22 04:33:35
Brian, it would be nice if tags in each boorkmarks were links pointing to the list of bookmarks with the same tag.
Comment by DarTar
2006-06-24 06:02:14
Some further ideas.

1. If the userpage exists, create a link to it.

2. Instead of using the commentsheader class, give the div a dedicated class (e.g. "bookmark") with something like the following:

div.bookmark {
font-size: 85%;
padding: 0 5px;
color: #666;
background: #EEE;
border: 1px solid #CCC;
}

3. In the tag cloud, using a % for fonts instead of a fixed size might give you more flexibility and make sure the result is good on screens with different resolutions.

4. Finally, you should really get rid of markup like: <p><p>, <b></b>.
Comment by NilsLindenberg
2006-06-24 08:14:31
Still further ideas : Wikka.freetag.class.php should really go into /libs since it is no action. I'll change it in the instructions above.

It good that the functions have a doc-header, but please change it accordingly to WikkaCodignGuidelinesHowTo so in fits in with the rest of wikka. The helper functions should go into an extra file (like libs/Wikka.freetag.lib.php). This has two reasons: a) it makes bookmarks.php more readable and you'll get no php notice for declaring functions another time and b) it makes them re-usable for other actions/handlers etc.
Comment by DarTar
2006-07-04 04:58:23
Another idea that would be extremely useful (especially when maintaining long lists of links): add a broken link checker. I realize this is really a different project, maybe something we would like to see at work at wiki-level and not just at plugin-level, but it would be so useful to be able to run a spider once in a while to check whether some external links are dead and mark them as such.

A lot of ideas can be found here:
http://www.hotscripts.com/PHP/Scripts_and_Programs/Link_Checking/
Comment by NilsLindenberg
2006-07-06 09:11:47
Brian, I have set up a test installation at my localhost and I am recieving errors from the lib:
- Warning: ksort() expects parameter 1 to be array, null given in ...\libs\Wikka.freetag.class.php on line 156
- array_values() [function.array-values]: The argument should be an array in ...\libs\Wikka.freetag.class.php on line 77
- Wrong parameter count for max() in ...\libs\Wikka.freetag.class.php on line 77
...

seems like get_tag_cloud_html_with_limits() should check if there are tags to display and put out a message otherwise.
Comment by BrianKoontz
2006-07-06 13:30:09
Nils, I agree, there should be some checks in place for null arrays to eliminate the warnings. I don't think it's necessary to display a message, though, because it's not an error condition to have no tags to display (such as with a new install).
Comment by JavaWoman
2006-07-08 13:53:14
>> An integrated bookmark manager in the spirit of de.lirio.us

Brian, I confess I'm confused. The de.lirio.us home page doesn't tell me about its "spirit" but it seems to be about placing your bookmarks online, at their site.

All my browsers already have bookmanagers. Why would I need another? So what does this thing actually *do* (that my browser's bookmark manager cannot do), and what is it integrated with? Where do the bookmarks go? And why?

Baby steps, please!
Comment by BrianKoontz
2006-07-08 15:40:05
>>The de.lirio.us home page doesn't tell me about its "spirit" but it seems to be about placing your bookmarks online, at their site.<<

de.lirio.us is RIP for all intents. It was meant as an open source alternative to de.licio.us, but the maintainer for whatever reason decided he no longer wanted to run the site. Those with accounts were invited to move their accounts to Simpy. Simpy is closed-source, which doesn't sit well with me. So I wrote something that imitates some of the functionality of de.lirio.us, and of course made it open source. That's the "spirit" part.

I wrote a couple of scripts to transfer my 200 or so de.lirio.us bookmarks to my Wikka site. I use it to make my bookmarks available at school/work (which happen to be the same for me) and at home. One repository of bookmarks, accessible anywhere (as long as I have Internet access to the wiki that's hosting them, but I'm also working on a way to mirror the bookmarks for local access as well). I run it on a Wikka wiki I host myself. My students are encouraged to participate by adding bookmarks they feel will be of interest or of value in the future after they've finished with the class.

If you've figured out a system to keep all your local bookmarks in each of your browser environments synced up, great -- BookmarkManager probably isn't necessary. I use any number of computers throughout the day, some that are mine, some that aren't, so it's important to me that I'm able to access certain bookmarks. It's integrated with Wikka, because I find it much easier to write applications for an existing framework than trying to recreate things from scratch. The bookmarks are stored in the same database that the hosting wiki uses. Why? Because it was already there for me to use :)

--Brian
Comment by DarTar
2006-07-09 07:08:42
Here's my 2 cents to support the development of this plugin:
- wikis (especially personal ones) are very often used as link dumps; providing a plugin for more structured/flexible bookmarking within a wiki looks to me like a quite natural step;
- the point of having bookmarks stored remotely is exactly the same of having one's work stored on a server instead of on one's hard disk. Server-side bookmarks can be accessed from different machines, retrieved using different browsers/platforms, (optionally) shared among multiple users. Storing bookmarks server-side is also (at least in the general case) safer than storing them in the local settings of a specific client on a specific OS of a specific machine (each of which can break at some point, leading to unavailability or loss of bookmarks).
- there are a lot of browser extensions allowing integration of remote bookmarking services with clients (see for instance FF's del.icio.us-oriented extensions). Similar extensions could be easily adapted to wiki-based bookmarking services.
Comment by JavaWoman
2006-07-18 04:27:10
Thanks Brian and DarTar - I now understand better what this is about. Certainly, if you are "operating" from different locations (and don't have access to your own computer from there) this would be useful.

That set of circumstances has never happened to me though. I actually keep my bookmarks all over the place (apart from one major tree and some temporary bookmarks stored in Mozilla), many mixed in with other files. In other words, I classify *files*, and bookmarks (also files) are classified right along with them, in the hierarchical system I use. Sometimes sets of new bookmarks for some subject I'm researching go into a separate folder, to be merged into the main tree at a later point. So, apart from temporary bookmarks in Mozilla, I don't keep bookmarks "in a browser environment" at all. There's nothing to integrate, they work with all browsers.
I understand some people can't work with or don't want a hierarchical system - but for me that's simply the best way to work. Just to give you some idea of my preoblems understanding what this was about...

That's not to say a keywords-based system is inferior - just different. Different minds, different patterns. :)

Anyway, thanks for the explanations! This BookmarksManager looks like something that could be really useful for many (if not me, or other "hierarchical" thinkers). I'll leave it to others to actually test it, you wouldn't get much useful feedback from me! (Though I could look at the code, of course. ;) )
Comment by NilsLindenberg
2006-08-02 18:51:45
Brian, maybe some ideas can be found here: http://www.integratedelf.com/wiki/doku.php?id=wikkawiki:about

IntElf as a userpage here, too.
Comment by DarTar
2007-10-10 20:15:08
Brian I was thinking of a feature that could make this plugin very attractive to existing del.icio.us/del.irio.us users: a sync utility. I know this is beyond the scope of this plugin, but think of the advantages: people will be able to claim back - or at least dump - their bookmarks while keeping their del.icio.us account for its social features.
This should be feasible via the del.icio.us API: http://del.icio.us/help/api
Comment by BrianKoontz
2007-10-11 14:50:57
Actually, one of my tasks upon my return to the fold is to take care of a few items with the Bookmark Mgr (more robust searches, some other minor details). I've been running it for over a year now, up to about 1000 bookmarks, and it's been very stable. I don't see why data can't be dumped directly from the API into the DB table to be rendered by the manager.
Comment by KairoMiami
2007-11-21 10:15:39
Hi Brian, hi everyone else.

This bookmark-script is exactly what I was looking for :-)

Get my question in all my seriousness, I get an error message after installing:

<em class="error">Unknown action "bookmarks"</em>

This is the output after creating a page and having the script opened with:

{{bookmarks}}


Please help, I'd be very pleased. Any hints "furtherwise" very appreciated.

Tobias Kalle Aellig
Genfusion.info
Comment by KairoMiami
2007-11-21 10:31:21
Follow-Up, very important:

That's the new error message I get:

Warning: mysql_pconnect() [function.mysql-pconnect]: Access denied for user 'www-data'@'localhost' (using password: NO) in /var/www/web283/html/ww/3rdparty/plugins/freetag/adodb/drivers/adodb-mysql.inc.php on line 356
List all (0) | List yours (0) | Add
Search: TagsTitle/Desc
Syntax Error: SELECT DISTINCT tag, tag_id, COUNT(*) as count FROM freetags INNER JOIN freetagged_objects ON (id = tag_id) GROUP BY NULL

I double-checked the login entries for mysql in the bookmarks.php file

The other issue I could solve by myself ;-)))

Thank you, Tobias Kalle Aellig
Genfusion.info (Check out the logo ... :-)
Comment by BrianKoontz
2007-11-22 02:30:15
Hi, Tobias! You'll need to GRANT appropriate privileges to the freetag tables. I would suggest, at least initially, setting the DB access vars in bookmarks.php the same as those in your wikka.config.php file. At some point, I plan on integrating this plugin so that there is no need for a separate DB authentication route.

Something like this will work on a development system, giving access to all DBs:

grant all privileges on *.* to 'youruser'@'localhost' identified by 'yourpassword' with grant option;
Valid XHTML :: Valid CSS: :: Powered by WikkaWiki