Revision [8441]

This is an old revision of AdvancedReferrersHandler made by JavaWoman on 2005-05-24 19:41:53.

 

Advanced Referrers Handler


See also:
  • Documentation: AdvancedReferrersHandlerInfo.
  • If you're looking for how to adapt the styling of the user interface to match your own skin, see 8. css/refmenu_col.css at the end of the page.
This is the development page for an advanced referrers handler.
 


Referrer lists generated by WikkaWiki on high-traffic servers are likely to become unmanageable, due to their ever growing size. Of course you can limit the volume of the referrers by changing the referrers_purge_time in the ConfigurationOptions, so referrers older then n days are purged from the database. To allow a better management of the referrer list without purging the DB, I've modified the referrer handlers to allow searching and filtering. --DarTar

After DarTar's first version published on this page, we discussed some ideas, and then started completely revising all handlers dealing with referrers and the blacklist in close cooperation (working together on code on IRC is fun!). The original referrers_sites handler is now completely integrated with the referrers handler, and also the review_blacklist and delete_referrer handlers have been modified to integrate seamlessly. The details are below, and the new version will soon be installed as a beta feature on this site, too. --JavaWoman

Preview


Here's an example of what the new interface looks like:

External pages linking to HomePage (last 7 days)

Note to spammers: This page is not indexed by search engines, so don't waste your time.


Total: 1 referrers linking to HomePage

Filter view:

Result: 1 referrers linking to HomePage

HitsReferrers
1http://javawoman.com/



Features


Current version: 0.8


Todo:


The code


1. wikka.php


The method LoadReferrers() (from line 754) is obsolete now. You can comment it out, or remove it, or even leave it in place, but it isn't used any more. All queries are completely dynamically generated depending on the "view" requested and the selection criteria given.

2. handlers/page/referrers_sites.php


This handler file is now obsolete as well; its functionality is completely integrated with the new referrers handler (below). It's best to remove or rename this file since it will not work together with the new handlers.

3. handlers/page/referrers.php


