Links plugin: When URL rewriting is enabled, return rewritten URLs for search results
3 // Reminder: always indent with 4 spaces (no tabs).
4 // +---------------------------------------------------------------------------+
5 // | Links Plugin 2.1 |
6 // +---------------------------------------------------------------------------+
9 // | This file does two things: 1) it implements the necessary Geeklog Plugin |
10 // | API method and 2) implements all the common code needed by the Links |
11 // | Plugins' PHP files. |
12 // +---------------------------------------------------------------------------+
13 // | Copyright (C) 2000-2009 by the following authors: |
15 // | Authors: Tony Bibbs - tony AT tonybibbs DOT com |
16 // | Mark Limburg - mlimburg AT users.sourceforge DOT net |
17 // | Jason Whittenburg - jwhitten AT securitygeeks DOT com |
18 // | Dirk Haun - dirk AT haun-online DOT de |
19 // | Trinity Bays - trinity93 AT gmail DOT com |
20 // | Oliver Spiesshofer - oliver AT spiesshofer DOT com |
21 // | Euan McKay - info AT heatherengineering DOT com |
22 // +---------------------------------------------------------------------------+
24 // | This program is licensed under the terms of the GNU General Public License|
25 // | as published by the Free Software Foundation; either version 2 |
26 // | of the License, or (at your option) any later version. |
28 // | This program is distributed in the hope that it will be useful, |
29 // | but WITHOUT ANY WARRANTY; without even the implied warranty of |
30 // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
31 // | See the GNU General Public License for more details. |
33 // | You should have received a copy of the GNU General Public License |
34 // | along with this program; if not, write to the Free Software Foundation, |
35 // | Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
37 // +---------------------------------------------------------------------------+
40 * Implementation of the Plugin API for the Links plugin
45 if (strpos(strtolower($_SERVER['PHP_SELF']), 'functions.inc') !== false) {
46 die('This file can not be used on its own.');
49 $plugin_path = $_CONF['path'] . 'plugins/links/';
52 * Language file include
54 $langfile = $plugin_path . 'language/' . $_CONF['language'] . '.php';
55 if (file_exists($langfile)) {
56 require_once $langfile;
58 require_once $plugin_path . 'language/english.php';
62 * Check and see if we need to load the plugin configuration
64 if (!isset($_LI_CONF['linksloginrequired'])) {
65 require_once $_CONF['path_system'] . 'classes/config.class.php';
67 $li_config = config::get_instance();
68 $_LI_CONF = $li_config->get_config('links');
72 // +---------------------------------------------------------------------------+
73 // | Geeklog Plugin API Implementation |
74 // +---------------------------------------------------------------------------+
77 * Returns the items for this plugin that should appear on the main menu
79 * NOTE: this MUST return the url/value pairs in the following format
80 * $<arrayname>[<label>] = <url>
82 * @return mixed menu entry, or boolean false if disabled / hidden
85 function plugin_getmenuitems_links()
87 global $_CONF, $_LI_CONF, $LANG_LINKS;
89 if (($_LI_CONF['hidelinksmenu'] == 1) || (COM_isAnonUser() &&
90 ($_CONF['loginrequired'] || $_LI_CONF['linksloginrequired']))) {
94 $menuitems[$LANG_LINKS[114]] = $_CONF['site_url'] . '/links/index.php';
100 * Return headlines for New Links section in the What's New block, if enabled
102 * @return mixed array(headline, byline), or boolean false if disabled
105 function plugin_whatsnewsupported_links()
107 global $_LI_CONF, $LANG_LINKS, $LANG_WHATSNEW;
109 if ($_LI_CONF['hidenewlinks'] == 0) {
110 $retval = array($LANG_LINKS[84],
111 COM_formatTimeString($LANG_WHATSNEW['new_last'],
112 $_LI_CONF['newlinksinterval'])
122 * Return new links for the What's New block
124 * @return string HTML list of new links
127 function plugin_getwhatsnew_links()
129 global $_CONF, $_TABLES, $_USER, $_LI_CONF, $LANG_LINKS;
134 $sql = "SELECT lid,title FROM {$_TABLES['links']} WHERE (date >= (DATE_SUB(NOW(), INTERVAL {$_LI_CONF['newlinksinterval']} SECOND)))" . COM_getPermSQL('AND') . LINKS_getCategorySQL('AND') . ' ORDER BY date DESC LIMIT 15';
135 $result = DB_query($sql);
136 $nrows = DB_numRows($result);
140 for ($x = 0; $x < $nrows; $x++) {
141 $A = DB_fetchArray($result);
142 $A['title'] = stripslashes($A['title']);
144 // redirect link via portal.php so we can count the clicks
145 $lcount = COM_buildUrl($_CONF['site_url']
146 . '/links/portal.php?what=link&item=' . $A['lid']);
148 $title = COM_truncate($A['title'], $_CONF['title_trim_length'],
150 $link_title = array();
151 if ($title != $A['title']) {
152 $link_title = array('title' => $A['title']);
155 $newlinks[] = COM_createLink($title, $lcount, $link_title) . LB;
158 $retval .= COM_makeList($newlinks, 'list-new-plugins');
160 $retval .= $LANG_LINKS[88] . '<br' . XHTML . '>' . LB;
167 * Implements the [link:] autotag.
169 * @param string $op operation to perform
170 * @param string $content item (e.g. story text), including the autotag
171 * @param array $autotag parameters used in the autotag
172 * @param mixed tag names (for $op='tagname') or formatted content
175 function plugin_autotags_links($op, $content = '', $autotag = '')
177 global $_CONF, $_TABLES, $LANG_DIRECTION;
179 if ($op == 'tagname' ) {
181 } else if ($op == 'parse') {
182 $lid = COM_applyFilter($autotag['parm1']);
184 $url = COM_buildUrl($_CONF['site_url']
185 . '/links/portal.php?what=link&item=' . $lid);
186 if (empty($autotag['parm2'])) {
187 $result = DB_query("SELECT url, title FROM {$_TABLES['links']} WHERE lid = '$lid'");
188 list($siteurl, $linktext) = DB_fetchArray($result);
189 $linktext = stripslashes($linktext);
191 $linktext = $autotag['parm2'];
192 $siteurl = DB_getItem($_TABLES['links'], 'url', "lid = '$lid'");
195 if ((!empty($LANG_DIRECTION)) && ($LANG_DIRECTION == 'rtl')) {
202 $link = COM_createLink($linktext, $url, $attr);
203 $content = str_replace($autotag['tagstr'], $link, $content);
211 * Called by the plugin Editor to display the current plugin code version
212 * This may be different than the version installed and registered currently.
213 * If newer then you may want to run the update
215 * @return string version number
218 function plugin_chkVersion_links ()
222 require_once $_CONF['path'] . 'plugins/links/autoinstall.php';
224 $inst_parms = plugin_autoinstall_links('links');
226 return $inst_parms['info']['pi_version'];
230 * Helper function: count number of links and total number of clicks
232 * @return array(number of links, number of clicks);
235 function LINKS_countLinksAndClicks()
239 $result = DB_query("SELECT COUNT(*) AS count,SUM(hits) AS clicks FROM {$_TABLES['links']}" . COM_getPermSQL() . LINKS_getCategorySQL('AND'));
240 $A = DB_fetchArray($result);
241 $total_links = $A['count'];
242 $total_clicks = $A['clicks'];
243 if (empty($total_clicks)) {
247 return array($total_links, $total_clicks);
251 * Shows the statistics for the links plugin on stats.php.
252 * If $showsitestats is 1 then we are to only print the overall stats in the
253 * 'site statistics box' otherwise we show the detailed stats
255 * @param int $showsitestate Flag to let us know which stats to get
256 * @param string HTML for the stats section
259 function plugin_showstats_links ($showsitestats)
261 global $_CONF, $_TABLES, $LANG_LINKS_STATS;
263 require_once $_CONF['path_system'] . 'lib-admin.php';
267 $result = DB_query("SELECT lid,url,title,hits FROM {$_TABLES['links']} WHERE (hits > 0)" . COM_getPermSQL('AND') . LINKS_getCategorySQL('AND') . " ORDER BY hits DESC LIMIT 10");
268 $nrows = DB_numRows ($result);
271 array('text' => $LANG_LINKS_STATS['stats_page_title'],
273 'header_class' => 'stats-header-title'
275 array('text' => $LANG_LINKS_STATS['stats_hits'],
277 'header_class' => 'stats-header-count',
278 'field_class' => 'stats-list-count'
282 $text_arr = array('has_menu' => false,
283 'title' => $LANG_LINKS_STATS['stats_headline'],
285 for ($i = 0; $i < $nrows; $i++) {
286 $A = DB_fetchArray ($result);
287 $title = stripslashes (str_replace ('$', '$', $A['title']));
288 $url = COM_buildUrl ($_CONF['site_url']
289 . '/links/portal.php?what=link&item=' . $A['lid']);
290 $sid = COM_createLink($title, $url, array('title' => $A['url']));
291 $hits = COM_numberFormat ($A['hits']);
292 $data_arr[] = array('title' => $title,
297 $retval .= ADMIN_simpleList ('', $header_arr, $text_arr, $data_arr);
299 $retval .= COM_startBlock ($LANG_LINKS_STATS['stats_headline']);
300 $retval .= $LANG_LINKS_STATS['stats_no_hits'];
301 $retval .= COM_endBlock ();
308 * New stats plugin API function for proper integration with the site stats
310 * @return array(item text, item count);
313 function plugin_statssummary_links ()
315 global $LANG_LINKS_STATS;
317 list($total_links, $total_clicks) = LINKS_countLinksAndClicks ();
319 $item_count = COM_NumberFormat ($total_links)
320 . ' (' . COM_NumberFormat ($total_clicks) . ')';
323 return array ($LANG_LINKS_STATS['links'], $item_count);
327 * Geeklog is asking us to provide any items that show up in the type
328 * drop-down on search.php. Let's users search for links.
330 * @return array (plugin name/entry title) pair for the dropdown
333 function plugin_searchtypes_links()
337 $tmp['links'] = $LANG_LINKS[14];
344 * This searches for links matching the user query and returns an array for the
345 * header and table rows back to search.php where it will be formated and printed
347 * @param string $query Keywords user is looking for
348 * @param date $datestart Start date to get results for
349 * @param date $dateend End date to get results for
350 * @param string $topic The topic they were searching in
351 * @param string $type Type of items they are searching, or 'all' (deprecated)
352 * @param int $author Get all results by this author
353 * @param string $keyType search key type: 'all', 'phrase', 'any'
354 * @param int $page page number of current search (deprecated)
355 * @param int $perpage number of results per page (deprecated)
356 * @return object search result object
359 function plugin_dopluginsearch_links($query, $datestart, $dateend, $topic, $type, $author, $keyType, $page, $perpage)
361 global $_TABLES, $LANG_LINKS;
363 // Make sure the query is SQL safe
364 $query = trim(addslashes($query));
366 $sql = "SELECT lid AS id, title, description, UNIX_TIMESTAMP(date) AS date, owner_id AS uid, hits, ";
367 $sql .= "CONCAT('/links/portal.php?what=link&item=', lid) AS url ";
368 $sql .= "FROM {$_TABLES['links']} WHERE date <> 1 ";
369 $sql .= COM_getPermSQL('AND') . ' ';
371 if (!empty ($author)) {
372 $sql .= "AND (owner_id = '$author') ";
375 $search = new SearchCriteria('links', $LANG_LINKS[14]);
377 $columns = array('title' => 'title', 'description');
378 $sql .= $search->getDateRangeSQL('AND', 'date', $datestart, $dateend);
379 list($sql,$ftsql) = $search->buildSearchSQL($keyType, $query, $columns, $sql);
381 $search->setSQL($sql);
382 $search->setFTSQL($ftsql);
384 $search->setAppendQuery(false);
385 $search->setURLRewrite(true);
392 * This will put an option for links in the command and control block on
395 * @return mixed array(title, url, icon), or boolean false when not allowed
398 function plugin_cclabel_links()
400 global $_CONF, $LANG_LINKS;
402 if (SEC_hasRights ('links.edit')) {
403 return array ($LANG_LINKS[14],
404 $_CONF['site_admin_url'] . '/plugins/links/index.php',
405 plugin_geticon_links ());
412 * returns the administrative option for this plugin
414 * @return mixed array(title, url, num. links), or void when not allowed
417 function plugin_getadminoption_links()
419 global $_CONF, $_TABLES, $LANG_LINKS;
421 if (SEC_hasRights ('links.edit,links.delete', 'OR')) {
422 $total_links = DB_getItem ($_TABLES['links'], 'COUNT(*)',
423 COM_getPermSql (''));
425 return array ($LANG_LINKS[14],
426 $_CONF['site_admin_url'] . '/plugins/links/index.php',
432 * A user is about to be deleted. Update ownership of any links owned
433 * by that user or delete them.
435 * @param int $uid User id of deleted user
439 function plugin_user_delete_links ($uid)
441 global $_TABLES, $_LI_CONF;
443 if (DB_count ($_TABLES['links'], 'owner_id', $uid) == 0) {
447 if ($_LI_CONF['delete_links'] == 1) {
449 DB_delete($_TABLES['links'], 'owner_id', $uid);
451 // assign ownership to a user from the Root group
452 $rootgroup = DB_getItem ($_TABLES['groups'], 'grp_id',
453 "grp_name = 'Root'");
454 $result = DB_query ("SELECT DISTINCT ug_uid FROM {$_TABLES['group_assignments']} WHERE ug_main_grp_id = $rootgroup ORDER BY ug_uid LIMIT 1");
455 $A = DB_fetchArray ($result);
456 $rootuser = $A['ug_uid'];
457 DB_query ("UPDATE {$_TABLES['links']} SET owner_id = $rootuser WHERE owner_id = $uid");
462 * Do we support feeds?
464 * @return array id/name pairs of all supported feeds
467 function plugin_getfeednames_links()
473 $result = DB_query ("SELECT cid,category FROM {$_TABLES['linkcategories']} GROUP BY category ORDER BY category ASC");
474 $num = DB_numRows ($result);
477 $feeds[] = array ('id' => 'all', 'name' => 'all categories');
479 for ($i = 0; $i < $num; $i++) {
480 $A = DB_fetchArray ($result);
481 $feeds[] = array ('id' => $A['cid'], 'name' => $A['category']);
491 * @param int $feed feed ID
494 * @return array feed entries
497 function plugin_getfeedcontent_links ($feed, &$link, &$update)
499 global $_CONF, $_TABLES;
501 $result = DB_query( "SELECT topic,limits,content_length FROM {$_TABLES['syndication']} WHERE fid = '$feed'" );
502 $S = DB_fetchArray( $result );
504 $result = DB_query( "SELECT lid,owner_id,title,description,UNIX_TIMESTAMP(date) AS modified FROM " . $_TABLES['links'] . links_buildSql ($S['topic'], $S['limits']) );
508 $nrows = DB_numRows( $result );
510 for( $i = 0; $i < $nrows; $i++ )
512 $row = DB_fetchArray( $result );
513 $lids[] = $row['lid'];
515 $linktitle = stripslashes( $row['title'] );
516 $linkdesc = stripslashes( $row['description'] );
518 $linklink = COM_buildUrl( $_CONF['site_url']
519 . '/links/portal.php?what=link&item=' . $row['lid'] );
521 $content[] = array( 'title' => $linktitle,
522 'summary' => $linkdesc,
524 'uid' => $row['owner_id'],
525 'author' => COM_getDisplayName( $row['owner_id'] ),
526 'date' => $row['modified'],
527 'format' => 'plaintext'
531 $link = $_CONF['site_url'] . '/links/index.php';
532 $update = implode( ',', $lids );
538 * Helper function: Build part of an SQL request
540 * @param string $cid category id
541 * @param string $limits limit (number of entries or number of hours)
542 * @return string part of an SQL request
545 function links_buildSql($cid, $limits)
549 $where = "cid='" . addslashes($cid) . "'";
553 if (!empty ($limits)) {
554 if (substr ($limits, -1) == 'h') { // last xx hours
556 $hours = substr ($limits, 0, -1);
557 if (!empty ($where)) {
560 $where .= "date >= DATE_SUB(NOW(),INTERVAL $hours HOUR)";
562 $limitsql = ' LIMIT ' . $limits;
567 $limitsql = ' LIMIT 10';
570 if (!empty ($where)) {
571 $where = ' WHERE ' . $where;
574 $sql = $where . ' ORDER BY date DESC' . $limitsql;
580 * Checking if links feeds are up to date
582 * @param int $feed id of feed to be checked
583 * @param string $topic topic (actually: cid)
584 * @param string $update_data data describing current feed contents
585 * @param string $limit number of entries or number of hours
586 * @param string $updated_type (optional) type of feed to be updated
587 * @param string $updated_topic (optional) feed's "topic" to be updated
588 * @param string $updated_id (optional) id of entry that has changed
589 * @return boolean true: feed data is up to date; false: isn't
592 function plugin_feedupdatecheck_links ($feed, $topic, $update_data, $limit, $updated_type = '', $updated_topic = '', $updated_id = '')
598 if ($updated_type != 'links') {
599 // we're not interested
605 $sql = "SELECT lid FROM {$_TABLES['links']}" . links_buildSql ($topic, $limit);
606 $result = DB_query ($sql);
607 $num = DB_numRows ($result);
610 for ($i = 0; $i < $num; $i++) {
611 $A = DB_fetchArray ($result);
613 if ($A['lid'] == $updated_id) {
614 // this feed has to be updated - no further checks needed
620 $current = implode (',', $lids);
622 return ($current != $update_data) ? false : true;
626 * Update the Links plugin
628 * @return int Number of message to display (true = generic success msg)
631 function plugin_upgrade_links()
633 global $_CONF, $_TABLES, $_LI_CONF;
635 $installed_version = DB_getItem($_TABLES['plugins'], 'pi_version',
636 "pi_name = 'links'");
637 $code_version = plugin_chkVersion_links();
638 if ($installed_version == $code_version) {
643 require_once $_CONF['path'] . 'plugins/links/autoinstall.php';
645 if (! plugin_compatible_with_this_version_links('links')) {
649 $inst_parms = plugin_autoinstall_links('links');
650 $pi_gl_version = $inst_parms['info']['pi_gl_version'];
652 if (! isset($_LI_CONF['new_window'])) {
653 $c = config::get_instance();
654 $c->add('new_window',false,'select',0,0,1,55,TRUE,'links');
657 if (! isset($_LI_CONF['category_permissions'])) {
658 $c = config::get_instance();
659 $c->add('fs_cpermissions', NULL, 'fieldset', 0, 3, NULL, 0, true, 'links');
660 $c->add('category_permissions', array (3, 2, 2, 2),
661 '@select', 0, 3, 12, 150, true, 'links');
664 DB_query("UPDATE {$_TABLES['plugins']} SET pi_version = '$code_version', pi_gl_version = '$pi_gl_version' WHERE pi_name = 'links'");
670 * Called during site migration - handle changed URLs or paths
672 * @param array $old_conf contents of the $_CONF array on the old site
673 * @param boolean true on success, otherwise false
676 function plugin_migrate_links($old_conf)
681 'linkcategories' => 'cid, description',
682 'links' => 'lid, description, url',
683 'linksubmission' => 'lid, description, url'
686 if ($old_conf['site_url'] != $_CONF['site_url']) {
687 INST_updateSiteUrl($old_conf['site_url'], $_CONF['site_url'], $tables);
694 * Geeklog informs us that we're about to be enabled or disabled
696 * @param boolean $enable true = we're being enabled, false = disabled
700 function plugin_enablestatechange_links ($enable)
704 $is_enabled = $enable ? 1 : 0;
706 // toggle links feeds
707 DB_query ("UPDATE {$_TABLES['syndication']} SET is_enabled = $is_enabled WHERE type = 'links'");
711 * Counts the items that are submitted
713 * @return int number of items in submission queue
716 function plugin_submissioncount_links()
722 if (plugin_ismoderator_links ()) {
723 $retval = DB_count ($_TABLES['linksubmission']);
730 * Checks that the current user has the rights to moderate the
731 * plugin, returns true if this is the case, false otherwise
733 * @return boolean Returns true if moderator
736 function plugin_ismoderator_links()
738 return SEC_hasRights ('links.moderate');
743 * Returns SQL & Language texts to moderation.php
745 * @return mixed plugin object or void if not allowed
748 function plugin_itemlist_links()
750 global $_TABLES, $LANG_LINKS_SUBMIT;
752 if (plugin_ismoderator_links()) {
753 $plugin = new Plugin();
754 $plugin->submissionlabel = $LANG_LINKS_SUBMIT[11];
755 $plugin->submissionhelpfile = 'cclinksubmission.html';
756 $plugin->getsubmissionssql = "SELECT lid AS id,title,cid AS category,url FROM {$_TABLES['linksubmission']} ORDER BY title ASC";
757 $plugin->addSubmissionHeading($LANG_LINKS_SUBMIT[8]);
758 $plugin->addSubmissionHeading($LANG_LINKS_SUBMIT[10]);
759 $plugin->addSubmissionHeading($LANG_LINKS_SUBMIT[9]);
766 * returns list of moderation values
768 * The array returned contains (in order): the row 'id' label, main plugin
769 * table, moderation fields (comma seperated), and plugin submission table
771 * @return array Returns array of useful moderation values
774 function plugin_moderationvalues_links()
780 'lid,cid,url,description,title,date,owner_id',
781 $_TABLES['linksubmission']);
786 * Performs plugin exclusive work for items approved by moderation
788 * While moderation.php handles the actual move from linkssubmission
789 * to links tables, within the function we handle all other approval
792 * @param string $id Identifying string
793 * @return string Any wanted HTML output
796 function plugin_moderationapprove_links($id)
798 global $_TABLES, $_USER, $_GROUPS, $_LI_CONF;
800 // The linksubmission only keeps track of the submitter's uid, but not
801 // of grous and permissions. So set those to sensible defaults.
803 if (isset($_GROUPS['Links Admin'])) {
804 $group_id = $_GROUPS['Links Admin'];
806 $group_id = SEC_getFeatureGroup('links.moderate');
810 SEC_setDefaultPermissions($A, $_LI_CONF['default_permissions']);
812 DB_query("UPDATE {$_TABLES['links']} SET group_id = '$group_id', perm_owner = '{$A['perm_owner']}', perm_group = '{$A['perm_group']}', perm_members = '{$A['perm_members']}', perm_anon = '{$A['perm_anon']}' WHERE lid = '$id'");
818 * Performs plugin exclusive work for items deleted by moderation
820 * While moderation.php handles the actual removal from <plugin>submission
821 * table, within this function we handle all other deletion
824 * @param string $id Identifying string
825 * @return string Any wanted HTML output
828 function plugin_moderationdelete_links($id)
832 // these tables should not contain any rows with ml_id = $id
833 // this is done 'just in case'
834 DB_delete ($_TABLES['linksubmission'], 'lid', $id);
840 * Check submission form values and save if OK. Else show form again
842 * @param array $A The link record
843 * @return string Any wanted HTML output
846 function plugin_savesubmission_links($A)
852 if (!empty ($A['title']) && !empty ($A['description']) &&
853 !empty ($A['url'])) {
854 $retval = plugin_save_submit_links ($A);
856 $retval .= COM_siteHeader()
857 . COM_startBlock ($LANG12[22], '',
858 COM_getBlockTemplate ('_msg_block', 'header'))
860 . COM_endBlock (COM_getBlockTemplate ('_msg_block', 'footer'))
861 . submissionform('links')
869 * Shows link submission form
871 * @return string HTML for the link submission form
874 function plugin_submit_links()
876 global $_CONF, $LANG_LINKS_SUBMIT, $LANG12;
878 $retval = COM_startBlock ($LANG_LINKS_SUBMIT[1], 'submitlink.html');
880 $linkform = new Template($_CONF['path'] . 'plugins/links/templates');
881 $linkform->set_file('linkform', 'submitlink.thtml');
882 $linkform->set_var('site_url', $_CONF['site_url']);
883 $linkform->set_var('layout_url', $_CONF['layout_url']);
884 $linkform->set_var('lang_title', $LANG12[10]);
885 $linkform->set_var('lang_link', $LANG_LINKS_SUBMIT[2]);
886 $linkform->set_var('lang_category', $LANG_LINKS_SUBMIT[3]);
888 if (isset($_REQUEST['cid'])) {
889 $category = $_REQUEST['cid'];
891 $linkform->set_var('link_category_options', links_select_box(2, $category));
892 $linkform->set_var('lang_description', $LANG12[15]);
893 $linkform->set_var('lang_htmlnotallowed', $LANG12[35]);
894 $linkform->set_var('lang_submit', $LANG12[8]);
895 $linkform->set_var('max_url_length', 255);
896 $linkform->set_var('xhtml', XHTML);
897 $linkform->parse('theform', 'linkform');
898 $retval .= $linkform->finish($linkform->get_var('theform'));
899 $retval .= COM_endBlock();
905 * Saves a link submission
907 * @param array $A Data for that submission
908 * @return string HTML redirect
911 function plugin_save_submit_links($A)
913 global $_CONF, $_TABLES, $_USER, $_LI_CONF, $LANG12;
917 // pseudo-formatted link description for the spam check
918 $spamcheck = '<p>'. COM_createLink($A['title'], $A['url']) .' ('
919 . $A['categorydd'] . ')<br' . XHTML . '>' . $A['description']
921 $result = PLG_checkforSpam($spamcheck, $_CONF['spamx']);
923 COM_updateSpeedlimit('submit');
924 COM_displayMessageAndAbort($result, 'spamx', 403, 'Forbidden');
927 $A['cid'] = strip_tags(COM_stripslashes($A['categorydd']));
930 if (!empty($A['cid'])) {
931 $cid = addslashes($A['cid']);
932 $cat = DB_getItem($_TABLES['linkcategories'], 'category',
939 $retval .= COM_startBlock($LANG12[22], '',
940 COM_getBlockTemplate('_msg_block', 'header'))
942 . COM_endBlock(COM_getBlockTemplate('_msg_block', 'footer'))
943 . submissionform('links')
949 $A['cid'] = addslashes($A['cid']);
950 $A['description'] = addslashes(htmlspecialchars(COM_checkWords($A['description'])));
951 $A['title'] = addslashes(strip_tags(COM_checkWords($A['title'])));
952 $A['url'] = addslashes(COM_sanitizeUrl($A['url']));
953 $A['lid'] = addslashes(COM_makeSid());
955 COM_updateSpeedlimit('submit');
956 if (COM_isAnonUser()) {
957 $owner_id = 1; // anonymous user
959 $owner_id = $_USER['uid'];
962 if (($_LI_CONF['linksubmission'] == 1) && !SEC_hasRights('links.submit')) {
963 $result = DB_save($_TABLES['linksubmission'],
964 'lid,cid,url,description,title,date,owner_id',
965 "{$A['lid']},'{$A['cid']}','{$A['url']}','{$A['description']}','{$A['title']}',NOW(),$owner_id");
967 if ($_LI_CONF['notification'] == 1) {
968 LINKS_sendNotification($_TABLES['linksubmission'], $A);
971 $retval = COM_refresh($_CONF['site_url']
972 . '/index.php?msg=1&plugin=links');
974 } else { // add link directly
976 if (SEC_hasRights('links.submit')) {
977 $A['group_id'] = SEC_getFeatureGroup('links.submit');
979 $A['group_id'] = DB_getItem($_TABLES['groups'], 'grp_id',
980 "grp_name = 'All Users'");
982 SEC_setDefaultPermissions($A, $_LI_CONF['default_permissions']);
984 $result = DB_save($_TABLES['links'],
985 'lid,cid,url,description,title,date,owner_id,group_id,perm_owner,perm_group,perm_members,perm_anon',
986 "{$A['lid']},'{$A['cid']}','{$A['url']}','{$A['description']}','{$A['title']}',NOW(),$owner_id,{$A['group_id']},{$A['perm_owner']},{$A['perm_group']},{$A['perm_members']},{$A['perm_anon']}");
988 PLG_itemSaved($A['lid'], 'links');
990 if ($_LI_CONF['notification'] == 1) {
991 LINKS_sendNotification($_TABLES['links'], $A);
993 COM_rdfUpToDateCheck('links', $A['cid'], $A['lid']);
995 $retval = COM_refresh($_CONF['site_url']
996 . '/index.php?msg=4&plugin=links');
1003 * Send an email notification for a new submission.
1005 * @param string $table Table where the new submission can be found
1006 * @param array $A submission data
1009 function LINKS_sendNotification ($table, $A)
1011 global $_CONF, $_TABLES, $LANG_LINKS, $LANG_LINKS_SUBMIT, $LANG08;
1013 $title = stripslashes ($A['title']);
1014 $description = stripslashes ($A['description']);
1016 $mailbody = "$LANG_LINKS_SUBMIT[8]: $title\n"
1017 . "$LANG_LINKS_SUBMIT[9]: <{$A['url']}>\n"
1018 . "$LANG_LINKS_SUBMIT[3]: {$A['category']}\n\n"
1019 . $description . "\n\n";
1020 if ($table == $_TABLES['linksubmission']) {
1021 $mailbody .= "$LANG_LINKS[10] <{$_CONF['site_admin_url']}/moderation.php>\n\n";
1023 $mailbody .= "$LANG_LINKS[114] <{$_CONF['site_url']}/links/index.php?category=" . urlencode ($A['category']) . ">\n\n";
1025 $mailsubject = $_CONF['site_name'] . ' ' . $LANG_LINKS_SUBMIT[11];
1027 $mailbody .= "\n------------------------------\n";
1028 $mailbody .= "\n$LANG08[34]\n";
1029 $mailbody .= "\n------------------------------\n";
1031 COM_mail ($_CONF['site_mail'], $mailsubject, $mailbody);
1035 * Returns the URL of the plugin's icon
1037 * @return string URL of the icon
1040 function plugin_geticon_links ()
1044 return $_CONF['site_url'] . '/links/images/links.png';
1047 function plugin_getListField_links($fieldname, $fieldvalue, $A, $icon_arr)
1049 global $_CONF, $LANG_ACCESS, $LANG_LINKS_ADMIN;
1053 $access = SEC_hasAccess($A['owner_id'],$A['group_id'],$A['perm_owner'],$A['perm_group'],$A['perm_members'],$A['perm_anon']);
1055 switch($fieldname) {
1058 $retval = COM_createLink(
1060 "{$_CONF['site_admin_url']}/plugins/links/index.php?mode=edit&lid={$A['lid']}"
1066 $retval = $LANG_ACCESS['edit'];
1068 $retval = $LANG_ACCESS['readonly'];
1072 $retval = COM_createLink(stripslashes($A['title']), $A['url']);
1075 $retval = links_validateUrl($A['url']);
1077 case 'beforevalidate';
1078 $retval = $LANG_LINKS_ADMIN[57];
1081 if (isset($A['indent'])) {
1082 $indent = ($A['indent'] - 1) * 20;
1086 $cat = COM_createLink($A['category'],
1087 "{$_CONF['site_url']}/links/index.php?category="
1088 . urlencode($A['cid']));
1089 $retval = "<span style=\"padding-left:{$indent}px;\">$cat</span>";
1093 $retval = $fieldvalue;
1101 function plugin_getListField_categories($fieldname, $fieldvalue, $A, $icon_arr)
1103 global $_CONF, $_TABLES, $LANG_ACCESS, $LANG_LINKS_ADMIN;
1107 $access = SEC_hasAccess($A['owner_id'],$A['group_id'],$A['perm_owner'],$A['perm_group'],$A['perm_members'],$A['perm_anon']);
1109 switch($fieldname) {
1112 $retval = COM_createLink(
1114 "{$_CONF['site_admin_url']}/plugins/links/category.php?mode=edit&cid=" . urlencode($A['cid'])
1120 $retval = $LANG_ACCESS['edit'];
1122 $retval = $LANG_ACCESS['readonly'];
1126 $indent = ($A['indent'] - 1) * 20;
1127 $cat = COM_createLink($A['category'],
1128 "{$_CONF['site_url']}/links/index.php?category="
1129 . urlencode($A['cid']));
1130 $retval = "<span style=\"padding-left:{$indent}px;\">$cat</span>";
1134 $retval = COM_createLink(
1135 $icon_arr['addchild'],
1136 "{$_CONF['site_admin_url']}/plugins/links/category.php?mode=edit&pid=" . urlencode($A['cid'])
1141 if ($A['tid'] == 'all') {
1142 $retval = $LANG_LINKS_ADMIN[35];
1144 $retval = DB_getItem($_TABLES['topics'], 'topic',
1145 "tid = '{$A['tid']}'");
1147 if (empty($retval)) {
1148 $retval = $A['tid'];
1152 $retval = $fieldvalue;
1161 function links_validateUrl($url)
1163 global $LANG_LINKS_STATUS;
1165 require_once 'HTTP/Request.php';
1169 $req = new HTTP_Request($url);
1170 $req->setMethod(HTTP_REQUEST_METHOD_HEAD);
1171 $req->addHeader('User-Agent', 'Geeklog/' . VERSION);
1173 $response = $req->sendRequest();
1174 if (PEAR::isError($response)) {
1175 $retval = $response->getMessage();
1177 $status_code = $req->getResponseCode();
1178 if (isset($LANG_LINKS_STATUS[$status_code])) {
1179 $retval = $status_code . ": " . $LANG_LINKS_STATUS[$status_code];
1181 $retval = $LANG_LINKS_STATUS[999];
1189 * Set template variables
1191 * @param string $templatename name of template, e.g. 'header'
1192 * @param ref $template reference of actual template
1195 * Note: A plugin should use its name as a prefix for the names of its
1196 * template variables, e.g. 'links_xxx' and 'lang_links_xxx'.
1197 * 'button_links' is an exception, as such a variable existed for header.thtml
1198 * in Geeklog 1.3.11 and earlier, where the Links section was an integral part
1199 * of Geeklog. It is added here for backward-compatibility.
1202 function plugin_templatesetvars_links ($templatename, &$template)
1206 if ($templatename == 'header') {
1207 $template->set_var ('button_links', $LANG_LINKS[14]);
1212 * Automatic uninstall function for plugins
1216 * This code is automatically uninstalling the plugin.
1217 * It passes an array to the core code function that removes
1218 * tables, groups, features and php blocks from the tables.
1219 * Additionally, this code can perform special actions that cannot be
1220 * foreseen by the core code (interactions with other plugins for example)
1223 function plugin_autouninstall_links ()
1226 /* give the name of the tables, without $_TABLES[] */
1227 'tables' => array('links','linksubmission','linkcategories'),
1228 /* give the full name of the group, as in the db */
1229 'groups' => array('Links Admin'),
1230 /* give the full name of the feature, as in the db */
1231 'features' => array('links.edit', 'links.moderate', 'links.submit'),
1232 /* give the full name of the block, including 'phpblock_', etc */
1233 'php_blocks' => array('phpblock_topic_categories', 'phpblock_topic_links'),
1234 /* give all vars with their name */
1240 // +--------------------------------------------------------------------------+
1241 // | Category HTML functions |
1242 // | Functions for building select boxes and breadcrumb trails etc. |
1243 // +--------------------------------------------------------------------------+
1246 * Build selection list
1248 function links_select_box ($access, $sel = '')
1250 global $_CONF, $LANG_LINKS, $_LI_CONF;
1253 $menu = '<option value="' . $_LI_CONF['root'] . '">' . $LANG_LINKS['root'] . '</option>';
1254 // get option values
1255 $list = links_select_box_recursive($menu, $_LI_CONF['root'], $sel, ' ', $access);
1256 // return list of options
1262 * Build recursive tree
1264 function links_select_box_recursive (&$menu, $cid, $sel, $indent, $access)
1266 global $_CONF, $_TABLES;
1268 $cat = addslashes($cid);
1269 $sql = "SELECT cid,category
1270 FROM {$_TABLES['linkcategories']}
1271 WHERE (pid='{$cat}') " . COM_getPermSQL('AND', 0, $access) . "
1273 $query = DB_query($sql);
1274 while (list($cid, $category) = DB_fetchArray($query)) {
1275 // set selected item
1278 $menu .= '<option value="' . htmlspecialchars($cid)
1279 . '" selected="selected">' . $indent . $category
1283 $menu .= '<option value="' . htmlspecialchars($cid) . '">'
1284 . $indent . $category . '</option>';
1286 // Check and see if this category has any sub categories
1287 if (DB_count($_TABLES['linkcategories'], 'pid', addslashes($cid)) > 0) {
1289 $dum = links_select_box_recursive ($menu, $cid, $sel,
1290 $indent . ' ', $access);
1299 * Build breadcrumb trail
1301 * Breadcrumb trail does not use the "root" category in the database: the top
1302 * level category is set from the language file using $LANG_LINKS['root']
1304 function links_breadcrumbs($root, $cid)
1306 global $_CONF, $_TABLES, $LANG_LINKS;
1309 $separator = ' > ';
1311 $cat = addslashes($cid);
1314 if ($root != $cid) {
1315 while ($pid != $root) {
1316 $parent = DB_query("SELECT cid,pid,category FROM {$_TABLES['linkcategories']} WHERE cid='{$cat}'");
1317 $A = DB_fetchArray($parent);
1319 $content = stripslashes($A['category']);
1320 $url = $_CONF['site_url'] . '/links/index.php?category='
1321 . urlencode($A['cid']);
1322 $breadcrumb = COM_createLink($content, $url) . $separator
1325 $breadcrumb = '<b>' . $A['category'] . '</b>' . $breadcrumb;
1329 $cat = addslashes($c);
1333 $url = $_CONF['site_url'] . '/links/index.php';
1334 if (empty($breadcrumb)) {
1335 $breadcrumb = '<b>' . $LANG_LINKS['root'] . '</b>';
1337 $breadcrumb = COM_createLink($LANG_LINKS['root'], $url) . $separator . $breadcrumb;
1340 $breadcrumb = '<span class="links-breadcrumb">' . $LANG_LINKS[126] . ' '
1341 . $breadcrumb . '</span>';
1348 // +--------------------------------------------------------------------------+
1349 // | PHP Block functions |
1350 // +--------------------------------------------------------------------------+
1353 * Returns a list of links that belong to categories associated with
1356 * Relies on the fact that $topic is set (and sanitized) in lib-common.php
1358 function phpblock_topic_links()
1360 global $_CONF, $_TABLES, $LANG_LINKS, $topic;
1364 if (!empty($topic)) {
1365 $tid = addslashes($topic);
1366 $result = DB_query("SELECT l.lid, l.title, l.url, c.cid FROM {$_TABLES['links']} AS l LEFT JOIN {$_TABLES['linkcategories']} AS c ON l.cid=c.cid WHERE c.tid='{$tid}' OR c.tid='all'" . COM_getPermSQL('AND', 0, 2, 'c'));
1367 $nrows = DB_numRows($result);
1369 for ($i = 0; $i < $nrows; $i++) {
1370 $A = DB_fetchArray($result);
1371 $content = stripslashes($A['title']);
1372 $url = COM_buildUrl($_CONF['site_url']
1373 . '/links/portal.php?what=link&item=' . $A['lid']);
1374 $retval .= COM_createLink($content, $url,
1375 array('title' => $A['url']))
1376 . '<br' . XHTML . '>';
1386 * Returns a list of categories that are associated with the current topic
1388 * Relies on the fact that $topic is set (and sanitized) in lib-common.php
1390 function phpblock_topic_categories()
1392 global $_CONF, $_TABLES, $LANG_LINKS, $topic;
1396 if (!empty($topic)) {
1397 $tid = addslashes($topic);
1398 $result = DB_query("SELECT category, cid FROM {$_TABLES['linkcategories']} WHERE tid='{$tid}' OR tid='all'" . COM_getPermSQL ('AND'));
1399 $nrows = DB_numRows($result);
1401 for ($i = 0; $i < $nrows; $i++) {
1402 $A = DB_fetchArray($result);
1403 $content = stripslashes($A['category']);
1404 $url = $_CONF['site_url'] . '/links/index.php?category='
1405 . urlencode($A['cid']);
1406 $retval .= COM_createLink($content, $url) . '<br' . XHTML . '>';
1415 * Return information for a link
1417 * @param string $lid link ID or '*'
1418 * @param string $what comma-separated list of properties
1419 * @param int $uid user ID or 0 = current user
1420 * @param array $options (reserved for future extensions)
1421 * @return mixed string or array of strings with the information
1424 function plugin_getiteminfo_links($lid, $what, $uid = 0, $options = array())
1426 global $_CONF, $_TABLES;
1428 // parse $what to see what we need to pull from the database
1429 $properties = explode(',', $what);
1431 foreach ($properties as $p) {
1433 case 'date-modified':
1434 $fields[] = 'UNIX_TIMESTAMP(date) AS unixdate';
1438 $fields[] = 'description';
1444 $fields[] = 'title';
1447 // needed for $lid == '*', but also in case we're only requesting
1448 // the URL (so that $fields isn't emtpy)
1457 $fields = array_unique($fields);
1459 if (count($fields) == 0) {
1465 // prepare SQL request
1470 $where = " WHERE lid = '" . addslashes($lid) . "'";
1474 $permSql = COM_getPermSql($permOp, $uid)
1475 . LINKS_getCategorySQL('AND', $uid);
1477 $permSql = COM_getPermSql($permOp) . LINKS_getCategorySQL('AND');
1479 $sql = "SELECT " . implode(',', $fields)
1480 . " FROM {$_TABLES['links']}" . $where . $permSql;
1485 $result = DB_query($sql);
1486 $numRows = DB_numRows($result);
1489 for ($i = 0; $i < $numRows; $i++) {
1490 $A = DB_fetchArray($result);
1493 foreach ($properties as $p) {
1495 case 'date-modified':
1496 $props['date-modified'] = $A['unixdate'];
1500 $props[$p] = stripslashes($A['description']);
1503 $props['id'] = $A['lid'];
1506 $props['title'] = stripslashes($A['title']);
1509 if (empty($A['lid'])) {
1510 $props['url'] = COM_buildUrl($_CONF['site_url']
1511 . '/links/portal.php?what=link&item=' . $lid);
1513 $props['url'] = COM_buildUrl($_CONF['site_url']
1514 . '/links/portal.php?what=link&item='
1519 // return empty string for unknown properties
1526 foreach ($props as $key => $value) {
1529 $mapped[$key] = $value;
1537 $retval[] = $mapped;
1544 if (($lid != '*') && (count($retval) == 1)) {
1545 $retval = $retval[0];
1552 * Return SQL expression to check for allowed categories.
1554 * Creates part of an SQL expression that can be used to only request links
1555 * from categories to which the user has access to.
1557 * Note that this function does SQL requests, so you should cache
1558 * the resulting SQL expression if you need it more than once.
1560 * @param string $type part of the SQL expr. e.g. 'WHERE', 'AND'
1561 * @param int $u_id user id or 0 = current user
1562 * @param string $table table name if ambiguous (e.g. in JOINs)
1563 * @return string SQL expression string (may be empty)
1564 * @see COM_getTopicSQL
1567 function LINKS_getCategorySQL($type = 'WHERE', $u_id = 0, $table = '')
1569 global $_TABLES, $_USER, $_GROUPS;
1571 $categorysql = ' ' . $type . ' ';
1573 if (!empty($table)) {
1577 $UserGroups = array();
1578 if (($u_id <= 0) || (isset($_USER['uid']) && ($u_id == $_USER['uid']))) {
1579 if (!COM_isAnonUser()) {
1580 $uid = $_USER['uid'];
1584 $UserGroups = $_GROUPS;
1587 $UserGroups = SEC_getUserGroups($uid);
1590 if (empty($UserGroups)) {
1591 // this shouldn't really happen, but if it does, handle user
1592 // like an anonymous user
1596 if (SEC_inGroup('Root', $uid)) {
1600 $parents = array('root');
1604 $result = DB_query("SELECT cid FROM {$_TABLES['linkcategories']}"
1605 . COM_getPermSQL('WHERE', $uid) . " AND pid IN ('"
1606 . implode("','", $parents) . "')");
1609 while ($C = DB_fetchArray($result)) {
1610 $parents[] = $C['cid'];
1611 $cids[] = $C['cid'];
1613 } while (count($parents) > 0);
1615 if (count($cids) > 0) {
1616 $categorysql .= "({$table}cid IN ('" . implode("','", $cids) . "'))";
1618 $categorysql .= '0';
1621 return $categorysql;
1625 * Provide URL of a documentation file
1627 * @param string $file documentation file being requested, e.g. 'config'
1628 * @return mixed URL or false when not available
1631 function plugin_getdocumentationurl_links($file)
1640 if (isset($docurl)) {
1643 $doclang = COM_getLanguageName();
1644 $docs = 'docs/' . $doclang . '/links.html';
1645 if (file_exists($_CONF['path_html'] . $docs)) {
1646 $retval = $_CONF['site_url'] . '/' . $docs;
1648 $retval = $_CONF['site_url'] . '/docs/english/links.html';