Wikka : AdvancedReferrersHandler

HomePage :: Categories :: Index :: Changes :: Comments :: Documentation :: Blog :: Login/Register

Advanced Referrers Handler


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