This has undergone a complete overhaul by both DarTar and JavaWoman. See the docblock and various comments in the code for details. Since it's still beta code, there is some debug code present as well - that will disappear by the time we get to version 1.0 (see the @todo list in the docblock).

  1. <?php
  2. /**
  3.  * Display, filter and search a list of referrers or referring sites for the current page or the site as a whole.
  4.  *
  5.  * Usage: append /referrers to the URL of the page
  6.  *      add global=1 to specify referrers for the site instead of the current page
  7.  *      add sites=1 to specify referrerring domains instead of full URLs
  8.  *
  9.  * This handler allows logged-in users to display, filter and search the referrer list for
  10.  * the current page and for the whole site. Current search criteria include strings,
  11.  * number of hits, reference period.
  12.  *
  13.  * @package     Handlers
  14.  * @subpackage  DatabaseHandlers
  15.  * @name        Referrers
  16.  *
  17.  * @author      {@link http://wikka.jsnx.com/DarTar Dario Taraborelli} - code cleanup, search/filter functionality added.
  18.  * @author      {@link http://wikka.jsnx.com/JavaWoman JavaWoman} - more code cleanup, accessibility, integration with referrers_sites
  19.  * @version     0.8
  20.  * @since       Wikka 1.1.6.X
  21.  *
  22.  * @todo        for 1.0:
  23.  *              - clean up debug code
  24.  *              - remove LoadReferrers() from core (commented out now)
  25.  *              - configurable choice hostname (NAME_GLOBAL) or 'this site' (config, installer)
  26.  *              - configurable parameters for building days dropdown (config, installer)
  27.  *              - configurable limit to express days as hours (config, installer)
  28.  *              - build an index on the referrer column in the referrers table (installer)
  29.  *              later:
  30.  *              - transfer filter parameters as well so we cen redirect to the exact view we came from
  31.  *              - (global) icons to represent each of the five views, small and larger versions (menu/page)
  32.  *              - adapt FormOpen() to accept id; then fix form kluge here and in stylesheet
  33.  *              - adapt text definitions to take singular-plural into account
  34.  *              - add paging
  35.  *              - turn list into form with checkboxes to allow mass blacklisting
  36.  *
  37.  * @input       string  $q  optional: string used to filter the referrers;
  38.  *              default: NULL;
  39.  *              the default can be overridden by providing a POST parameter 'q'
  40.  * @input       integer $qo optional: determines the kind of search to be performed for string $q:
  41.  *              1: search for all referrers containing a given string
  42.  *              0: search for all referrers not containing a given string
  43.  *              default: 1;
  44.  *              the default can be overridden by providing a POST parameter 'qo'
  45.  * @input       integer $h  optional: number of hits used to filter the referrers;
  46.  *              default: 1;
  47.  *              the default can be overridden by providing a POST parameter 'h'
  48.  * @input       integer $ho optional: determines the kind of filter to be applied to $h:
  49.  *              1: search for referrers with at least $h hits;
  50.  *              0: search for referrers with no more than $h hits;
  51.  *              default: 1;
  52.  *              the default can be overridden by providing a POST parameter 'ho'
  53.  * @input       integer $days  optional: number of days used to filter the referrers;
  54.  *              default: 1;
  55.  *              the default can be overridden by providing a POST parameter 'h'
  56.  * @input       integer $global optional: switches between local/global referrers:
  57.  *              1: display referrers for the whole site;
  58.  *              0: display referrers for the current page;
  59.  *              default: 0;
  60.  *              the default can be overridden by providing a GET/POST parameter 'global'
  61.  * @input       integer $sites  optional: switches between referring urls and domains
  62.  *              1: display referring sites (domains);
  63.  *              0: display referrers (URLs);
  64.  *              default: 0;
  65.  *              the default can be overridden by providing a GET/POST parameter 'sites'
  66.  * @input       integer $refdel optional: number of referrer records deleted
  67.  * @inpur       integer $bladd  optional: number of blacklist records added
  68.  */
  69.  
  70. // Utilities
  71.  
  72. /**
  73.  * Build an array of numbers consisting of 'ranges' with increasing step size in each 'range'.
  74.  *
  75.  * A list of numbers like this is useful for instance for a dropdown to choose
  76.  * a period expressed in number of days: a difference between 2 and 5 days may
  77.  * be significant while that between 92 and 95 may not be.
  78.  *
  79.  * @author      {@link http://wikka.jsnx.com/JavaWoman JavaWoman}
  80.  * @copyright   Copyright © 2005, Marjolein Katsma
  81.  * @license     http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
  82.  * @version     1.0
  83.  *
  84.  * @param   mixed   $limits required: single integer or array of integers;
  85.  *                  defines the upper limits of the ranges as well as the next step size
  86.  * @param   int     $max    required: upper limit for the whole list
  87.  *                  (will be included if smaller than the largest limit)
  88.  * @param   int     $firstinc optional: increment for the first range; default 1
  89.  * @return  array   resulting list of numbers
  90.  */
  91. function optionRanges($limits, $max, $firstinc = 1)
  92. {
  93.     // initializations
  94.     if (is_int($limits)) $limits = array($limits);
  95.     if ($firstinc < 1) $firstinc = 1;
  96.     $opts = array();
  97.     $inc = $firstinc;
  98.  
  99.     // first element is the first increment
  100.     $opts[] = $inc;
  101.     // each $limit is the upper limit of a 'range'
  102.     foreach ($limits as $limit)
  103.     {
  104.         for ($i = $inc + $inc; $i <= $limit && $i < $max; $i += $inc)
  105.         {
  106.             $opts[] = $i;
  107.         }
  108.         // we quit at $max, even if there are more $limit elements
  109.         if ($limit >= $max)
  110.         {
  111.             // add $max to the list; then break out of the loop
  112.             $opts[] = $max;
  113.             break;
  114.         }
  115.         // when $limit is reached, it becomes the new start and increment for the next 'range'
  116.         $inc = $limit;
  117.     }
  118.  
  119.     return $opts;
  120. }
  121.  
  122. // constants
  123.  
  124. define('DEBUG',FALSE);      # @@@ set TRUE to generate debugging output
  125.  
  126. define('SEARCH_LIKE','LIKE');           # search string operator
  127. define('SEARCH_UNLIKE','NOT LIKE');     # search string operator
  128. define('HITS_DEFAULT', '1');            # (was 0 for referrers, 1 for sites)
  129. define('HITS_MIN_OPTION', '>=');
  130. define('HITS_MAX_OPTION', '<=');
  131.  
  132. define('HOURS_LIMIT',2);                # days expressed as hours               @@@ could be made configurable
  133. define('DAYS_MAX', $this->GetConfigValue('referrers_purge_time'));
  134. define('DAYS_DEFAULT', '7');                    # default period to retrieve    @@@ make configurable
  135.  
  136. $days_limits = array(7,30,90,365);              # ranges for days dropdown      @@@ make configurable
  137.  
  138. // -------------------------------------
  139.  
  140. // initialize parameters
  141.  
  142. $q = NULL;                              # search string
  143. $qo = 1;                                # search string option
  144. $h = HITS_DEFAULT;                      # hits number
  145. $ho = 1;                                # hits option
  146. $days = DAYS_DEFAULT;                   # period selection
  147. $global = FALSE;                        # global (site) or this page only
  148. $sites = FALSE;                         # referrers or referring sites
  149. $refdel = NULL;                         # referrer records deleted
  150. $bladd = NULL;                          # blacklist records added
  151.  
  152. // -------------------------------------
  153.  
  154. // initialize internal variables
  155.  
  156. $string_option = SEARCH_LIKE;           # LIKE or NOT LIKE
  157. $hits_option = HITS_MIN_OPTION;         # MIN (>=) or MAX (<=)
  158. $tag = $this->GetPageTag();
  159. $isAdmin = $this->IsAdmin();
  160. $loggedin = ($isAdmin) ? TRUE : (bool)$this->GetUser();
  161. $pre = $this->config['table_prefix'];
  162. $par = '';
  163.  
  164. $query = '';
  165. $rows = 0;
  166.  
  167. // -------------------------------------
  168.  
  169. // User-interface strings
  170.  
  171. define('NAME_GLOBAL',$this->GetConfigValue('wakka_name'));
  172.  
  173. define('TITLE_REFERRERS','External pages linking to %s');
  174. define('TITLE_SITES','Domains linking to %s');
  175.  
  176. define('REPORT_BLACKLIST','Referrer records removed: %d; blacklist records added: %d');
  177.  
  178. define('TOTAL_REFERRERS','Total: %d referrers linking to %s');
  179. define('TOTAL_SITES','Total: %d referrers linking to %s');
  180.  
  181. // current target
  182. # you can use NAME_GLOBAL instead of 'this site' if the site name is short enough
  183. # @@@ JW: choice between 'this site' and NAME_GLOBAL could be set via configuration (later)
  184. define('TARGET_GLOBAL','this site');
  185. define('TARGET_PAGE',$tag);
  186.  
  187. // menus don't use current target but *possible* targets
  188. define('MENU_REFERRERS','Referrers to %s');
  189. define('MENU_SITES','Domains linking to %s');
  190. define('MENU_REFERRERS_PAGE',sprintf(MENU_REFERRERS,TARGET_PAGE));
  191. define('MENU_SITES_PAGE',sprintf(MENU_SITES,TARGET_PAGE));
  192. define('MENU_REFERRERS_GLOBAL',sprintf(MENU_REFERRERS,TARGET_GLOBAL));
  193. define('MENU_SITES_GLOBAL',sprintf(MENU_SITES,TARGET_GLOBAL));
  194. define('MENU_BLACKLIST','Blacklisted sites');
  195.  
  196. define('FORM_LEGEND','Filter view:');
  197. define('FORM_URL_OPT_REFERRERS','URL:');
  198. define('FORM_URL_OPT_SITES','Domain:');
  199. define('FORM_URL_OPT_TITLE','Select search option');
  200. define('FORM_URL_OPT_1','containing');
  201. define('FORM_URL_OPT_0','not containing');
  202. define('FORM_URL_STRING_LABEL','string');
  203. define('FORM_URL_STRING_TITLE','Enter a search string');
  204. define('FORM_HITS_OPT_LABEL','Hits:');
  205. define('FORM_HITS_OPT_TITLE','Select filter option');
  206. define('FORM_HITS_OPT_1','at least');
  207. define('FORM_HITS_OPT_0','no more than');
  208. define('FORM_HITS_NUM_LABEL','hits');
  209. define('FORM_HITS_NUM_TITLE','Enter number of hits');
  210. define('FORM_DAYS_OPT_LABEL','Period:');
  211. define('FORM_DAYS_OPT_TITLE','Select period in days');
  212. define('FORM_DAYS_NUM_LABEL','days');
  213. define('FORM_SUBMIT_URLS','Show referrers');
  214. define('FORM_SUBMIT_SITES','Show referring domains');
  215.  
  216. define('LIST_PERIOD_HOURS',' (last %d hours)');
  217. define('LIST_PERIOD_DAYS',' (last %d days)');
  218. define('LIST_SUMMARY_REFERRERS','Filtered list of referrers, with hits%s, sorted by number of hits');
  219. define('LIST_SUMMARY_SITES','Filtered list of referring sites, with hits%s, sorted by number of hits');
  220. define('LIST_HEAD_HITS','Hits');
  221. define('LIST_HEAD_ACTION','Action');
  222. define('LIST_HEAD_LIST_REFERRERS','Referrers');
  223. define('LIST_HEAD_LIST_SITES','Referring hosts');
  224. define('LIST_REF_UNKNOWN','unknown');           # make sure the *exact* same string is used in the whitelist definition (delete_referrer.php)
  225. define('LIST_ACTION_DESC',' and links to blacklist spammers');
  226. define('LIST_ACTION_BLACKLIST','Blacklist');
  227. define('LIST_ACTION_BLACKLIST_TITLE','Blacklist this domain');
  228.  
  229. define('SPAM_NOTE','Note to spammers: This page is not indexed by search engines, so don\'t waste your time.');
  230. define('LOGIN_NOTE','You need to login to see referring sites.');
  231.  
  232. // show result counts for target
  233. define('LIST_RESULT_COUNTER_REFERRERS','Filtered result: %d referrers linking to %s');  # @@@ does not take account of singular
  234. define('LIST_RESULT_COUNTER_SITES','Filtered result: %d domains linking to %s');        # @@@ does not take account of singular
  235. // show 'no result' summary for target
  236. define('NONE_NOTE_REFERRERS','Filtered result: No referrers found linking to %s');
  237. define('NONE_NOTE_SITES','Filtered result: No domains found linking to %s');
  238.  
  239.  
  240. // -------------------------------------
  241.  
  242. // fetch and validate parameters
  243.  
  244. // get query string and comparison method
  245. if (isset($_POST['q']))
  246. {
  247.     $tq = trim(strip_tags($_POST['q']));
  248.     if ('' != $tq)
  249.     {
  250.         $q = mysql_real_escape_string($tq);
  251.         if (isset($_POST['qo']))
  252.         {
  253.             $qo = ($_POST['qo'] == '1') ? 1 : 0;
  254.             $string_option = ($qo == 1) ? SEARCH_LIKE : SEARCH_UNLIKE;
  255.         }
  256.     }
  257. }
  258. // get hits and min or max criteria
  259. if (isset($_POST['h']))
  260. {
  261.     $h = (is_numeric($_POST['h'])) ? abs((int)$_POST['h']) : HITS_DEFAULT;  # cast to positive integer if numeric
  262. }
  263. if (isset($_POST['ho']))
  264. {
  265.     $ho = ($_POST['ho'] == '1') ? 1 : 0;
  266.     $hits_option = ($ho == 1) ? HITS_MIN_OPTION : HITS_MAX_OPTION;
  267. }
  268. // get period, not longer than purge time
  269. if (isset($_POST['days']))
  270. {
  271.     $days = (is_numeric($_POST['days'])) ? min(abs((int)$_POST['days']),DAYS_MAX) : DAYS_DEFAULT;
  272. }
  273. // get search target: page or site (global)
  274. if (isset($_POST['global']))
  275. {
  276.     $global = (bool)$_POST['global'];
  277. }
  278. elseif (isset($_GET['global']))
  279. {
  280.     $global = (bool)$_GET['global'];
  281. }
  282. $iglobal = (int)$global;
  283. // get precision: URLS (referrers) or referring sites (domains)
  284. if (isset($_POST['sites']))
  285. {
  286.     $sites = (bool)$_POST['sites'];
  287. }
  288. elseif (isset($_GET['sites']))
  289. {
  290.     $sites = (bool)$_GET['sites'];
  291. }
  292. $isites = (int)$sites;
  293. //get reported values (no validation needed, just cast to integer)
  294. if (isset($_GET['refdel']))
  295. {
  296.     $refdel = (int)$_GET['refdel'];
  297.     $bladd  = (isset($_GET['bladd'])) ? $bladd = (int)$_GET['bladd'] : 0;
  298. }
  299.  
  300. // derive parameters for 'current' links
  301. if (1 == $global)
  302. {
  303.     if ('' != $par) $par .= '&amp;';
  304.     $par .= 'global=1';
  305. }
  306. if (1 == $sites)
  307. {
  308.     if ('' != $par) $par .= '&amp;';
  309.     $par .= 'sites=1';
  310. }
  311.  
  312. // -------------------------------------
  313.  
  314. // build query from chunks depending on criteria chosen
  315.  
  316. if ($loggedin)
  317. {
  318.     $query  = 'SELECT referrer';
  319.     if ($sites)
  320.     {
  321.         // add 'host' = domain extracted from referrring URL using this algorithm:
  322.         // find first char after http:// : LOCATE('//',referrer)+2
  323.         // find first / after this: LOCATE('/',referrer,(LOCATE('//',referrer)+2)-1
  324.         // calculate length: (LOCATE('/',referrer,(LOCATE('//',referrer)+2)-1) - (LOCATE('//',referrer)+2)
  325.         // get host (standard): SUBSTRING(referrer FROM (LOCATE('//',referrer)+2) FOR ((LOCATE('/',referrer,(LOCATE('//',referrer)+2)-1) - (LOCATE('//',referrer)+2)))
  326.         // *or*
  327.         // get host (MySQL-specific): SUBSTRING(SUBSTRING_INDEX(referrer,'/',3) FROM (LOCATE('//',referrer)+1))
  328.         $protocol_host = 'SUBSTRING_INDEX(referrer,"/",3)';     # protocol and host: everything before first single /
  329.         $start_host = 'LOCATE("//",referrer)+2';                # start position of host: after //
  330.         $query .= ', SUBSTRING('.$protocol_host.' FROM ('.$start_host.')) AS host';
  331.         // NOTE: COUNT() cannot use a derived column name but it *can* take an expression
  332.         $query .= ', COUNT(SUBSTRING('.$protocol_host.' FROM ('.$start_host.'))) AS num';
  333.         $query .= ' FROM '.$pre.'referrers';
  334.         if (!$global)
  335.         {
  336.             $query .= " WHERE page_tag = '".mysql_real_escape_string($tag)."'";
  337.         }
  338.         if ($days != DAYS_MAX)
  339.         {
  340.             $query .= (!strpos($query,'WHERE')) ? ' WHERE' : ' AND';
  341.             $query .= ' TO_DAYS(NOW()) - TO_DAYS(time) <= '.$days;          # filter by period
  342.         }
  343.         $query .= ' GROUP BY host ';
  344.         if (isset($q))
  345.         {
  346.             $query .= ' HAVING host '.$string_option." '%".$q."%'";         # filter by string (derived column so we use HAVING)
  347.         }
  348.         if ($hits_option != HITS_MIN_OPTION || $h != 1)
  349.         {
  350.             $query .= (!strpos($query,'HAVING')) ? ' HAVING' : ' AND';
  351.             $query .= ' num '.$hits_option.' '.$h;                          # filter by hits number (derived column so we use HAVING)
  352.         }
  353.     }
  354.     else
  355.     {
  356.         $query  = 'SELECT referrer';
  357.         $query .= ', COUNT(referrer) AS num';
  358.         $query .= ' FROM '.$pre.'referrers';
  359.         if (!$global)
  360.         {
  361.             $query .= " WHERE page_tag = '".mysql_real_escape_string($tag)."'";
  362.         }
  363.         if (isset($q))
  364.         {
  365.             $query .= (!strpos($query,'WHERE')) ? ' WHERE' : ' AND';
  366.             $query .= ' referrer '.$string_option." '%".$q."%'";            # filter by string
  367.         }
  368.         if ($days != DAYS_MAX)
  369.         {
  370.             $query .= (!strpos($query,'WHERE')) ? ' WHERE' : ' AND';
  371.             $query .= ' TO_DAYS(NOW()) - TO_DAYS(time) <= '.$days;          # filter by period
  372.         }
  373.         $query .= ' GROUP BY referrer ';
  374.         if ($hits_option != HITS_MIN_OPTION || $h != 1)
  375.         {
  376.             $query .= ' HAVING num '.$hits_option.' '.$h;                   # filter by hits number (derived column so we use HAVING)
  377.         }
  378.     }
  379.     $query .= ' ORDER BY num DESC, referrer ASC';                           # set order
  380.  
  381.     // get total number of referrers (NOT records!)
  382.     $query_refcount  = 'SELECT COUNT(DISTINCT(referrer)) AS total';         # @@@ referrer column should be indexed to make this really efficient
  383.     $query_refcount .= ' FROM '.$pre.'referrers';
  384.     if (!$global)
  385.     {
  386.         $query_refcount .= " WHERE page_tag = '".mysql_real_escape_string($tag)."'";
  387.     }
  388. }
  389.  
  390. // -------------------------------------
  391.  
  392. // execute query (if logged in)
  393.  
  394. // @@@ NOTE: we don't use LoadReferrers any more since the query is now completely dynamically built
  395. if ($loggedin)
  396. {
  397.     // execute query
  398.     $referrers = $this->LoadAll($query);
  399.     $totalrefs = $this->LoadSingle($query_refcount);
  400. }
  401.  
  402. // -------------------------------------
  403.  
  404. // build UI elements
  405.  
  406. // define current target
  407. $target = ($global) ? TARGET_GLOBAL : TARGET_PAGE;
  408.  
  409. // title
  410. $title  = ($sites) ? sprintf(TITLE_SITES,$target) : sprintf(TITLE_REFERRERS,$target);
  411. $title .= ($days <= HOURS_LIMIT) ? sprintf(LIST_PERIOD_HOURS,24*$days) : sprintf(LIST_PERIOD_DAYS,$days);
  412.  
  413. if ($isAdmin)
  414. {
  415.     if (isset($refdel)) $rptblacklisted = sprintf(REPORT_BLACKLIST,$refdel,$bladd);
  416. }
  417.  
  418. if ($loggedin)
  419. {
  420.     // results
  421.     $tot = $totalrefs['total'];
  422.     $total = ($sites) ? sprintf(TOTAL_SITES,$tot,$target) : sprintf(TOTAL_REFERRERS,$tot,$target);
  423.     $creferrers = count($referrers);
  424.     $result = ($sites) ? sprintf(LIST_RESULT_COUNTER_SITES,$creferrers,$target) : sprintf(LIST_RESULT_COUNTER_REFERRERS,$creferrers,$target);
  425.  
  426.     // menu elements: prevent wrapping within element (these *don't* use current target!
  427.     $menu_referrers_page    = str_replace(' ','&nbsp;',MENU_REFERRERS_PAGE);
  428.     $menu_sites_page        = str_replace(' ','&nbsp;',MENU_SITES_PAGE);
  429.     $menu_referrers_global  = str_replace(' ','&nbsp;',MENU_REFERRERS_GLOBAL);
  430.     $menu_sites_global      = str_replace(' ','&nbsp;',MENU_SITES_GLOBAL);
  431.     $menu_blacklist         = str_replace(' ','&nbsp;',MENU_BLACKLIST);
  432.  
  433.     // menu
  434.     if ($global)
  435.     {
  436.         $m_referrers_page = '<a href="'.$this->Href('referrers').'">'.$menu_referrers_page.'</a>';
  437.         $m_sites_page ='<a href="'.$this->Href('referrers','','sites=1').'">'.$menu_sites_page.'</a>';
  438.         $m_referrers_global = ($sites) ? '<a href="'.$this->Href('referrers','','global=1').'">'.$menu_referrers_global.'</a>' : $menu_referrers_global;
  439.         $m_sites_global = ($sites) ? $menu_sites_global : '<a href="'.$this->Href('referrers','','global=1&sites=1').'">'.$menu_sites_global.'</a>';
  440.     }
  441.     else
  442.     {
  443.         $m_referrers_page = ($sites) ? '<a href="'.$this->Href('referrers').'">'.$menu_referrers_page.'</a>' : $menu_referrers_page;
  444.         $m_sites_page = ($sites) ? $menu_sites_page : '<a href="'.$this->Href('referrers','','sites=1').'">'.$menu_sites_page.'</a>';
  445.         $m_referrers_global = '<a href="'.$this->Href('referrers','','global=1').'">'.$menu_referrers_global.'</a>';
  446.         $m_sites_global = '<a href="'.$this->Href('referrers','','global=1&sites=1').'">'.$menu_sites_global.'</a>';
  447.     }
  448.     $m_blacklist = '<a href="'.$this->Href('review_blacklist').'">'.$menu_blacklist.'</a>';
  449.     $menu  = '<ul class="menu">'."\n";
  450.     $menu .= '<li>'.$m_referrers_page.'</li>';
  451.     $menu .= '<li>'.$m_sites_page.'</li>';
  452.     $menu .= '<li>'.$m_referrers_global.'</li>';
  453.     $menu .= '<li>'.$m_sites_global.'</li>';
  454.     $menu .= '<li>'.$m_blacklist.'</li>';
  455.     $menu .= "\n".'</ul>'."\n";
  456.  
  457.     // days dropdown content
  458.     $daysopts = optionRanges($days_limits,DAYS_MAX);
  459.  
  460.     // form
  461.     $form  = $this->FormOpen('referrers','','POST');        # @@@ add parameter for id
  462.     $form .= '<fieldset class="hidden">'."\n";
  463.     $form .= '<input type="hidden" name="global" value="'.$iglobal.'" />'."\n";
  464.     $form .= '<input type="hidden" name="sites" value="'.$isites.'" />'."\n";
  465.     $form .= '</fieldset>'."\n";
  466.     $form .= '<fieldset>'."\n";
  467.     $form .= '<legend>'.FORM_LEGEND.'</legend>'."\n";
  468.  
  469.     $form .= '<label for="qo" class="mainlabel">'.(($sites) ? FORM_URL_OPT_SITES : FORM_URL_OPT_REFERRERS).'</label> '."\n";
  470.     $form .= '<select name="qo" id="qo" title="'.FORM_URL_OPT_TITLE.'">'."\n";
  471.     $form .= '<option value="1"'.(($qo == '1')? ' selected="selected"' : '').'>'.FORM_URL_OPT_1.'</option>'."\n";
  472.     $form .= '<option value="0"'.(($qo == '0')? ' selected="selected"' : '').'>'.FORM_URL_OPT_0.'</option>'."\n";
  473.     $form .= '</select> '."\n";
  474.     $form .= '<label for="q">'.FORM_URL_STRING_LABEL.'</label> '."\n";
  475.     $form .= '<input type ="text" name="q" id="q" title="'.FORM_URL_STRING_TITLE.'" size="10" maxlength="50" value="'.$q.'" />';
  476.  
  477.     $form .= '<br />'."\n";
  478.  
  479.     $form .= '<label for="ho" class="mainlabel">'.FORM_HITS_OPT_LABEL.'</label> '."\n";
  480.     $form .= '<select name="ho" id="ho" title="'.FORM_HITS_OPT_TITLE.'">'."\n";
  481.     $form .= '<option value="1"'.(($ho == '1')? ' selected="selected"' : '').'>'.FORM_HITS_OPT_1.'</option>'."\n";
  482.     $form .= '<option value="0"'.(($ho == '0')? ' selected="selected"' : '').'>'.FORM_HITS_OPT_0.'</option>'."\n";
  483.     $form .= '</select> '."\n";
  484.     $form .= '<input type ="text" name="h" id="h" title="'.FORM_HITS_NUM_TITLE.'" size="5" maxlength="5" value="'.$h.'" />'."\n";
  485.     $form .= ' <label for="h">'.FORM_HITS_NUM_LABEL.'</label>';
  486.  
  487.     $form .= '<br />'."\n";
  488.  
  489.     $form .= '<label for="days" class="mainlabel">'.FORM_DAYS_OPT_LABEL.'</label> '."\n";
  490.     $form .= '<select name="days" id="days" title="'.FORM_DAYS_OPT_TITLE.'">'."\n";
  491.     // build drop-down
  492.     foreach ($daysopts as $opt)
  493.     {
  494.         $selected = ($opt == $days) ? ' selected="selected"' : '';
  495.         $form .= '<option value="'.$opt.'"'.$selected.'>'.$opt.'</option>';
  496.     }
  497.     $form .= '</select> '."\n";
  498.     $form .= ' <label for="h">'.FORM_DAYS_NUM_LABEL.'</label>'."\n";
  499.  
  500.     $form .= '</fieldset>'."\n";
  501.  
  502.     $form .= '<input type="submit" value="'.(($sites) ? FORM_SUBMIT_SITES : FORM_SUBMIT_URLS).'" accesskey="r" />'."\n";
  503.     $form .= $this->FormClose();
  504.  
  505.     // referrers list with admin link for blacklisting
  506.     if ($sites)
  507.     {
  508.         $summary  = ($isAdmin) ? sprintf(LIST_SUMMARY_SITES,LIST_ACTION_DESC) : sprintf(LIST_SUMMARY_SITES,'');
  509.         $refshead = LIST_HEAD_LIST_SITES;
  510.     }
  511.     else
  512.     {
  513.         $summary  = ($isAdmin) ? sprintf(LIST_SUMMARY_REFERRERS,LIST_ACTION_DESC) : sprintf(LIST_SUMMARY_REFERRERS,'');
  514.         $refshead = LIST_HEAD_LIST_REFERRERS;
  515.     }
  516.     if ($isAdmin)
  517.     {
  518.         $redir = ($global||$sites) ? $this->GetMethod().'&amp;'.$par : $this->GetMethod();  # ensure we return to the same view
  519.         $par = ($sites) ? 'spam_site' : 'spam_link';
  520.         $blacklisturl = $this->Href('delete_referrer','',$par.'=').'%s&amp;redirect=%s';
  521.         $blacklink = '<a href="'.$blacklisturl.'" title="'.LIST_ACTION_BLACKLIST_TITLE.'">'.LIST_ACTION_BLACKLIST.'</a>';
  522.     }
  523. }
  524.  
  525. // -------------------------------------
  526.  
  527. // show user interface (pre-template)
  528.  
  529. echo '<div class="page">'."\n";
  530. echo '<h3>'.$title.'</h3>'."\n";
  531. echo '<p><em>'.SPAM_NOTE.'</em></p>'."\n";
  532. echo '<p class="notes">Doesn\'t look right with your skin? See <a href="http://wikka.jsnx.com/AdvancedReferrersHandler">AdvancedReferrersHandler</a>.<!--temporary for site only-->'."\n";
  533. # debug
  534. if (DEBUG)
  535. {
  536.     echo 'Query (ref): '.$query.'<br />';
  537.     echo 'Query (sites): '.$query_sites.'<br />';
  538.     echo ($global) ? 'Global: TRUE<br />' : 'Global: FALSE<br />';
  539.     echo ($sites)  ? 'Sites: TRUE<br />' : 'Sites: FALSE<br />';
  540. }
  541. # debug
  542.  
  543. if ($loggedin)
  544. {
  545.     if ($isAdmin && isset($refdel)) echo '<p class="notes">'.$rptblacklisted.'</p>';
  546.     echo '<div class="refmenu">'.$menu.'</div><br class="clear" />'."\n";
  547.     echo '<h4>'.$total.'</h4>'."\n";
  548.     echo '<div id="refform">'.$form.'</div>'."\n";  # @@@ kluge until FormOpen() is adapted: id should actually be on form itself and div not necessary!
  549.  
  550.     if ($creferrers != 0)
  551.     {
  552.         echo '<h4>'.$result.'</h4>'."\n";
  553.         echo '<table id="reflist" summary="'.$summary.'">'."\n";
  554.         echo '<thead>';
  555.         echo '<tr><th class="hits" scope="col">'.LIST_HEAD_HITS.'</th>';
  556.         if ($isAdmin) echo '<th class="action" scope="col">'.LIST_HEAD_ACTION.'</th>';
  557.         echo '<th class="refs" scope="col">'.$refshead.'</th></tr>'."\n";
  558.         echo '</thead>'."\n";
  559.         echo '<tbody>'."\n";
  560.  
  561.         foreach ($referrers as $referrer)
  562.         {
  563.             $hits   = $referrer['num'];
  564.             if ($sites)
  565.             {
  566.                 $ref    = $this->htmlspecialchars_ent($referrer['host']);
  567.             }
  568.             else
  569.             {
  570.                 $ref    = $this->htmlspecialchars_ent($referrer['referrer']);
  571.             }
  572.             echo '<tr>';
  573.             echo '<td class="hits">'.$hits.'</td>';
  574.             if ($isAdmin) echo '<td class="action"><span class="keys">'.sprintf($blacklink,$ref,$redir).'</span></td>';
  575.             if ($sites)
  576.             {
  577.                 echo '<td class="refs">'.$ref.'</td>';
  578.             }
  579.             else
  580.             {
  581.                 echo '<td class="refs"><a href="'.$ref.'">'.$ref.'</a></td>';
  582.             }
  583.             echo '</tr>'."\n";
  584.         }
  585.  
  586.         echo '</tbody>'."\n";
  587.         echo '</table>'."\n";
  588.     }
  589.     else
  590.     {
  591.         echo '<p><em>'.(($sites) ? sprintf(NONE_NOTE_SITES,$target) : sprintf(NONE_NOTE_REFERRERS,$target)).'</em></p>'."\n";
  592.     }
  593.  
  594.     echo '<div class="refmenu">'.$menu.'</div><br class="clear" />'."\n";
  595. }
  596. else
  597. {
  598.     echo '<p><strong>'.LOGIN_NOTE.'</strong></p>'."\n";
  599. }
  600. echo '</div>'."\n";
  601. ?>


