We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 1932
    • 137 Posts
    During one of my recent projects, I required some form of general purpose links listing/display in MODx. At first I considered using MODx’s built-in Weblinks system along with Wayfinder or another display snippet. I ended up deciding against this in order to avoid cluttering the document tree (which becomes an efficiency problem in the current core) as well as provide a more meaningful interface for managing links.

    Favorite Links

    Favorite Links solves these issues and offers a broad range of customizability. Favorite Links consists of a module which allows for the management of a list of links and the corresponding snippet which handles link display. I’ve done a decent amount of testing and believe everything is fairly stable but both the snippet/module should still be considered development releases.

    Favorite Links has several components, organized below for better access: Latest Version: [Updated 4/12/07]

    Favorite Links Snippet

    Snippet Name: FavoriteLinks

    Description: <strong>0.0.7</strong> Favorite Links Snippet

    Required files: FavoriteLinks-0.0.7.zip, Extract directory from zip file above and place in assets/snippets/.

    Snippet Code:
    <?php
    /*---------------------------------------------------------------------------
    * Favorite Links Snippet - Displays customized listing for "favorite" links
    *                        - Used in conjunction w/ the Favorite Links module
    *----------------------------------------------------------------------------
    *
    * This program is free software; you can redistribute it and/or modify it
    * under the terms of the GNU General Public License as published by the
    * Free Software Foundation.
    *
    * This program is distributed in the hope that it will be useful, but
    * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    * for more details.
    *
    * @author Brian Stanback (www.stanback.net)
    * @copyright Brian Stanback 2007
    * @created: 2007/02/27
    * @updated: 2007/04/18
    * @version 0.0.7
    *
    
    $params = array();
    
    /**
    * General parameters
    *--------------------------------------------------------------------------*/
    
    $params['basePath'] = isset($basePath) ? $basePath : $modx->config['base_path'] . 'assets/snippets/FavoriteLinks/';
    	// Path to Favorite Links snippet directory (default: modx_base/assets/snippets/FavoriteLinks/)
    
    $params['id'] = isset($id) ? $id : '';
    	// Unique identifier
    	// FavoriteLinks will set any global placeholders like this: [+id_PhName+]
    	// (Required if using pagination with several instances of FavoriteLinks on a single page)
    
    $params['dispType'] = isset($dispType) ? $dispType : 'list';
    	// Type of results to display
    	// Possible types: list, tags, dates, results
    
    $params['landing'] = isset($landing) ? intval($landing) : $modx->documentIdentifier;
    	// Page ID containing the [!FavoriteLinks?  &results=`1`!] snippet call
    
    $params['results'] = isset($results) ? intval($results) : 0;
    	// Set to 1 to allow the snippet call to be used for displaying
    	// results by specific tag/date
    
    $params['startId'] = isset($startId) ? intval($startId) : 0;
    	// Start displaying data after the specified link ID
    
    $params['tagSeparator'] = isset($tagSeparator) ? $tagSeparator : ', ';
    	// Separate tag display using the specified string
    	// Default: comma and a space
    
    $params['tags'] = isset($tags) ? $tags : '';
    	// Display results matching the specified tag(s)
    	// Multiple tags are separated by a comma and a space
    
    $params['excludeTags'] = isset($excludeTags) ? $excludeTags : '';
    	// Display results which do not match the specified tag(s)
    	// Multiple tags are separated by a comma and a space
    
    $params['boolean'] = isset($boolean) ? $boolean : 'OR';
    	// Boolean operator to use when filtering by multiple &tags (default: OR)
    	// Options:
    	// 	OR - display records matching any defined tag
    	// 	AND - only display records matching all defined tags
    
    $params['filter'] = isset($filter) ? $filter : '';
    	// Display only the links/dates/tags with a title or summary matching the filter text
    
    $params['startDate'] = isset($startDate) ? $startDate : null;
     	// Return links added on or after the specified date
    	// Example: 2007-01-03
    	// Example: -1 Month
    
    $params['endDate'] = isset($endDate) ? $endDate : null;
    	// Return results added on or before the specified date
    
    $params['tplOuter'] = isset($tplOuter) ? $tplOuter : '@FILE:tpl/outer.tpl';
    	// Outer container template for links display/archive
    	// Default: @FILE:tpl/outer.tpl
    	// Possible placeholders:
    	// 	[+favlinks_items+]
    	//
    	// Templates can be:
    	// 	Any valid chunk name
    	// 	Code via @CODE:
    	// 	File via @FILE:
    
    $params['tplNoResults'] = isset($tplNoResults) ? $tplNoResults : '@CODE:<p>No links found.</p>';
    	// Text or chunk to display when there are no results
    	// Default: @CODE:<p>No links found.</p>
    
    /**
    * Link display parameters
    *--------------------------------------------------------------------------*/
    
    $params['paginate'] = isset($paginate) ? intval($paginate) : 0;
    	// Split up the display into pages after reaching the specified number of links
    	// When paginate is enabled, &maxLinks becomes the number of links per page
    	// If set to 0, pagination is disabled
    	// Default: 0 (disabled)
    
    $params['paginateAlwaysShowLinks'] = isset($paginateAlwaysShowLinks) ? intval($paginateAlwaysShowLinks) : 1;
    	// Whether or not to always show previous/next links
    	// Set to 1 to show or 0 to hide when there are 1 page or fewer links
    	// Default: 1 (enabled)
    
    $params['paginateSplitter'] = isset($paginateSplitter) ? $paginateSplitter : ' ';
    	// Splitter character/text to include between page numbers
    
    $params['showLinks'] = isset($showLinks) ? intval($showLinks) : 1;
    	// To show individual links, set to 1
    	// For only showing the tags/dates, set to 0
    	// Default: 1
    	// Only applicable to taglist and datelist display types
    
    $params['sortLinks'] = isset($sortLinks) ? $sortLinks : 'title ASC';
    	// Sort link items
    	// Possible fields: id, url, title, summ, tags, xfn, rating, added, RAND()
    	// Add ASC or DESC to change the sort order, example: &sortLinks=`added DESC`
    	// Default: title ASC
    
    $params['maxLinks'] = isset($maxLinks) ? intval($maxLinks) : 100000;
    	// Maximum number of links to display in the result
    
    $params['offsetLinks'] = isset($offsetLinks) ? intval($offsetLinks) : 0;
    	// Offset for the links result
    
    $params['tplLink'] = isset($tplLink) ? $tplLink : '@FILE:tpl/link.tpl';
    	// Template for each individual link in the listing
    	// Default: @FILE:tpl/link.tpl
    	// Possible placeholders:
    	// 	[+id+], [+url+], [+title+], [+summ+], [+tags+], [+xfn+], [+rating+], [+added+]
    
    $params['tplAltLink'] = isset($tplAltLink) ? $tplAltLink : '';
    	// Template for alternating links
    
    /**
    * Tag-specific parameters
    *--------------------------------------------------------------------------*/
    
    $params['sortTags'] = isset($sortTags) ? $sortTags : 'count DESC';
    	// Sort tag items
    	// Possible fields: tag, count
    	// Add ASC or DESC to change the sort order, example: &sortTags=`tag ASC`
    	// Default: count DESC
    
    $params['maxTags'] = isset($maxTags) ? intval($maxTags) : 100000;
     	// Maximum number of tags to display in the result
    
    $params['offsetTags'] = isset($offsetTags) ? intval($offsetTags) : 0;
    	// Offset for the tags result
    
    $params['tplTag'] = isset($tplTag) ? $tplTag : '@FILE:tpl/tag.tpl';
    	// Template for each individual tag
    	// Default: @FILE:tpl/tag.tpl
    	// Possible placeholders:
    	// 	[+url+], [+class+], [+tag+], [+count+], [+favlinks_links+]
    
    /**
    * Date-specific parameters
    *--------------------------------------------------------------------------*/
    
    $params['groupByYears'] = isset($groupByYears) ? intval($groupByYears) : 1;
    	// To show dates grouped by years, set to 1
    	// To show dates grouped by combined month and year, set to 0
    	// Default: 1
    	// Note: If this is set to 0, only &tplMonth will be used
    
    $params['sortYears'] = isset($sortYears) ? $sortYears : 'ASC';
    	// Direction to sort the years
    	// Can be: ASC (for ascending order) or DESC (for descending)
    	// Default: ASC
    
    $params['sortMonths'] = isset($sortMonths) ? $sortMonths : 'ASC';
    	// Direction to sort the months
    	// Can be: ASC (for ascending order) or DESC (for descending)
    	// Default: ASC
    
    $params['tplYear'] = isset($tplYear) ? $tplYear : '@FILE:tpl/year.tpl';
    	// Template for each year
    	// Default: @FILE:tpl/year.tpl
    	// Possible placeholders:
    	// 	[+url+], [+year+], [+count+], [+favlinks_months+]
    
    $params['tplMonth'] = isset($tplMonth) ? $tplMonth : '@FILE:tpl/month.tpl';
    	// Template for each month
    	// Default: @FILE:tpl/month.tpl
    	// Possible placeholders:
    	// 	[+url+], [+month+], [+month_name+], [+year+], [+count+], [+favlinks_links+]
    
    /*--------------------------------------------------------------------------*/
    
    include_once($params['basePath'] . 'classes/favoritelinks.class.inc.php');
    
    if ($params['paginate'])  // Set offset according to requested value
    {
    	$params['offsetPage'] = isset($_REQUEST['offset']) ? intval($_REQUEST['offset']) : 0;
    	$params['offsetLinks'] += ($params['offsetPage'] * $params['maxLinks']);
    }
    
    if (class_exists('FavoriteLinks'))
    	$favlinks = new FavoriteLinks($params);
    else
    {
    	$modx->logEvent(1, 3, 'FavoriteLinks: Error loading main class:' . $params['basePath'] . 'classes/favoritelinks.class.inc.php');
    	return 'FavoriteLinks: Error loading main class';
    }
    
    return $favlinks->execute();
    ?>




    Favorite Links Module

    The module will automatically create the needed database table when it is first run. The default table name is: prefix_favorite_links, and you can manage your links through this module.

    Module name: Favorite Links

    Description: Management for favorite links snippet.

    Required files: none.

    [b]Module Code
    //<?php
    /**
    * Favorite Links Module
    * Brian Stanback
    * 2007/04/12
    * Version 0.0.5
    * Compatible with MODx 0.9.5
    *
    * Loosely Based off of Friends List Module by Garry Nutting
    */
    
    $_lang['action_title'] = 'Links Listing';
    $_lang['add_link'] = 'Add Link';
    $_lang['cancel'] = 'Cancel';
    $_lang['delete'] = 'Delete';
    $_lang['edit'] = 'Edit';
    $_lang['error_unfilled'] = 'Please fill all required fields';
    $_lang['form_message'] = '<p>To add a new link, enter link details in the form on the left and click the "Add Link" button. Tags should be separated by a comma and a space, eg. tag1, tag2, tag3. For an explanation and more information on XFN, please visit <a href="http://gmpg.org/xfn/">http://gmpg.org/xfn/</a></p><br /><p>To update a link select the link from the list, click the \'Edit\' button, and update the details in the form on the left.</p><br /><p>To remove a link select the link from the list and click the \'Delete\' button.</p>';
    $_lang['form_title'] = 'Add/Update a Link';
    $_lang['favlinks_title'] = 'Title';
    $_lang['favlinks_url'] = 'URL';
    $_lang['favlinks_summ'] = 'Summary';
    $_lang['favlinks_rating'] = 'Rating';
    $_lang['favlinks_tags'] = 'Tags';
    $_lang['favlinks_xfn'] = 'XFN';
    $_lang['filter'] = 'Filter';
    $_lang['go'] = 'Go';
    $_lang['link_added'] = 'The link has been added.';
    $_lang['link_deleted'] = 'The link has been deleted.';
    $_lang['link_updated'] = 'The link has been updated.';
    $_lang['module_title'] = 'Favorite Links';
    $_lang['no_records'] = 'No links have been added.';
    $_lang['update_link'] = 'Update Link';
    
    $basePath = $modx->config['base_path'];
    $siteURL = $modx->config['site_url'];
    $tb_prefix = $modx->db->config['table_prefix'];
    
    // Include grid control class
    include_once($basePath . 'manager/includes/controls/datagrid.class.php');
    
    // Get configuration values
    global $manager_theme;
    $table = $modx->getFullTableName('favorite_links');
    
    // Handle request data
    $link_id = (isset($_POST['link_id'])) ? intval($_POST['link_id']) : '';
    $title = (isset($_POST['title'])) ? $_POST['title'] : '';
    $url = (isset($_POST['url']) && $_POST['url'] != 'http://') ? $_POST['url'] : 'http://';
    $summ = (isset($_POST['summ'])) ? $_POST['summ'] : '';
    $tags = (isset($_POST['tags'])) ? $_POST['tags'] : '';
    $xfn = (isset($_POST['xfn'])) ? $_POST['xfn'] : '';
    $rating = (isset($_POST['rating'])) ? $_POST['rating'] : '';
    
    // Determine what action to take
    if (isset($_POST['edit']) && $link_id > 0)
    {
    	$action = 'edit';
    }
    elseif (isset($_POST['delete']) && $link_id > 0)
    {
    	$action = 'delete';
    }
    elseif (isset($_POST['action']))
    {
    	$action = $_POST['action'];
    }
    
    // Check for required fields
    if (($action == 'add' || $action == 'update') && (!$title || !$url))
    	$error = $_lang['error_unfilled'] . ' (*)';
    
    if ($action == 'update' && !isset($error))
    {
    	$tags = ':' . str_replace(', ', ':', $tags) . ':';
    
    	$fields = array(
    		'title' => $modx->db->escape($title),
    		'url' => $modx->db->escape($url),
    		'summ' => $modx->db->escape($summ),
    		'tags' => $modx->db->escape($tags),
    		'xfn' => $modx->db->escape($xfn),
    		'rating' => $modx->db->escape($rating)
    	);
    
    	$modx->db->update($fields, $table, 'id=' . $link_id);
    	$msg .= $_lang['link_updated'];
    	$link_id = ''; $title = ''; $url = 'http://'; $summ = ''; $tags = ''; $xfn = ''; $rating = '';
    }
    elseif ($action == 'delete')
    {
    	$modx->db->delete($table, 'id=' . $link_id);
    	$msg .= $_lang['link_deleted'];
    	$link_id = '';
    }
    elseif ($action == 'add' && !isset($error))
    {
    	$tags = ':' . str_replace(', ', ':', $tags) . ':';
    
    	$fields = array(
    		'title' => $modx->db->escape($title),
    		'url' => $modx->db->escape($url),
    		'summ' => $modx->db->escape($summ),
    		'tags' => $modx->db->escape($tags),
    		'xfn' => $modx->db->escape($xfn),
    		'rating' => $modx->db->escape($rating),
    		'added' => time()
    	);
    
    	$modx->db->insert($fields, $table);
    	$msg .= $_lang['link_added'];
    	$link_id = ''; $title = ''; $url = 'http://'; $summ = ''; $tags = ''; $xfn = ''; $rating = '';
    }
    
    if ($action == 'edit')
    {
    	$result = $modx->db->select('*', $table, 'id="' . $link_id . '"');
    	$row = $modx->db->getRow($result);
    	$title = $row['title'];
    	$url = $row['url'];
    	$summ = $row['summ'];
    	$tags = str_replace(':', ', ', trim($row['tags'], ':'));
    	$xfn = $row['xfn'];
    	$rating = $row['rating'];
    }
    
    if (!function_exists('checkLinksTable'))
    {
    	/**
    	* Create the table if it does not exist
    	*/
    	function checkLinksTable($table, &$errorMsg)
    	{
    		global $modx;
    
    		$sql = "CREATE TABLE IF NOT EXISTS " . $table . " (" .
    			"`id` int(11) NOT NULL auto_increment PRIMARY KEY, " .
    			"`title` varchar(255)  NOT NULL, " .
    			"`url` varchar(255) NOT NULL, " .
    			"`summ` text, " .
    			"`tags` varchar(255), " .
    			"`rating` float(2), " .
    			"`xfn` varchar(64), " .
    			"`added` char(10)" .
    		")";
    
    		if (!$modx->db->query($sql))
    			$errorMsg .= "<p class=\"error\">Error creating table {$table}!</p>\n";
    	}
    }
    
    checkLinksTable($table, $errorMsg);
    
    $filter = "";
    if ($_POST['filter'])
    {
    	$q = $modx->db->escape($_POST['filter']);
    	$filter .= "(url LIKE '%$q%' OR title LIKE '%$q%' OR summ LIKE '%$q%')";
    }
    if ($_POST['tag'])
    {
    	$q = $modx->db->escape($_POST['tag']);
    	if ($filter != "") $filter .= " AND ";
    	$filter .= "(tags LIKE '%:$q:%')";
    }
    
    $linksQuery = $modx->db->select('*', $table, $filter, 'added DESC');
    
    // Page header
    $output .= '
    <html>
    <head>
        <link rel="stylesheet" type="text/css" href="media/style/' . $manager_theme . '/style.css" />
        <style type="text/css">
    	.topdiv {
    		border: 0;
    	}
    	.subdiv {
    		border: 0;
    	}
    	li {
    		list-style:none;
    	}
    	.tplbutton {
    		text-align: right;
    	}
    	#bttn .bttnheight {
    		height: 25px !important;
    		padding: 0px;
    		padding-top: 6px;
    		float: left;
    		vertical-align: middle !important;
    	}
    	ul.sortableList {
    		padding-left: 20px;
    		margin: 0px;
    		width: 300px;
    		font-family: Arial, sans-serif;
    	}
    	ul.sortableList li {
    		font-weight: bold;
    		cursor: move;
    		color: grey;
    		padding: 2px 2px;
    		margin: 2px 0px;
    		border: 1px solid #000000;
    		background-image: url("media/style/' . $manager_theme . '/images/bg/grid_hdr.gif");
    		background-repeat: repeat-x;
    	}
    	#bttn .bttnheight {
    		height: 25px !important;
    		padding: 0px; 
    		padding-top: 6px;
    		float: left;
    		vertical-align: middle !important;
    	}
    	#bttn a{
    		cursor: default !important;
    		font: icon !important;
    		color: black !important;
    		border: 0px !important;
    		padding: 5px 5px 7px 5px!important;
    		white-space: nowrap !important;
    		vertical-align: middle !important;
    		background: transparent !important;
    		text-decoration: none;
    	}
    	#bttn a:hover {
    		border: 1px solid darkgreen !important;
    		padding: 4px 4px 6px 4px !important;		
    		background-image: url("media/style/' . $manager_theme . '/images/bg/button_dn.gif") !important;
    		text-decoration: none;
    	}
    	#bttn a img {
    		vertical-align: middle !important;
    	}
    	.go a {
    		cursor: default !important;
    		font: icon !important;
    		color: black !important;
    		border: 0px !important;
    		padding: 5px 5px 7px 5px !important;
    		white-space: nowrap !important;
    		vertical-align: middle !important;
    		background: transparent;
    		text-decoration: none;
    	}
    	.go a:hover {
    		border: 1px solid darkgreen !important;
    		padding: 4px 4px 6px 4px !important;
    		background: url("media/style/' . $manager_theme . '/images/bg/button_dn.gif");
    		text-decoration: none;
    	}
    	.go a img {
    		vertical-align: middle !important;
    	}
    	.form_message {
    		font-size: 85%;
    	}
    	.gridItem, .gridAltItem {
    		font-sizE: 95%;
    	}
    	td {
    		font-size: 90%;
    	}
    	</style>
    </head>
    <body>
    	<div class="subTitle" id="bttn">
            <span class="right"><img src="media/style/' . $manager_theme . '/images/_tx_.gif" width="1" height="5"><br />' . $_lang['module_title'] . '</span>
    	<div class="bttnheight"><a id="Button5" onclick="document.location.href=\'index.php?a=106\';">
    		<img src="media/style/' . $manager_theme . '/images/icons/close.gif" alt="" /> Close Favorite Links Manager</a>
    	</div>
    	<div class="bttnheight"><a id="Button5" onclick="document.location.href=\'index.php?a=106\';">
    		<img src="media/style' . $manager_theme . '/images/icons/link_add.png" alt="" /> Import / Export</a>
    	</div>
    	<div class="stay"></div> 
    	</div>
    	<div class="sectionHeader">' . $_lang['form_title'] . '</div>
    	<div class="sectionBody">
    ';
    
    $output .= '
    	<table><tr><td style="width: 60%;" valign="top">
    	<form name="details" method="post" action="">
    		<input type="hidden" name="action" value="' . (($action == 'edit') ? 'update' : 'add') . '" />
    		<input type="hidden" name="link_id" value="' . $link_id . '" />
    		<table border="0" cellspacing="0" cellpadding="1" width="95%">
    			<tr><td>' . $_lang['favlinks_title'] . ': *  </td><td><input type="text" name="title" value="' . $title . '" style="width: 100%;" /></td></tr>
    			<tr><td>' . $_lang['favlinks_url'] . ': *  </td><td><input type="text" name="url" value="' . $url . '" style="width: 100%;" /></td></tr>
    			<tr><td valign="top">' . $_lang['favlinks_summ'] . ':  </td><td><textarea name="summ" rows="2" style="width: 100%; height: auto;">' . $summ . '</textarea></td></tr>
    			<tr><td>' . $_lang['favlinks_tags'] . ':  </td><td><input type="text" name="tags" value="' . $tags . '" style="width: 100%;" /></td></tr>
    			<tr><td>' . $_lang['favlinks_xfn'] . ':  </td><td><input type="text" name="xfn" value="' . $xfn . '" style="width: 100%;" /></td></tr>
    			<tr><td>' . $_lang['favlinks_rating'] . ':  </td><td><input type="text" name="rating" value="' . $rating . '" style="width: 100%;" /></td></tr>
    			<tr><td> </td><td><br /><input type="submit" name="postform" value="' . (($action == 'edit') ? $_lang['update_link'] : $_lang['add_link']) . '" /></td></tr>
    		</table>
    	</form>
    	</td><td valign="top" class="form_message">' . $_lang['form_message'] . '</td></tr></table>';
    
    // Output any messages or errors
    $output .= '<div>';
    $output .= '<p class="warning">' . $msg . '</p>';
    if (isset($error)) {
    	$output .= '<p class="warning">' . $error . '</p>';
    }
    $output .= '</div>';
    
    $output .= '</div>
    		<div class="sectionHeader">' . $_lang['action_title'] . '</div>
    		<div class="sectionBody">
    		<form name="links" method="post" action="">
    		<div style="float: right;">' . $_lang['filter'] . ': <select name="tag" onchange="links.submit()"><option value="">-</option>';
    
    // Get tags from database, populating array
    $result = $modx->db->select('DISTINCT tags', $table);
    $tags = array();
    while ($row = $modx->db->getRow($result))
    {
    	foreach (explode(':', trim($row['tags'], ':')) as $tag) $tags[$tag]++;
    }
    ksort($tags);
    foreach ($tags as $tag => $occurances)
    {
    	// Output tags
    	if ($tag == $_POST['tag'])
    		$output .= "<option selected=\"selected\" value=\"$tag\">$tag</option>";
    	else
    		$output .= "<option value=\"$tag\">$tag</option>";
    }
    
    $output .= '
    		</select>  <input type="text" name="filter" size="20" value="' . $_POST['filter'] . '" /> <input type="submit" value="' . $_lang['go'] . '" /></div>
    		<input type="submit" value="' . $_lang['edit'] . '" name="edit" />  
    		<input type="submit" value="' . $_lang['delete'] . '" name="delete" /><br /><br />';
    
    // Render links grid
    $grid = new DataGrid('', $linksQuery);
    $grid->noRecordMsg = $_lang['no_records'];
    $grid->cssClass = "grid";
    $grid->columnHeaderClass = "gridHeader";
    $grid->itemClass = "gridItem";
    $grid->altItemClass = "gridAltItem";
    $grid->columns = " ," . $_lang['favlinks_title'] . "," . $_lang['favlinks_url'] . "," . $_lang['favlinks_rating'];
    $grid->colTypes = "template:<input type='radio' value='[+id+]' name='link_id' />";
    $grid->colWidths = "5%,35%,35%,15%";
    $grid->fields = "template,title,url,rating";
    $output .= $grid->render();
    
    $output .= '</form>';
    $output .= '</div></body></html>';
    
    return $output;




    Favorite Links Form

    Please visit this post for code and example use of FavoriteLinksForm. Thanks.
      • 27376
      • 576 Posts
      This is exactly what one of my co-workers is looking for!

      I’ll definitely take a look at it and give you some feedback smiley
        • 1932
        • 137 Posts
        @sirlancelot Great, let me know how it works. wink

        I wanted to address a couple more topics:

        Date formatting

        To show link added date, I recommend using PHx to convert from timestamp to a formatted date. Below is an example TV which would be included in the chunk called by the Favorite Links snippet:

        [+added:date=`%b %d, %Y`+]

        Rating Display

        By default, the rating will be displayed as an integer/float.
        For something a little more visual, you can use a custom PHx modifier to format an integer/float into corresponding images.

        To create the custom modifier, create a new snippet with the name: phx:stars
        Add the following code to the snippet, changing the configuration options as you see fit.

        <?php
        //////////////////////////////////////////////////////////////////
        // Configuration options
        
        $max = 5;  // Maximum rating
        $imageOn = 'images/star.png';  // Image for highlighted star
        $imageOff = 'images/star_off.png';  // Image for unhighlighted star
        $imageParts = array(
        	25 => 'images/star_quarter.png',  // Quarter star (optional)
        	50 => 'images/star_half.png',  // Half star (optional)
        	75 => 'images/star_3quarter.png'  // 3 quarters star (optional)
        );
        
        //////////////////////////////////////////////////////////////////
        
        $rating = intval($output);
        $partial_star = 0;
        if ($rating > 0 && $rating != $output)
        	$partial_star = ($output - $rating)*100;
        
        $output = '';
        
        for ($i=0; $i<$rating; $i++)
        	$output .= '<img src="' . $imageOn . '" alt="*" />';
        
        if ($partial_star > 0)
        {
        	$output .= '<img src="' . $imageParts[$partial_star] . '" alt="~" />';
        	$rating++;
        }
        
        for ($i=$rating; $i<$max; $i++)
        	$output .= '<img src="' . $imageOff . '" alt="" />';
        
        return $output;
        ?>
        


        The rating can then be included like this: [+rating:stars+]

        The image paths could potentially be passed using PHx’s option parameter but it didn’t seem quite as straight forward to me as adding the configuration to the top of the snippet.

        More information on PHx can be found here: http://wiki.modxcms.com/index.php/PHx

        Extending XFN

        It’s possible to use XFN + JSON + Javascript to create dynamic, decentralized social networks. Below is an example of how to create your own xfn.js JSON "feed":

        Create a new page, with a text/javascript content type and blank template. In the page content area enter:

        pingXFN({
        name:"Your Name",
        url:"[(site_url)]",
        pubDate:"[*pub_date:date=`%Y/%m/%d %H:%M:%S %z`*]",
        tags:["tag1","tag2","tag3"],
        people:[
        [[FavoriteLinks? &rowChunk=`blogRollJSON` &tags=`blogroll`]]
        ]
        });
        


        Because the rel modifiers in the XFN field must be reformatted, I have written yet another PHx modifier for creating JSON-compatible rel entities.

        Create a new snippet called phx:jsonRel and enter the following code:

        <?php
        $relationships = array(
        	'me' => 'me',
        	'contact' => 'friendship',
        	'acquaintance' => 'friendship',
        	'friend' => 'friendship',
        	'met' => 'physical',
        	'co-worker' => 'professional',
        	'colleague' => 'professional',
        	'co-resident' => 'geographical',
        	'neighbor' => 'geographical',
        	'child' => 'family',
        	'parent' => 'family',
        	'sibling' => 'family',
        	'spouse' => 'family',
        	'kin' => 'family',
        	'muse' => 'romantic',
        	'crush' => 'romantic',
        	'date' => 'romantic',
        	'sweetheart' => 'romantic'
        );
        
        $relationshipsMulti = array('professional', 'romantic');
        
        $rel = explode(' ', $output);
        $output = '';
        $final = array();
        
        // Find and categorize matching relationships
        foreach ($rel as $attr)
        {
        	if (isset($relationships[$attr]))
        	{
        		if (!isset($final[$relationships[$attr]]))
        			$final[$relationships[$attr]] = '"' . $attr . '"';
        		else
        			$final[$relationships[$attr]] .= ',"' . $attr . '"';
        	}
        }
        
        // Prepare final string
        foreach ($final as $name => $attr)
        {
        	if (in_array($name, $relationshipsMulti))
        		$output .= "$name:[$attr],";
        	else
        		$output .= "$name:$attr,";
        }
        
        return substr($output, 0, -1);
        ?>
        


        The above modifier will turn something like: met friend co-worker colleague
        Into: professional:["co-worker","colleague"],friend:"friend",physical:"met"

        Now create a new chunk named blogRollJSON and enter the following code:

        {name:"[+summ+]",url:"[+url+]",rel:{[+xfn:jsonRel+]}},
        
        


        Of course, you can choose whatever names you like for the chunk/PHx modifier. At this point, you should now be able to view your new XFN JSON document.

        Please check out http://www.mindsack.com/?page_id=39 for examples and the Javascript code which parses, spiders, and displays your social network.
          • 12656
          • 13 Posts
          wow that’s exactly what i was looking for. nice work!
            • 19667
            • 26 Posts
            Really nice module/snippet

            I’ve been testing it. So far. Nothing to complain about. Grin.

            You asked for additional features?

            - A Bookmarklet to add pages while browsing in your favorite Browser
            - Tag Cloud
            - Abilities to pass tags via $_GET
            - May be you add a default wrapper (<ul></ul>)

            I could provided a german language pack. Let me know when you think about a multilingual version.


            Maybe i come up with more. Later on.


              • 1932
              • 137 Posts
              All good suggestions..

              - I haven’t done much with bookmarklets, what about allowing users to define a prefix/suffix for generated links? That way you could have:

              Prefix: javascript:myFunction(’
              Suffix: ’);

              .. and the URL would be generated as <a href="javascript:myFunction(’http://www.nowhere.com/’);">

              - I like the tag cloud idea, I’ll work on that in the near future.

              - Passing of tags/query via _GET would most likely be needed for tag cloud support and should be pretty easy to implement. Should I allow for multiple tags to be passed? Thoughts on how the tags should be separated?

              - I thought about having a wrapper option but didn’t see any advantages over simply adding the wrapper around the snippet call in the template/chunk.

              Thanks for the testing/suggestions. Once I get this ready for an official release, a German language pack would be great. wink
                • 33337
                • 3,975 Posts
                very nice work!

                Is there anything in the plans for front end link postings? think about link directory etc.

                best regards.
                  Zaigham R - MODX Professional | Skype | Email | Twitter

                  Digging the interwebs for #MODX gems and bringing it to you. modx.link
                  • 19667
                  • 26 Posts
                  Actually I meant something else when I mentioned the bookmarklet. The bookmarket would be a tool for the admin of the website to easily add new links to his modx fav links database table. Without the need to login in the backend. The module is nice when you update, delete or may be change the summary of links. But the bookmarklet would let you add a link quick.
                  See what i mean?

                  Should I allow for multiple tags to be passed? Thoughts on how the tags should be separated?
                  Whats wrong with urlencode/decode?
                  page.html?tags=modx%2Bgreat+cms
                  But it doesnt look nice when i think about it. Ok. No thoughts on that one.

                  I thought about having a wrapper option but didn’t see any advantages over simply adding the wrapper around the snippet call in the template/chunk.
                  But it will be easily forgotten. I am pretty sure.


                  Another feature request. grin
                  Import (Firefox) Bookmarks? Export?
                    • 27376
                    • 576 Posts
                    Quote from: 0x7B at Feb 12, 2007, 10:43 AM
                    The bookmarket would be a tool for the admin of the website to easily add new links to his modx fav links database table.
                    It’s like having your very own del.icio.us bookmarks wink

                    This Module would truly rock if this functionality were implemented smiley
                      • 1932
                      • 137 Posts
                      Favorite Links Form

                      Below is a standalone snippet, intended to be used with the main Favorite Links module/snippet. This snippet allows posting of links from the front-end as well as bookmarklet support. This snippet requires 2 files for the bookmarklet and a default template for html form display so I have included everything in a zip file which can be downloaded at: http://cyphergate.com/FavoriteLinksForm.zip

                      The zip file should contain the folder "FavoriteLinksForm" upload this to your web server and place it into the assets/snippets/ directory. The possible parameters are commented in the top section of the snippet code.

                      After uploading the folder, create the new snippet:

                      Snippet Name: FavoriteLinksForm
                      Description: <strong>0.0.2</strong> Snippet for bookmarklets and front-end link submission

                      <?php
                      /*
                      * Favorite Links Form Snippet
                      * Brian Stanback
                      * 2007/04/15
                      * Version 0.0.2
                      * Compatible with MODx 0.9.5
                      * 
                      * Provides the necessary files and handler for boookmarklets as well as the ability for 
                      * posting of links in the front-end.
                      * 
                      * /////////////////////////////////////////////////////////////////////////////////////////
                      * SUPPORTED PARAMETERS:
                      * /////////////////////////////////////////////////////////////////////////////////////////
                      *
                      * &bookmarkletPh
                      *	Build and save the bookmarklet link to specified placeholder name
                      *	Add [+your_placeholder_name+] below the snippet in your page/chunk/template to display the link
                      *	(This is required for retrieving of the initial bookmarklet link)
                      *
                      * &dispForm
                      *	Set to 1 to display the html-base link submission form (default: 0)
                      *
                      * &formChunk
                      *	Chunk containing the submission form
                      *	(defaults to template in assets/snippets/FavoriteLinksForm/default_form.tpl)
                      *
                      * &successMesg
                      *	Output the specified message upon submission
                      *
                      * &successPage
                      *	Redirect to the specified page ID upon submission (overrides successMessage)
                      *	Can be set to `referrer` to redirect back to the referring page
                      *
                      * &errorMesg
                      *	Output the specified message if required fields are left unfilled
                      *
                      * &errorPage
                      *	Redirect to the specified page ID if required fields are left unfilled
                      *	Can be set to `referrer` to redirect back to the referring page
                      *
                      * &existsMesg
                      *	Output the specified message if a link with the same URL already exists
                      *
                      * &existsPage
                      *	Redirect to the specified page ID if a link with the same URL already exists
                      *	Can be set to `referrer` to redirect back to the referring page
                      *
                      * &unauthMesg
                      *	Output the specified message if the user is not allowed to post links
                      *
                      * &unauthPage
                      *	Redirect to the specified page ID if the user is not allowed to post links
                      *	Can be set to `referrer` to redirect back to the referring page
                      *
                      * &managerAccess
                      *	Set to 1 to allow link posting from logged in managers, 0 to deny
                      *	(default: 1)
                      *
                      * &webAccess
                      *	List of web groups allowed to post links, separated by a commas (no space)
                      *	Set to 1 to allow all authenticated web users
                      *
                      * &ipAccess
                      *	List of allowed IP addresses, separated by a commas (no space)
                      *
                      * &tagSeparator
                      *	Separate tags with the specified string
                      *	(default: comma and a space)
                      *
                      *///////////////////////////////////////////////////////////////////////////////////////*/
                      
                      // Sanitize parameters
                      $bookmarkletPh = (isset($bookmarkletPh)) ? $bookmarkletPh : '';
                      $dispForm = (isset($dispForm)) ? intval($dispForm) : 0;
                      $formChunk = (isset($formChunk)) ? $formChunk : '';
                      $successMesg = (isset($successMesg)) ? $successMesg : 'The link has been added.';
                      $successPage = (isset($successPage)) ? $successPage : '';
                      $errorMesg = (isset($errorMesg)) ? $errorMesg : 'Please fill all required fields.';
                      $errorPage = (isset($errorPage)) ? $errorPage : '';
                      $existsMesg = (isset($existsMesg)) ? $existsMesg : 'The link you are trying to add already exists in the database.';
                      $existsPage = (isset($existsPage)) ? $existsPage : '';
                      $unauthMesg = (isset($unauthMesg)) ? $unauthMesg : 'You are not allowed to access this portion of the site.';
                      $unauthPage = (isset($unauthPage)) ? $unauthPage : '';
                      $managerAccess = (isset($managerAccess)) ? intval($managerAccess) : 1;
                      $anonAccess = (isset($anonAccess)) ? intval($anonAccess) : 0;
                      $webAccess = (isset($webAccess)) ? $webAccess : '';
                      $ipAccess = (isset($ipAccess)) ? $ipAccess : '';
                      $tagSeparator = (isset($tagSeparator)) ? $tagSeparator : ', ';
                      
                      $snippetPath = $modx->config['base_path'] . 'assets/snippets/FavoriteLinksForm/';
                      $siteUrl = $modx->config['site_url'];
                      $pageUrl = $modx->makeUrl($modx->documentIdentifier, '', 'posted=1');
                      $pageUrl = preg_replace('#^' . $modx->config['base_url'] . '#', '', $pageUrl, 1);
                      
                      $allowed = false;
                      
                      if ($anonAccess)  // Check for anonymous access
                      {
                      	$allowed = true;
                      }
                      elseif ($managerAccess && $_SESSION['usertype'] == 'manager')  // Check for manager access
                      {
                      	$allowed = true;
                      }
                      elseif ($webAccess && $modx->isMemberOfWebGroup(explode(',', $webAccess)))  // Check for web user access
                      {
                      	$allowed = true;
                      }
                      elseif ($ipAccess != '')  // Check for access based on IP address
                      {
                      	while ($allowed == false && $tmpIp = each(explode(',', $ipAccess)))
                      	{
                      		if ($tmpIp[1] == $_SERVER['REMOTE_ADDR'])
                      			$allowed = true;
                      	}
                      }
                      
                      if (!$allowed)  // Cannot authorize current user
                      {
                      	if ($unauthPage != '')
                      	{
                      		// Redirect user
                      		if ($unauthPage == 'referrer')
                      			$forwardUrl = $_SERVER['HTTP_REFERER'];
                      		else
                      			$forwardUrl = $modx->makeUrl($unauthPage);
                      		$modx->sendRedirect($forwardUrl);
                      		exit;
                      	}
                      	else
                      		return $unauthMesg;  // Return message
                      }
                      
                      if ($bookmarkletPh != '')
                      {
                      	// Create bookmarklet link placeholder
                      	$bookmarkletLink = '<a href="javascript:';
                      	$bookmarkletLink .= "var bkmkletBase='" . $siteUrl . "';var bkmkletLanding='" . $pageUrl . "';";
                      	$bookmarkletLink .= "s=document.createElement('script');s.type='text/javascript';";
                      	$bookmarkletLink .= "s.src=bkmkletBase+'assets/snippets/FavoriteLinksForm/bookmarklet/favlinks.js';";
                      	$bookmarkletLink .= "void(document.body.appendChild(s));";
                      	$bookmarkletLink .= '">Add Favorite Link</a>';
                      	$bookmarkletLink .= "\n";
                      	$modx->setPlaceholder($bookmarkletPh, $bookmarkletLink);
                      }
                      
                      $output = '';
                      
                      if (isset($_GET['posted']))
                      {
                      	// Form was submitted, check submitted fields
                      	$link_url = (isset($_POST['url'])) ? $_POST['url'] : '';
                      	$link_title = (isset($_POST['title'])) ? $_POST['title'] : '';
                      	$link_summ = (isset($_POST['summ'])) ? $_POST['summ'] : '';
                      	$link_tags = (isset($_POST['tags'])) ? $_POST['tags'] : '';
                      	$link_rating = (isset($_POST['rating'])) ? $_POST['rating'] : '';
                      	$link_xfn = (isset($_POST['xfn'])) ? $_POST['xfn'] : '';
                      
                      	if ($link_url == '' || $link_title == '')
                      	{
                      		if ($errorPage != '')
                      		{
                      			// Redirect user
                      			if ($errorPage == 'referrer')
                      				$forwardUrl = $_SERVER['HTTP_REFERER'];
                      			else
                      				$forwardUrl = $modx->makeUrl($errorPage);
                      			$modx->sendRedirect($forwardUrl);
                      			exit;
                      		}
                      		else
                      			$output .= $errorMesg;  // Append error message
                      	}
                      	else
                      	{
                      		// Check for existing link
                      		$result = $modx->db->select('id', $modx->getFullTableName('favorite_links'), "url = '" . $modx->db->escape($link_url) . "'");
                      		if ($modx->db->getRecordCount($result) > 0)  // Link already exists
                      		{
                      			if ($existsPage != '')
                      			{
                      				// Redirect user
                      				if ($existsPage == 'referrer')
                      					$forwardUrl = $_SERVER['HTTP_REFERER'];
                      				else
                      					$forwardUrl = $modx->makeUrl($successPage);
                      				$modx->sendRedirect($forwardUrl);
                      				exit;
                      			}
                      			else
                      				$output .= $existsMesg;  // Append exists message
                      		}
                      		else
                      		{
                      			// Prepare query
                      			$link_tags = ':' . str_replace($tagSeparator, ':', $link_tags) . ':';
                      			$fields = array(
                      				'title' => $modx->db->escape($link_title),
                      				'url' => $modx->db->escape($link_url),
                      				'summ' => $modx->db->escape($link_summ),
                      				'tags' => $modx->db->escape($link_tags),
                      				'rating' => $modx->db->escape($link_rating),
                      				'xfn' => $modx->db->escape($link_xfn),
                      				'added' => time()
                      			);
                      
                      			// Submit query to database
                      			$modx->db->insert($fields, $modx->getFullTableName('favorite_links'));
                      
                      			if ($successPage != '')
                      			{
                      				// Redirect user
                      				if ($successPage == 'referrer')
                      					$forwardUrl = $_SERVER['HTTP_REFERER'];
                      				else
                      					$forwardUrl = $modx->makeUrl($successPage);
                      				$modx->sendRedirect($forwardUrl);
                      				exit;
                      			}
                      			else
                      			{
                      				$output .= $successMesg;  // Append success message
                      				$link_url = '';
                      				$link_title = '';
                      				$link_summ = '';
                      				$link_tags = '';
                      				$link_rating = '';
                      				$link_xfn = '';
                      			}
                      		}
                      	}
                      }
                      
                      if ($dispForm)
                      {
                      	// Retrieve form chunk/template
                      	if ($formChunk != '') $template = $modx->getChunk($formChunk);
                      	else $template = file_get_contents($snippetPath . 'default_form.tpl');
                      
                      	// Set form/field value placeholders
                      	$values['landing_page'] = $pageUrl;
                      	$values['url_value'] = $link_url;
                      	$values['title_value'] = $link_title;
                      	$values['summ_value'] = $link_summ;
                      	$values['tags_value'] = $link_tags;
                      	$values['rating_value'] = $link_rating;
                      	$values['xfn_value'] = $link_xfn;
                      
                      	// Replace placeholders
                      	foreach ($values as $name => $value)
                      	{
                      		$template = str_replace('[+' . $name . '+]', $value, $template);
                      	}
                      
                      	$output .= $template;  // Append form to output
                      }
                      
                      return $output;
                      ?>
                      


                      Example of use on a page:

                      [[FavoriteLinksForm? &bookmarkletPh=`bookmarkleet` &successPage=`referrer` ]]
                      [+bookmarkleet+]
                      


                      Using the bookmarklet: Once you are able to see the generated bookmarklet link, drag it to your bookmarks toolbar or add it as a favorite. You should then be able to click the shortcut when you are visiting any other site and it will show an add link dialog box/form.

                      Bookmarklets rule!! kiss I setup the Javascript form so that it will auto populate most of the fields using document location, page title, and meta tags. What I really like is setting success page to referrer, so you can add the link and go right back to where you came from. This might be a good scenario for using AJAX to process the request without any redirecting but I’ll have to wait until MODx provides a standardized way to create custom front controllers.

                      I suppose you could create your own RSS template for the display snippet, and allow others to access your favorites via live bookmark.

                      I don’t guarantee this release to be bug-free since I wrote and tested it in less than a day but do give it a shot an let me know what you guys think.



                      I’m starting work on an "archive" display snippet which will allow for a list of tags, tag cloud, and listing of links organized by date added. I may also start grouping the code used in the snippets into functions or possibly external classes.. are there any MODx conventions for doing this? I’ll make an official release once I finish the archival snippet and get some more input from testers.

                      Currently, there is no checking for duplicate URLs.. I will most likely add another configuration option on the FavoriteLinksForm snippet and FavoriteLinks module for allowing/disallowing duplicates.

                      I’m also planning on adding another option to the main display snippet for custom tag separator as well as support for field truncation (although you could technically use PHx to do this laugh). As far as passing of tags by $_GET string, you can currently do this using PHx placeholders with custom modifiers:

                      Create a new snippet named phx:get with the following code:

                      <?php
                          return $_GET[$options];
                      ?>
                      


                      And then call PHx inside of the snippet call, example:

                      URL format: testpage.html?mytags=test (or use mod_rewrite to create virtual directories)
                      [[FavoriteLinks? tags=`[+phx:get=`mytags`+]`]]