4. handlers/page/review_blacklist.php


This is rewritten mainly to make it integrate seamlessly with the referrers handler. There was also a problem with the output which was not valid XHTML; it now follows the same pattern as the referrers handler and got the same treatment for preparation for internationalization as well.

  1. <?php
  2. /**
  3.  * Display, filter and search a list of blacklisted domains.
  4.  *
  5.  * Usage: append /review_blacklist to the URL of the page
  6.  *
  7.  * This handler allows logged-in users to display and search the blacklist; an admin may
  8.  * remove blacklisted domains from the database.
  9.  *
  10.  * @package     Handlers
  11.  * @subpackage  DatabaseHandlers
  12.  * @name        Referrers
  13.  *
  14.  * @author      {@link http://wikka.jsnx.com/JavaWoman JavaWoman} - code cleanup, search/filter functionality added, valid XHTML, accessibility
  15.  * @version     0.8
  16.  * @since       Wikka 1.1.6.X
  17.  *
  18.  * @todo        for 1.0:
  19.  *              - clean up debug code
  20.  *              - configurable choice hostname (NAME_GLOBAL) or 'this site' (config, installer)
  21.  *              - make index on the spammer column in the referrer_blacklist table _unique_ (installer) and remove extra query
  22.  *              later:
  23.  *              - (global) icons to represent each of the five views, small and larger versions (menu/page)
  24.  *              - adapt FormOpen() to accept id; then fix form kluge here and in stylesheet
  25.  *              - adapt text definitions to take singular-plural into account
  26.  *              - add paging
  27.  *              - turn list into form with checkboxes to allow mass removing
  28.  *
  29.  * @input       string  $q  optional: string used to filter the referrers;
  30.  *              default: 'NULL;
  31.  *              the default can be overridden by providing a POST parameter 'q'
  32.  * @input       integer $qo optional: determines the kind of search to be performed for string $q:
  33.  *              1: search for all referrers containing a given string
  34.  *              0: search for all referrers not containing a given string
  35.  *              default: 1;
  36.  *              the default can be overridden by providing a POST parameter 'qo'
  37.  * @input       string  $remove  optional: GET parameter - domain to be removed from the blacklist
  38.  *              default: NULL;
  39.  */
  40.  
  41. // constants
  42.  
  43. define('DEBUG',FALSE);      # @@@ set TRUE to generate debugging output
  44.  
  45. define('SEARCH_LIKE','LIKE');           # search string operator
  46. define('SEARCH_UNLIKE','NOT LIKE');     # search string operator
  47.  
  48. // -------------------------------------
  49.  
  50. // initialize parameters
  51.  
  52. $q = NULL;                              # search string
  53. $qo = 1;                                # search string option
  54. $remove = NULL;                         # domain to be removed from the blacklist
  55.  
  56. // -------------------------------------
  57.  
  58. // initialize internal variables
  59.  
  60. $string_option = SEARCH_LIKE;           # LIKE or NOT LIKE
  61. $tag = $this->GetPageTag();
  62. $isAdmin = $this->IsAdmin();
  63. $loggedin = ($isAdmin) ? TRUE : (bool)$this->GetUser();
  64. $pre = $this->config['table_prefix'];
  65.  
  66. $queryd = '';
  67. $querys = '';
  68. $rows = 0;
  69.  
  70. // -------------------------------------
  71.  
  72. // User-interface strings
  73.  
  74. define('TITLE','Blacklisted domains');
  75.  
  76. define('REPORT_REMOVED','Removed: %d records');                         # @@@ does not take account of singular
  77.  
  78. define('TOTAL_BL','Total: %d blacklisted domain');
  79.  
  80. // current target
  81. # you can use NAME_GLOBAL instead of 'this site' if the site name is short enough
  82. # @@@ JW: choice between 'this site' and NAME_GLOBAL could be set via configuration (later)
  83. define('TARGET_GLOBAL','this site');
  84. define('TARGET_PAGE',$tag);
  85.  
  86. // menus don't use current target but *possible* targets
  87. define('MENU_REFERRERS','Referrers to %s');
  88. define('MENU_SITES','Domains linking to %s');
  89. define('MENU_REFERRERS_PAGE',sprintf(MENU_REFERRERS,TARGET_PAGE));
  90. define('MENU_SITES_PAGE',sprintf(MENU_SITES,TARGET_PAGE));
  91. define('MENU_REFERRERS_GLOBAL',sprintf(MENU_REFERRERS,TARGET_GLOBAL));
  92. define('MENU_SITES_GLOBAL',sprintf(MENU_SITES,TARGET_GLOBAL));
  93. define('MENU_BLACKLIST','Blacklisted sites');
  94.  
  95. define('FORM_LEGEND','Filter view:');
  96. define('FORM_URL_OPT_LABEL','Domain:');
  97. define('FORM_URL_OPT_TITLE','Select search option');
  98. define('FORM_URL_OPT_1','containing');
  99. define('FORM_URL_OPT_0','not containing');
  100. define('FORM_URL_STRING_LABEL','string');
  101. define('FORM_URL_STRING_TITLE','Enter a search string');
  102. define('FORM_SUBMIT_BLACKLIST','Show blacklisted domains');
  103.  
  104. define('LIST_SUMMARY_BL','Filtered list of blacklisted domains%s, sorted alphabetically');
  105. define('LIST_HEAD_ACTION','Action');
  106. define('LIST_HEAD_BL','Blacklisted domains');
  107. define('LIST_ACTION_DESC',' and links to remove domains from the blacklist');
  108. define('LIST_ACTION_BL','Remove');
  109. define('LIST_ACTION_BL_TITLE','Remove this domain from the blacklist');
  110.  
  111. define('LOGIN_NOTE','You need to login to see blacklisted domains.');
  112.  
  113. define('LIST_RESULT_COUNTER_SITES','Filtered result: %d domains');              # @@@ does not take account of singular
  114. define('NONE_NOTE','Filtered result: No blacklisted domains found');
  115.  
  116. // -------------------------------------
  117.  
  118. // fetch and validate parameters
  119.  
  120. // get query string and comparison method
  121. if (isset($_POST['q']))
  122. {
  123.     $tq = trim(strip_tags($_POST['q']));
  124.     if ('' != $tq)
  125.     {
  126.         $q = mysql_real_escape_string($tq);
  127.         if (isset($_POST['qo']))
  128.         {
  129.             $qo = ($_POST['qo'] == '1') ? 1 : 0;
  130.             $string_option = ($qo == 1) ? SEARCH_LIKE : SEARCH_UNLIKE;
  131.         }
  132.     }
  133. }
  134. // get host(s) to be removed
  135. if (isset($_GET['remove']))
  136. {
  137.     $remove = mysql_real_escape_string(strip_tags($_GET['remove']));
  138. }
  139.  
  140. // -------------------------------------
  141.  
  142. // build remove query
  143.  
  144. if ($isAdmin)
  145. {
  146.     $queryd = 'DELETE FROM '.$pre.'referrer_blacklist'
  147.             . ' WHERE spammer = "'.$remove.'"';
  148. }
  149.  
  150. // build filter query
  151.  
  152. if ($loggedin)
  153. {
  154.     $querys = 'SELECT * FROM '.$pre.'referrer_blacklist';
  155.     if (isset($q))
  156.     {
  157.         $querys .= ' WHERE spammer '.$string_option." '%".$q."%'";  # filter by string
  158.     }
  159.     $querys .= ' ORDER BY spammer ASC';                             # set order
  160.  
  161.     // get total number of domains in blacklist
  162.     $query_refcount  = 'SELECT COUNT(spammer) AS total';
  163.     $query_refcount .= ' FROM '.$pre.'referrer_blacklist';
  164. }
  165.  
  166. // -------------------------------------
  167.  
  168. // execute query (if logged in)
  169.  
  170. // do a 'remove' query first, then follow with the select query:
  171. // the list should then reflect the situation after removal of a domain
  172. if ($loggedin)
  173. {
  174.     if ($isAdmin && isset($remove))
  175.     {
  176.         $rc = $this->Query($queryd);                                # TRUE on success
  177.         $numbldeleted = mysql_affected_rows();                      # @@@ report back as GET parameter (in $removeurl/$removelink!)
  178.     }
  179.     $blacklist = $this->LoadAll($querys);
  180.     $totalrefs = $this->LoadSingle($query_refcount);
  181. }
  182.  
  183. // -------------------------------------
  184.  
  185. // build UI elements
  186.  
  187. // title
  188. $title = TITLE;
  189.  
  190. if ($isAdmin)
  191. {
  192.     if (isset($numbldeleted)) $rptremoved = sprintf(REPORT_REMOVED,$numbldeleted);
  193.  
  194.     $removeurl = $this->Href('review_blacklist','','remove=').'%s';
  195.     $removelink = '<a href="'.$removeurl.'" title="'.LIST_ACTION_BL_TITLE.'">'.LIST_ACTION_BL.'</a>';
  196. }
  197.  
  198. if ($loggedin)
  199. {
  200.     // results
  201.     $tot = $totalrefs['total'];
  202.     $total = sprintf(TOTAL_BL,$tot);
  203.     $cdomains = count($blacklist);
  204.     $result = sprintf(LIST_RESULT_COUNTER_SITES,$cdomains);
  205.  
  206.     // menu elements: prevent wrapping within element (these *don't* use current target!
  207.     $menu_referrers_page    = str_replace(' ','&nbsp;',MENU_REFERRERS_PAGE);
  208.     $menu_sites_page        = str_replace(' ','&nbsp;',MENU_SITES_PAGE);
  209.     $menu_referrers_global  = str_replace(' ','&nbsp;',MENU_REFERRERS_GLOBAL);
  210.     $menu_sites_global      = str_replace(' ','&nbsp;',MENU_SITES_GLOBAL);
  211.     $menu_blacklist         = str_replace(' ','&nbsp;',MENU_BLACKLIST);
  212.  
  213.     // menu
  214.     $m_referrers_page = '<a href="'.$this->Href('referrers').'">'.$menu_referrers_page.'</a>';
  215.     $m_sites_page ='<a href="'.$this->Href('referrers','','sites=1').'">'.$menu_sites_page.'</a>';
  216.     $m_referrers_global = '<a href="'.$this->Href('referrers','','global=1').'">'.$menu_referrers_global.'</a>';
  217.     $m_sites_global = '<a href="'.$this->Href('referrers','','global=1&sites=1').'">'.$menu_sites_global.'</a>';
  218.     $m_blacklist = $menu_blacklist;
  219.     $menu  = '<ul class="menu">'."\n";
  220.     $menu .= '<li>'.$m_referrers_page.'</li>';
  221.     $menu .= '<li>'.$m_sites_page.'</li>';
  222.     $menu .= '<li>'.$m_referrers_global.'</li>';
  223.     $menu .= '<li>'.$m_sites_global.'</li>';
  224.     $menu .= '<li>'.$m_blacklist.'</li>';
  225.     $menu .= "\n".'</ul>'."\n";
  226.  
  227.     // form
  228.     $form  = $this->FormOpen('review_blacklist','','POST');     # @@@ add parameter for id
  229.     $form .= '<fieldset>'."\n";
  230.     $form .= '<legend>'.FORM_LEGEND.'</legend>'."\n";
  231.  
  232.     $form .= '<label for="qo" class="mainlabel">'.FORM_URL_OPT_LABEL.'</label> '."\n";
  233.     $form .= '<select name="qo" id="qo" title="'.FORM_URL_OPT_TITLE.'">'."\n";
  234.     $form .= '<option value="1"'.(($qo == '1')? ' selected="selected"' : '').'>'.FORM_URL_OPT_1.'</option>'."\n";
  235.     $form .= '<option value="0"'.(($qo == '0')? ' selected="selected"' : '').'>'.FORM_URL_OPT_0.'</option>'."\n";
  236.     $form .= '</select> '."\n";
  237.     $form .= '<label for="q">'.FORM_URL_STRING_LABEL.'</label> '."\n";
  238.     $form .= '<input type ="text" name="q" id="q" title="'.FORM_URL_STRING_TITLE.'" size="10" maxlength="50" value="'.$q.'" />';
  239.  
  240.     $form .= '</fieldset>'."\n";
  241.  
  242.     $form .= '<input type="submit" value="'.FORM_SUBMIT_BLACKLIST.'" accesskey="b" />'."\n";
  243.     $form .= $this->FormClose();
  244.  
  245.     // blacklist with admin link for removing
  246.     $summary  = ($isAdmin) ? sprintf(LIST_SUMMARY_BL,LIST_ACTION_DESC) : sprintf(LIST_SUMMARY_BL,'');
  247.     $refshead = LIST_HEAD_BL;
  248. }
  249.  
  250. // -------------------------------------
  251.  
  252. // show user interface (pre-template)
  253.  
  254. echo '<div class="page">'."\n";
  255. echo '<h3>'.$title.'</h3>'."\n";
  256. # debug
  257. if (DEBUG)
  258. {
  259.     echo 'Query remove: '.$queryd.'<br />';
  260.     echo 'Query blacklist: '.$querys.'<br />';
  261.     echo 'remove: '.$remove.'<br/>';
  262.     echo 'removed: '.$numbldeleted.'<br/>';
  263. }
  264. # debug
  265. if ($loggedin)
  266. {
  267.     if ($isAdmin && isset($numbldeleted)) echo '<p class="notes">'.$rptremoved.'</p>';
  268.     echo '<div class="refmenu">'.$menu.'</div><br class="clear" />'."\n";
  269.     echo '<h4>'.$total.'</h4>'."\n";
  270.     echo '<div id="refform">'.$form.'</div>'."\n";  # @@@ kluge until FormOpen() is adapted: id should actually be on form itself and div not necessary!
  271.  
  272.     if ($cdomains != 0)
  273.     {
  274.         echo '<h4>'.$result.'</h4>'."\n";
  275.         echo '<table id="reflist" summary="'.$summary.'">'."\n";
  276.         echo '<thead>';
  277.         if ($isAdmin) echo '<th class="action" scope="col">'.LIST_HEAD_ACTION.'</th>';
  278.         echo '<th class="refs" scope="col">'.$refshead.'</th></tr>'."\n";
  279.         echo '</thead>'."\n";
  280.         echo '<tbody>'."\n";
  281.         foreach ($blacklist as $spammer)
  282.         {
  283.             $ref    = $this->htmlspecialchars_ent($spammer['spammer']);
  284.             echo '<tr>';
  285.             if ($isAdmin) echo '<td class="action"><span class="keys">'.sprintf($removelink,$ref).'</span></td>';
  286.             echo '<td class="refs">'.$ref.'</td>';
  287.             echo '</tr>'."\n";
  288.         }
  289.         echo '</tbody>'."\n";
  290.         echo '</table>'."\n";
  291.     }
  292.     else
  293.     {
  294.         echo '<p><em>'.NONE_NOTE.'</em></p>'."\n";
  295.     }
  296.  
  297.     echo '<div class="refmenu">'.$menu.'</div><br class="clear" />'."\n";
  298. }
  299. else
  300. {
  301.     echo '<p><strong>'.LOGIN_NOTE.'</strong></p>'."\n";
  302. }
  303. echo '</div>'."\n";
  304. ?>


5. handlers/page/delete_referrer.php


Two problems here were solved: the code was not actually secure (anyone knowing how to build a URL could blacklist a domain), and when the action was completed you would get back to a single view only - often not the view you were coming form, causing an extra click to get back.

Also there is now a list of domains that are "whitelisted" so they will never be blacklisted. Basically this is for local machines, but you could add your own domains here as well. We'll make this list configurable.

For further details see the code (there's stil quite a lot of debug code which will disappear):

  1. <?php
  2. /**
  3.  * Remove specified URL or referrer domain from the referrer list and add it to the blacklist.
  4.  *
  5.  * Usage: supposed to be used only from link or form produced by the referrers handler.
  6.  *
  7.  * Security:    - can be executed only by an admin (redirect to homepage otherwise)
  8.  *              - redirect to homepage if any parameter is missing or incorrect
  9.  *              - explicitly use GET or POST to retrieve parameters
  10.  *
  11.  * @package     Handlers
  12.  * @subpackage  DatabaseHandlers
  13.  * @name        DeleteReferrer
  14.  *
  15.  * @author      {@link http://wikka.jsnx.com/JavaWoman JavaWoman} - code cleanup, security, (integration with referrers)
  16.  * @version     0.8
  17.  * @since       Wikka 1.1.6.X
  18.  *
  19.  * @todo        for 1.0:
  20.  *              - clean up debug code
  21.  *              - configurable 'whitelist' of hosts that should never be blacklisted (config, installer)
  22.  *              later:
  23.  *              - transfer filter parameters as well so we cen redirect to the exact view we came from
  24.  *              - change to fetching POST parameters when we convert to using form(s))
  25.  *
  26.  * @input       string  $spam_link  required: spammer URL or domain to blacklist.
  27.  * @input       string  $redirect   required: handler for current page to redirect to.
  28.  * @input       int     $global     optional: query parameter for redirecting to the original view; default: 0
  29.  * @input       int     $sites      optional: query parameter for redirecting to the original view; default: 0
  30.  */
  31.  
  32. // constants
  33.  
  34. define('DEBUG',FALSE);      # @@@ set TRUE to generate debugging output
  35.  
  36. define('LIST_REF_UNKNOWN','unknown');                   # make sure this is *exactly* same string as used in referrers.php
  37.  
  38. $whitelist = array(LIST_REF_UNKNOWN,'localhost','127.0.0.1');   # @@@ make this configurable via wikka.config.php
  39.  
  40. // -------------------------------------
  41.  
  42. // initialize parameters
  43.  
  44. $spam_link = NULL;          # site to blacklist from referrers list
  45. $spam_site = NULL;          # domain to blacklist from sites (domains) list
  46. $redirect = NULL;           # handler / query string of referring page to redirect to
  47. $global = 0;                # extra parameter for redirect
  48. $sites = 0;                 # extra parameter for redirect
  49.  
  50. // -------------------------------------
  51.  
  52. // initialize internal variables
  53.  
  54. $isAdmin = $this->IsAdmin();
  55. $home = $this->Href('',$this->config['root_page']);
  56. $pre = $this->config['table_prefix'];
  57. $par = '';
  58.  
  59. // -------------------------------------
  60.  
  61. // User-interface strings
  62.  
  63. define('MSG_NOT_ALLOWED','Blacklisting not allowed');
  64. define('MSG_PAR_ERROR','Cannot blacklist: missing or incorrect parameter');
  65.  
  66. // -------------------------------------
  67.  
  68. // check permission and immediately redirect to home page if check fails
  69.  
  70. if (!$isAdmin) $this->Redirect($home,MSG_NOT_ALLOWED);
  71.  
  72. // -------------------------------------
  73.  
  74. // fetch and validate parameters
  75.  
  76. // ensure we have a spam_link OR spam_site parameter
  77. if(isset($_GET['spam_link']))
  78. {
  79.     $spam_link = strip_tags($_GET['spam_link']);            # blacklisting from referrers list
  80. }
  81. elseif (isset($_GET['spam_site']))
  82. {
  83.     $spam_site = strip_tags($_GET['spam_site']);            # blacklisting from sites list
  84. }
  85. // ensure we have a redirect parameter 'referrers' (we won't allow any other value)
  86. if (isset($_GET['redirect']))
  87. {
  88.     $redirect = preg_match('/^referrers$/',$_GET['redirect']) ? strip_tags($_GET['redirect']) : NULL;
  89. }
  90. if (isset($_GET['global']))
  91. {
  92.     $global = abs((int)$_GET['global']);                    # make sure we have a positive integer
  93. }
  94. if (isset($_GET['sites']))
  95. {
  96.     $sites = abs((int)$_GET['sites']);                      # make sure we have a positive integer
  97. }
  98. # debug
  99. if (DEBUG)
  100. {
  101.     echo 'spamlink: '.$spam_link.'<br/>';
  102.     echo 'spamsite: '.$spam_site.'<br/>';
  103. }
  104. # end debug
  105.  
  106. // check required parameters and immediately redirect to home page if check fails
  107.  
  108. if (!(isset($spam_link) || (isset($spam_site))) || !isset($redirect)) $this->Redirect($home,MSG_PAR_ERROR);
  109.  
  110. // -------------------------------------
  111.  
  112. // derive internal variables
  113.  
  114. // With $spam_link we get a full URL and need to parse out the host name;
  115. // with $spam_site we get a domain: no need to parse anything;
  116. // for both: check against whitelist before acting on it
  117. if (isset($spam_site))
  118. {
  119.     // referring domain already is host name (no need to parse)
  120.     $domain = $spam_site;
  121. }
  122. else
  123. {
  124.     $parsed_url = parse_url($spam_link);
  125.     if (FALSE !== $parsed_url)
  126.     {
  127.         // derive host name from referring URL
  128.         if (isset($parsed_url['host']))
  129.         {
  130.             $domain = $parsed_url['host'];
  131.         }
  132.     }
  133. }
  134. // exclude 'unknown', 'localhost' and others in the "whitelist"
  135. if (!in_array($domain,$whitelist))
  136. {
  137.     $spammer = $domain;
  138. }
  139. # debug
  140. if (DEBUG)
  141. {
  142.     echo 'domain: '.$domain.'<br/>';
  143.     echo 'spammer: '.$spammer.'<br/>';
  144.     #exit;
  145. }
  146. # end debug
  147.  
  148. // prepare extra parameters for redirect
  149. if (1 == $global)
  150. {
  151.     if ('' != $par) $par .= '&';
  152.     $par .= 'global=1';
  153. }
  154. if (1 == $sites)
  155. {
  156.     if ('' != $par) $par .= '&';
  157.     $par .= 'sites=1';
  158. }
  159.  
  160. // -------------------------------------
  161.  
  162. // do the blacklisting
  163.  
  164. if (isset($spammer)) {
  165.     // if $spammer = 'wakka' $queryd should remove http://wakka...
  166.     // but NOT http://example.com/wakka from the referrers table
  167.     #$hspammer = mysql_real_escape_string('//'.$spammer.'/');   # string to recognize host in referrers table
  168.     $hspammer = mysql_real_escape_string('//'.$spammer);    # string to recognize host in referrers table (trailing / removed: some referrers don't have one
  169.     $spammer  = mysql_real_escape_string($spammer);             # string to use for spammer in referrer_blacklist table
  170.  
  171.     $queryd = 'DELETE FROM '.$pre.'referrers'
  172.             . ' WHERE referrer LIKE "%'.$hspammer.'%"';
  173.     // check if domain is already blacklisted (must start with $spammer)
  174.     # @@@ JW: should not be necessary if we'd have a _unique_ index on spammer! (let the database do the work)
  175.     $querys = 'SELECT spammer FROM '.$pre.'referrer_blacklist'
  176.             . ' WHERE spammer like "'.$spammer.'%"';
  177.     // add domain to blacklist
  178.     $queryi = 'INSERT INTO '.$pre.'referrer_blacklist'
  179.             . ' SET spammer = "'.$spammer.'"';
  180. # debug
  181. if (DEBUG)
  182. {
  183.     echo 'delete referrers: '.$queryd.'<br/>';
  184.     echo 'check blacklist : '.$querys.'<br/>';
  185.     echo 'blacklist domain: '.$queryi.'<br/>';
  186.     $querye = str_replace('DELETE','EXPLAIN SELECT *',$queryd);
  187.     $explain = $this->LoadAll($querye);
  188.     echo 'Explain:<pre>';
  189.     print_r($explain);
  190.     echo '</pre>';
  191.     $queryes = str_replace('DELETE','SELECT *',$queryd);
  192.     $todelete = $this->LoadAll($queryes);
  193.     echo 'To delete:<pre>';
  194.     print_r($todelete);
  195.     echo '</pre>';
  196.     #exit;
  197. }
  198. # end debug
  199.  
  200.     $rcd = $this->Query($queryd);                           # TRUE on success
  201.     $numrefdeleted = mysql_affected_rows();                 # @@@ report back as GET parameter (in $par)
  202.     if ($rcd) $rcs = $this->LoadSingle($querys);            # row (array) if spammer already blacklisted
  203.     if (!is_array($rcs)) $rci = $this->Query($queryi);      # TRUE on success
  204.     $numblacklisted = mysql_affected_rows();                # @@@ report back as GET parameter (in $par)
  205.  
  206.     // if referrers were deleted, report both deleted referrers and added blacklist records
  207.     if (isset($numrefdeleted))
  208.     {
  209.         if ('' != $par) $par .= '&';
  210.         $par .= 'refdel='.$numrefdeleted;
  211.         $par .= '&bladd=';
  212.         $par .= (isset($numblacklisted)) ? $numblacklisted : 0;
  213.     }
  214. # debug
  215. if (DEBUG)
  216. {
  217.     echo 'referrers deleted: '.$numrefdeleted.'<br/>';
  218.     echo 'blacklisted: '.$numblacklisted.'<br/>';
  219.     echo 'par: '.$par.'<br/>';
  220. }
  221. # end debug
  222. }
  223.  
  224. // redirect to current page & handler, adding any extra parameters to get back to the original view
  225. # debug
  226. if (DEBUG)
  227. {
  228.     // display link instead of doing redirect so debug output can be seen
  229.     echo '<a href="'.$this->Href($redirect,'',str_replace('&','&amp;',$par)).'">Back</a>';
  230.     exit;
  231. }
  232. # end debug
  233. $this->Redirect($this->Href($redirect,'',$par));
  234. ?>


6. actions/header.php


We have created an extension of the stylesheet to style the user-interface elements for these handlers; to avoid (most) problems with all the custom "skins" people are using on this site (and maybe yours as well?), this is kept in a separate file (for now) so most of the new styles will become available. Therefore the extra stylesheet file should be linked into the header template before the general display stylesheet:

Existing actions/header.php:
  1.     <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
  2.     <meta name="keywords" content="<?php echo $this->GetConfigValue("meta_keywords") ?>" />
  3.     <meta name="description" content="<?php echo $this->GetConfigValue("meta_description") ?>" />
  4.     <link rel="stylesheet" type="text/css" href="css/<?php echo ($this->GetCookie("wikiskin"))? $this->GetCookie("wikiskin"): $this->GetConfigValue("stylesheet") ?>" media="screen" />
  5.     <link rel="stylesheet" type="text/css" href="css/print.css" media="print" />


Insert an extra link after line 15:
  1.     <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
  2.     <meta name="keywords" content="<?php echo $this->GetConfigValue("meta_keywords") ?>" />
  3.     <meta name="description" content="<?php echo $this->GetConfigValue("meta_description") ?>" />
  4.     <link rel="stylesheet" type="text/css" href="css/refmenu.css" /><!-- temp: extra styles for referrer handlers -->
  5.     <link rel="stylesheet" type="text/css" href="css/<?php echo ($this->GetCookie("wikiskin"))? $this->GetCookie("wikiskin"): $this->GetConfigValue("stylesheet") ?>" media="screen" />
  6.     <link rel="stylesheet" type="text/css" href="css/print.css" media="print" />


This will put the necessary styling for the referrers handler user interface in place even if a custom skin is used.

7. css/refmenu.css


This is the actual stylesheet file - it will later be integrated in the main wikka stylesheet, of course.

  1. /*
  2.     This stylesheet is for the referrers and blacklist handlers.
  3.     It will need to be integrated  with the main stylesheet.
  4. */
  5.  
  6. h4 {
  7.     margin-top: 0.3em !important;   /* remove !important when integrating into main stylesheet or including it after that */
  8. }
  9.  
  10. .refmenu {
  11.     margin: 0;
  12.     padding: 0;
  13.     margin-top: 1em;
  14. }
  15. .refmenu .menu {
  16.     margin: 0;
  17.     padding: 0;
  18. }
  19. .refmenu .menu li {
  20.     list-style: none;
  21.     float: left;
  22.     margin-right: 3px;              /* margin-right goes together with float left (or vice versa) */
  23.     padding: 1px 2px;
  24.     font-size: 85%;
  25.     line-height: 1.2em;
  26.     color: #000000;
  27.     background-color: #DDDDDD;
  28. }
  29. br.clear {
  30.     clear: both;
  31. }
  32.  
  33. form fieldset.hidden {              /* for all forms! not just referrers */
  34.     display: none;
  35. }
  36.  
  37. #refform {
  38.     color: inherit;
  39.     background-color: inherit;
  40.     margin-top: 1em;
  41.     margin-bottom: 1em;
  42.     width: 32em;
  43. }
  44.  
  45. #refform fieldset {
  46.     padding: 1em;
  47.     margin-bottom: 0.3em;
  48.     border: 1px solid #666666;
  49. }
  50.  
  51. #refform legend {
  52.     padding: 0 2px;
  53.     color: #000000;
  54.     background-color: #DDDDDD;
  55.     border: 1px solid #666666;
  56.     margin-bottom: 0.3em;
  57. }
  58.  
  59. #refform .mainlabel {
  60.     float: left;
  61.     width: 4.6em;   /* width will work on _floated_ element, even if not a block! */
  62.     padding-right: 0.5em;
  63. }
  64.  
  65. #q, #qo, #ho {
  66.     width: 10em;
  67. }
  68. #h {
  69.     width: 3em;
  70.     text-align: right;
  71. }
  72.  
  73. #reflist {
  74.     margin-top: 1em;
  75.     margin-bottom: 1em;
  76.     border: none;
  77. }
  78. #reflist .hits {
  79.     width: 3em;
  80.     padding-right: 5px;
  81.     text-align: right;
  82.     vertical-align: middle;
  83. }
  84. #reflist .action {
  85.     width: 5em;
  86.     padding-left: 5px;
  87.     padding-right: 5px;
  88.     text-align: center;
  89.     vertical-align: middle;
  90. }
  91. #reflist .refs {
  92.     padding-left: 5px;
  93.     text-align: left;
  94.     vertical-align: middle;
  95. }


8. css/refmenu_col.css


The styling was designed to match with the default Wikka style. If you're using a custom skin here, everything should be positioned and spaced correctly, but the colors may not fit in with yours.

To save you hunting down what would need to be changed, grab this little file and copy it into your own skin on TestSkin: it contains all the color settings using in the extra stylesheet. Then simply adapt the colors to match your own: these will then override those in css/refmenu_col.css.

  1. /*
  2.     For custom stylesheets: copy this into your stylesheet; the
  3.     adapt the colors here (made to match the default Wikka skin)
  4.     to match your own.
  5. */
  6.  
  7. .refmenu .menu li {
  8.     color: #000000;
  9.     background-color: #DDDDDD;
  10. }
  11. #refform fieldset {
  12.     border: 1px solid #666666;
  13. }
  14. #refform legend {
  15.     color: #000000;
  16.     background-color: #DDDDDD;
  17.     border: 1px solid #666666;
  18. }



CategoryDevelopment
There are 8 comments on this page. [Show comments]
Valid XHTML :: Valid CSS: :: Powered by WikkaWiki