├── README.md ├── css └── form.css ├── js ├── form.js └── jquery.uniform.min.js ├── plugin.php └── themes └── default ├── images ├── bg-input-focus.png ├── bg-input.png └── sprite.png └── uniform.default.css /README.md: -------------------------------------------------------------------------------- 1 | # PIWIK-YOURLS 2 | 3 | A [Piwik](http://piwik.org/) plugin for [YOURLS](http://yourls.org) 4 | 5 | ## Features 6 | 7 | * Logs all requests with Piwik 8 | * Tracks IP and custom variables 9 | * Lets you disable local tracking/stats 10 | * Includes "Don't Log Bots" from OZH 11 | 12 | 13 | ## Requirements 14 | 15 | * PHP 5.3 16 | * PiwikTracker.php from your Piwik installation 17 | 18 | ## Setup 19 | 20 | ### YOURLS 21 | * Pull the files to /user/plugins/piwik 22 | * Place PiwikTracker.php into the /user/plugins/piwik/libs/Piwik folder 23 | * Activate the plugin in the admin zone of YOURLS 24 | * Go to the plugin page and fill in the required fields 25 | 26 | If you want better alert messages, you need to [apply this change](https://code.google.com/p/yourls/source/detail?r=787) to your YOURLS installation. 27 | 28 | ### Piwik 29 | 30 | * Create a test "website" and use that at the beginning until you find the right way to log your hits 31 | * Create or assign an admin auth token to that site 32 | * Visit some of your links 33 | * Check the hits on the "Visitors in real-time" widget 34 | 35 | ## Modifications 36 | 37 | If you're not happy with the way hits are tracked, just change the way the plugin does it by editing: 38 | 39 | // This shows up in the visitor logs and identify the source of the data 40 | $pt->setCustomVariable(1, 'App', 'Piwik plugin for YOURLS', 'visit'); 41 | 42 | // Some useful variables 43 | $pt->setCustomVariable(2, 'Domain landed', $domain_landed, 'page'); 44 | $pt->setCustomVariable(3, 'Keyword', $keyword, 'page'); 45 | 46 | // Track the visit in Piwik 47 | $title = yourls_get_keyword_title($keyword); 48 | @$pt->doTrackPageView($title); 49 | 50 | // The destination URL will show up as an outlink 51 | @$pt->doTrackAction($destination, 'link'); 52 | 53 | in plugin.php, function "itfs_piwik_log_request" 54 | 55 | 56 | ## Inspiration 57 | 58 | * Piwik plugin thread: https://code.google.com/p/yourls/issues/detail?id=661 59 | * Google Analytics plugin: http://www.seodenver.com/yourls-analytics/ 60 | * WP-Piwik Wordpress plugin: http://wordpress.org/extend/plugins/wp-piwik/ 61 | 62 | ## License 63 | 64 | Copyright 2012 - interfaSys sàrl - www.interfasys.ch 65 | 66 | Licensed under the GNU Affero General Public License, version 3 (AGPLv3) (the "License"); 67 | you may not use this file except in compliance with the License. 68 | You may obtain a copy of the License at 69 | 70 | `http://www.gnu.org/licenses/agpl-3.0.html` 71 | 72 | Unless required by applicable law or agreed to in writing, software 73 | distributed under the License is distributed on an "AS IS" BASIS, 74 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 75 | See the License for the specific language governing permissions and 76 | limitations under the License. -------------------------------------------------------------------------------- /css/form.css: -------------------------------------------------------------------------------- 1 | .message_success { 2 | border: 1px solid #4F8A10; 3 | background: #DFF2BF; 4 | -moz-border-radius: 6px; 5 | -webkit-border-radius: 6px; 6 | border-radius: 6px; 7 | width: 70%; 8 | margin-left: 15%; 9 | padding-left: 10px; 10 | margin-bottom: 5px; 11 | } 12 | 13 | .message_error { 14 | border: 1px solid #D8000C; 15 | background: #FFBABA; 16 | -moz-border-radius: 6px; 17 | -webkit-border-radius: 6px; 18 | border-radius: 6px; 19 | width: 70%; 20 | margin-left: 15%; 21 | padding-left: 10px; 22 | margin-bottom: 5px; 23 | } 24 | 25 | .message_warning { 26 | border: 1px solid #9F6000; 27 | background: #FEEFB3; 28 | -moz-border-radius: 6px; 29 | -webkit-border-radius: 6px; 30 | border-radius: 6px; 31 | width: 70%; 32 | margin-left: 15%; 33 | padding-left: 10px; 34 | margin-bottom: 5px; 35 | } 36 | 37 | #piwik { 38 | 39 | } 40 | 41 | #piwik_config { 42 | display: inline-block; 43 | width: 40%; 44 | } 45 | 46 | #piwik_support { 47 | display: inline-block; 48 | width: 40%; 49 | vertical-align: top; 50 | margin-left: 5%; 51 | } 52 | 53 | #reset { 54 | clear: both ; 55 | } 56 | 57 | #piwik_config form { 58 | margin-bottom: 20px 59 | } 60 | 61 | .piwik_config_zone { 62 | border: 1px solid #2a85b3; 63 | background: #F3FAFD; 64 | -moz-border-radius: 6px; 65 | -webkit-border-radius: 6px; 66 | border-radius: 6px; 67 | margin: 0 0 5% 2%; 68 | padding: 0 10px 14px 10px; 69 | } 70 | 71 | .piwik_config_zone .required { 72 | font-size: smaller; 73 | color: #D8000C; 74 | } 75 | 76 | .piwik_config_zone h3 { 77 | color: #2A85B3; 78 | } 79 | 80 | .piwik_config_zone p { 81 | margin-top: 0; 82 | } 83 | 84 | .piwik_config_zone p.checkbox_description { 85 | margin-top: 25px; 86 | } 87 | 88 | .parameter { 89 | position: relative; 90 | } 91 | 92 | .parameter p { 93 | display: inline-block; 94 | } 95 | 96 | .piwik_config_zone label { 97 | font-weight: bold; 98 | color: #555; 99 | display: inline-block; 100 | margin-bottom: 10px; 101 | width: 100px; 102 | } 103 | 104 | .piwik_config_zone label.piwik_config_checkbox { 105 | width: 150px; 106 | } 107 | 108 | .piwik_config_zone input.text { 109 | background-color: white; 110 | } 111 | 112 | .piwik_config_submit { 113 | text-align: center; 114 | } -------------------------------------------------------------------------------- /js/form.js: -------------------------------------------------------------------------------- 1 | /*Some JavaScript for the form*/ 2 | $(function () { 3 | 'use strict'; 4 | $('#options h3 a').click(function () { 5 | var a = $(this).attr('href'); 6 | $(a).slideToggle(); 7 | return false; 8 | }); 9 | $(".parameter").hide(); 10 | 11 | $('A[rel="external"]').click(function () { 12 | window.open($(this).attr('href')); 13 | return false; 14 | }); 15 | 16 | setTimeout(function () { 17 | $('.message_success').slideToggle(); 18 | }, 2000); 19 | 20 | // Enable uniform.js. Select cannot be used in a hidden layer...yet 21 | $("input, button").uniform(); 22 | //$("select, input, button").uniform(); 23 | }); -------------------------------------------------------------------------------- /js/jquery.uniform.min.js: -------------------------------------------------------------------------------- 1 | (function(f,n){function h(a,b){for(var c in b)b.hasOwnProperty(c)&&(c.replace(/ |$/g,".uniform"),a.bind(c,b[c]))}function k(a,b,c){h(a,{focus:function(){b.addClass(c.focusClass)},blur:function(){b.removeClass(c.focusClass);b.removeClass(c.activeClass)},mouseenter:function(){b.addClass(c.hoverClass)},mouseleave:function(){b.removeClass(c.hoverClass);b.removeClass(c.activeClass)},"mousedown touchbegin":function(){a.is(":disabled")||b.addClass(c.activeClass)},"mouseup touchend":function(){b.removeClass(c.activeClass)}})} 2 | function q(a){if(a[0].multiple)return!0;a=a.attr("size");return a===n||1>=a?!1:!0}function r(a,b,c){a=a.val();""===a?a=c.fileDefaultHtml:(a=a.split(/[\/\\]+/),a=a[a.length-1]);b.text(a)}function l(a,b){a.removeClass(b.hoverClass+" "+b.focusClass+" "+b.activeClass)}function i(a,b,c){var d=b.is(":checked");b.prop?b.prop("checked",d):d?b.attr("checked","checked"):b.removeAttr("checked");b=c.checkedClass;d?a.addClass(b):a.removeClass(b)}function j(a,b,c){c=c.disabledClass;b.is(":disabled")?a.addClass(c): 3 | a.removeClass(c)}function p(a,b,c){switch(c){case "after":return a.after(b),a.next();case "before":return a.before(b),a.prev();case "wrap":return a.wrap(b),a.parent()}return null}function m(a,b,c){var d,e;c||(c={});c=f.extend({bind:{},css:null,divClass:null,divWrap:"wrap",spanClass:null,spanHtml:null,spanWrap:"wrap"},c);d=f("
");e=f("");b.autoHide&&!a.is(":visible")&&d.hide();c.divClass&&d.addClass(c.divClass);c.spanClass&&e.addClass(c.spanClass);b.useID&&a.attr("id")&&d.attr("id",b.idPrefix+ 4 | "-"+a.attr("id"));c.spanHtml&&e.html(c.spanHtml);d=p(a,d,c.divWrap);e=p(a,e,c.spanWrap);c.css&&a.css(c.css);j(d,a,b);return{div:d,span:e}}f.uniform={defaults:{activeClass:"active",autoHide:!0,buttonClass:"button",checkboxClass:"checker",checkedClass:"checked",disabledClass:"disabled",fileButtonClass:"action",fileButtonHtml:"Choose File",fileClass:"uploader",fileDefaultHtml:"No file selected",filenameClass:"filename",focusClass:"focus",hoverClass:"hover",idPrefix:"uniform",radioClass:"radio",resetDefaultHtml:"Reset", 5 | resetSelector:!1,selectAutoWidth:!1,selectClass:"selector",submitDefaultHtml:"Submit",useID:!0},elements:[]};var s=!0,t=[{match:function(a){return a.is("button, :submit, :reset, a, input[type='button']")},apply:function(a,b){var c,d;d=b.submitDefaultHtml;a.is(":reset")&&(d=b.resetDefaultHtml);if(a.is("a, button"))d=a.html()||d;else if(a.is(":submit, :reset, input[type=button]")){var e;e=(e=a.attr("value"))?f("").text(e).html():"";d=e||d}c=m(a,b,{css:{display:"none"},divClass:b.buttonClass, 6 | spanHtml:d}).div;k(a,c,b);h(c,{"click touchend":function(b){f(b.target).is("span, div")&&(a[0].dispatchEvent?(b=document.createEvent("MouseEvents"),b.initEvent("click",!0,!0),a[0].dispatchEvent(b)):a.click())}});f.uniform.noSelect(c);return{remove:function(){return a.unwrap().unwrap()},update:function(){l(c,b);j(c,a,b)}}}},{match:function(a){return a.is(":checkbox")},apply:function(a,b){var c,d,e;c=m(a,b,{css:{opacity:0},divClass:b.checkboxClass});d=c.div;e=c.span;k(a,d,b);h(a,{"click touchend":function(){i(e, 7 | a,b)}});i(e,a,b);return{remove:function(){return a.unwrap().unwrap()},update:function(){l(d,b);e.removeClass(b.checkedClass);i(e,a,b);j(d,a,b)}}}},{match:function(a){return a.is(":file")},apply:function(a,b){function c(){r(a,g,b)}var d,e,g;d=m(a,b,{css:{opacity:0},divClass:b.fileClass,spanClass:b.fileButtonClass,spanHtml:b.fileButtonHtml,spanWrap:"after"});e=d.div;d=d.span;g=f("").html(b.fileDefaultHtml);g.addClass(b.filenameClass);g=p(a,g,"after");a.attr("size")||a.attr("size",e.width()/ 8 | 10);k(a,e,b);c();f.browser.msie?h(a,{click:function(){a.trigger("change");setTimeout(c,0)}}):h(a,{change:c});f.uniform.noSelect(g);f.uniform.noSelect(d);return{remove:function(){a.siblings("span").remove();a.unwrap();return a},update:function(){l(e,b);r(a,g,b);j(e,a,b)}}}},{match:function(a){return a.is("input")?(a=a.attr("type").toLowerCase(),0<=" color date datetime datetime-local email month number password search tel text time url week ".indexOf(" "+a+" ")):!1},apply:function(a){var b=a.attr("type"); 9 | a.addClass(b);return{remove:function(){a.removeClass(b)},update:function(){}}}},{match:function(a){return a.is(":radio")},apply:function(a,b){var c,d,e;c=m(a,b,{css:{opacity:0},divClass:b.radioClass});d=c.div;e=c.span;k(a,d,b);h(a,{"click touchend":function(){var c="."+b.radioClass.split(" ")[0]+" span."+b.checkedClass+":has([name='"+a.attr("name")+"'])";f(c).each(function(){var a=f(this),c=a.find(":radio");i(a,c,b)});i(e,a,b)}});i(e,a,b);return{remove:function(){return a.unwrap().unwrap()},update:function(){l(d, 10 | b);i(e,a,b);j(d,a,b)}}}},{match:function(a){return a.is("select")&&!q(a)?!0:!1},apply:function(a,b){var c,d,e,g;g=a.width();c=m(a,b,{css:{opacity:0,left:"2px",width:g+32+"px"},divClass:b.selectClass,spanHtml:(a.find(":selected:first")||a.find("option:first")).html(),spanWrap:"before"});d=c.div;e=c.span;b.selectAutoWidth?(d.width(f("
").width()-f("").width()+g+25),c=parseInt(d.css("paddingLeft"),10),e.width(g-c-15),a.width(g+c),a.css("min-width",g+c+"px"),d.width(g+c)):(c=a.width(),d.width(c), 11 | e.width(c-25));k(a,d,b);h(a,{change:function(){e.html(a.find(":selected").html());d.removeClass(b.activeClass)},"click touchend":function(){var b=a.find(":selected").html();e.html()!==b&&a.trigger("change")},keyup:function(){e.html(a.find(":selected").html())}});f.uniform.noSelect(e);return{remove:function(){a.siblings("span").remove();a.unwrap();return a},update:function(){l(d,b);e.html(a.find(":selected").html());j(d,a,b)}}}},{match:function(a){return a.is("select")&&q(a)?!0:!1},apply:function(a){a.addClass("uniform-multiselect"); 12 | return{remove:function(){a.removeClass("uniform-multiselect")},update:function(){}}}},{match:function(a){return a.is("textarea")},apply:function(a){a.addClass("uniform");return{remove:function(){a.removeClass("uniform")},update:function(){}}}}];f.browser.msie&&7>f.browser.version&&(s=!1);f.fn.uniform=function(a){var b=this,a=f.extend({},f.uniform.defaults,a);!1!==a.resetSelector&&f(a.resetSelector).mouseup(function(){window.setTimeout(function(){f.uniform.update(b)},10)});return this.each(function(){var b= 13 | f(this),d,e;b.data("uniformed")&&f.uniform.update(b);if(!b.data("uniformed")&&s)for(d=0;dPiwik. Tracks IP and custom variables. Includes "Don't Log Bots" from OZH 6 | Version: 1.0 7 | Author: interfaSys sàrl 8 | Author URI: http://www.interfasys.ch 9 | */ 10 | 11 | // No direct call 12 | if (!defined('YOURLS_ABSPATH')) die(); 13 | 14 | /********************** 15 | * ADMIN SECTION 16 | *********************/ 17 | 18 | // Once the plugins have been loaded, register ours 19 | yourls_add_action('plugins_loaded', 'itfs_piwik_register_plugin_page'); 20 | 21 | /** 22 | * Registers our configuration page with YOURLS 23 | */ 24 | function itfs_piwik_register_plugin_page() { 25 | // Parameters: page slug, page title, and function that will display the page itself 26 | yourls_register_plugin_page('itfs_piwik_admin', 'Piwik', 'itfs_piwik_admin_settings'); 27 | } 28 | 29 | /** 30 | * Displays the plugin's configuration page 31 | */ 32 | function itfs_piwik_admin_settings() { 33 | 34 | // Check if an update to the configuration was submitted 35 | if (isset($_POST['piwik_config'])) { 36 | itfs_piwik_admin_settings_update(); 37 | } 38 | 39 | // Get current configuration from database 40 | $piwik_config = yourls_get_option('piwik_config'); 41 | 42 | ?> 43 | 45 | 47 | 48 | 49 |
50 |
51 |

Piwik plugin settings

52 | 53 |
54 |
55 |

Basic settings

56 | 57 |
58 | 60 | 64 |
65 |
66 | 68 | 72 |
73 |
74 |

You can disable the built-in stats system by ticking the box 75 | below

76 | 77 | 78 | id="disable_stats" name="piwik_config[disable_stats]"/> 79 | 80 |

81 | Clicks will still be counted locally 82 |

83 |
84 |
85 |

You can stop tracking visits from bots by ticking the box below

86 | 87 | 88 | id="remove_bots" name="piwik_config[remove_bots]"/> 89 |
90 |

91 | * Required fields 92 |

93 |
94 |
95 |
96 |

Authentication

97 | 98 |
99 |

This is required if you want to be able to track you visitors' IPs
100 | This must be an admin token (read/write access)

101 | 102 |
103 | 104 | 106 |
107 |
108 |
109 |
110 |

Custom Variable

111 | 112 |
113 |

You can set an optional custom 115 | variable if you have the use for it

116 | 117 |
118 | 119 | 123 |
124 |
125 | 126 | 130 |
131 |
132 | 133 | 139 |
140 |
141 |
142 |
143 |
144 | 145 |
146 |
147 |
148 |
149 |

Support developpement

150 | 151 |

This plugin was developed by interfaSys and you 152 | can support its development by making a donation below.

153 | 154 |

Even $2 makes a difference by showing your appreciation ;)

155 | 156 |
157 | 158 | 159 | 161 | 162 |
163 |

Project homepage

164 | 165 |

You can find the latest version of the plugin on GitHub.

167 | 168 |

License

169 | 170 |

Copyright 2012 - interfaSys sàrl - www.interfasys.ch

172 | Licensed under the GNU Affero General Public License, version 3 (AGPLv3) (the "License"); you may not use 173 | this file except in compliance with the License. You may obtain a copy of the License at

174 | http://www.gnu.org/licenses/agpl-3.0.html 176 |

177 |
178 |
179 |
180 | $v) { 205 | if ($k == 'site_id') { 206 | $piwik_config[$k] = @intval($v); 207 | } else if ($k == 'piwik_url') { 208 | // Site URL must end with a slash. Stolen as-is from wp-piwik 209 | if (substr($v, -1, 1) != '/' && substr($v, -10, 10) != '/index.php') { 210 | $v .= '/'; 211 | } 212 | $piwik_config[$k] = yourls_sanitize_url($v); 213 | } else { 214 | $piwik_config[$k] = yourls_sanitize_title($v); 215 | } 216 | } 217 | 218 | try { 219 | yourls_update_option('piwik_config', $piwik_config); 220 | } catch (Exception $e) { 221 | $message = "ITFS_PIWIK: Error when trying to save settings. " . $e->getMessage(); 222 | error_log($message, 0); 223 | echo yourls_add_notice($message, 'message_error'); 224 | return false; 225 | } 226 | } 227 | } 228 | } 229 | 230 | yourls_add_action('admin_init', 'itfs_piwik_admin_messages'); 231 | 232 | /** 233 | * Displays an error message in case of missing required variables 234 | * 235 | * TODO: Needs more testing 236 | */ 237 | function itfs_piwik_admin_messages() { 238 | if (!isset($_POST['piwik_config'])) { 239 | return; 240 | } 241 | $error_message = ''; 242 | if (empty($_POST['piwik_config']['piwik_url'])) { 243 | $error_message .= '

is a required field.

'; 244 | } 245 | if (empty($_POST['piwik_config']['site_id'])) { 246 | $error_message .= '

is a required field.

'; 247 | } 248 | 249 | if (!empty($error_message)) { 250 | echo yourls_add_notice($error_message, 'message_error'); 251 | } else { 252 | echo yourls_add_notice('Settings have been saved', 'message_success'); 253 | } 254 | } 255 | 256 | yourls_add_filter('table_add_row_action_array', 'itfs_piwik_edit_stats_link'); 257 | 258 | /** 259 | * Switches the internal link to stats to the Piwik one 260 | * @param array $actions 261 | * @return array 262 | */ 263 | function itfs_piwik_edit_stats_link($actions) { 264 | 265 | // Get current configuration from database 266 | $piwik_config = yourls_get_option('piwik_config'); 267 | 268 | // If we don't log stats locally 269 | if ($piwik_config[disable_stats]) { 270 | 271 | // If we're not using the free version of the plugin 272 | if ($piwik_config[SKU] !== "free") { 273 | 274 | // Replace each link with a link to the Piwik installation 275 | foreach ($actions as $k => &$action) { 276 | if ($k == "stats") { 277 | $action['href'] = $piwik_config[piwik_url]; 278 | } 279 | } 280 | } 281 | } 282 | 283 | return $actions; 284 | } 285 | 286 | yourls_add_action('load_template_infos', 'itfs_piwik_disable_stats'); 287 | 288 | /** 289 | * Disables the stats page and redirects the user to the long URL instead 290 | * @param array $keyword 291 | */ 292 | function itfs_piwik_disable_stats($keyword) { 293 | // Get current configuration from database 294 | $piwik_config = yourls_get_option('piwik_config'); 295 | 296 | // We don't display the stats page in the free version if you don't log locally 297 | if ($piwik_config[disable_stats] && $piwik_config[SKU] === "free") { 298 | $keyword = $keyword[0]; 299 | 300 | // This redirects users to the destination url instead of the stats page 301 | include(YOURLS_ABSPATH . '/yourls-go.php'); 302 | exit; 303 | } 304 | } 305 | 306 | /********************** 307 | * TRACKING SECTION 308 | *********************/ 309 | 310 | // We hook into the internal logging function 311 | yourls_add_filter('shunt_log_redirect', 'itfs_piwik_log_request'); 312 | 313 | /** 314 | * Sends the keyword and destination URL to Piwik 315 | * 316 | * @param bool $return The value to return. Defaults to false with doesn't enable the filter 317 | * @param string $keyword The requested keyword 318 | * @return bool 319 | */ 320 | function itfs_piwik_log_request($return, $keyword) { 321 | 322 | // Get current configuration from database 323 | $piwik_config = yourls_get_option('piwik_config'); 324 | 325 | // Let's check if the user wants to log bots 326 | if ($piwik_config[remove_bots]) { 327 | if (itfs_piwik_is_bot()) { 328 | return $return; 329 | } 330 | } 331 | 332 | try { 333 | // Need to use a file_exists check as require only produces a fatal compilation error 334 | if (!file_exists(dirname(__FILE__) . '/libs/Piwik/PiwikTracker.php')) { 335 | throw new Exception ("Error can't load PiwikTracker.php"); 336 | } else { 337 | // Piwik Tracking API init 338 | require_once dirname(__FILE__) . '/libs/Piwik/PiwikTracker.php'; 339 | PiwikTracker::$URL = $piwik_config[piwik_url]; 340 | } 341 | } catch (Exception $e) { 342 | error_log("ITFS_PIWIK: " . $e->getMessage(), 0); 343 | return $return; 344 | } 345 | // Use this to get the destination 346 | $destination = yourls_get_keyword_longurl($keyword); 347 | 348 | // Only log a request if we have a destination and the proper Piwik settings 349 | if ($destination == false) { 350 | error_log("ITFS_PIWIK: Missing parameters prevented me from logging the request with Piwik", 0); 351 | error_log("ITFS_PIWIK: Parameters we have: " . $keyword . ', ' . $destination, 0); 352 | return $return; 353 | } 354 | 355 | //Useful for hosts using one Piwik installation with multiple YOURLS installation 356 | $domain_landed = $_SERVER['HTTP_HOST']; 357 | 358 | $page_url = "http://" . $domain_landed . "/" . $keyword; 359 | 360 | try { 361 | $pt = new PiwikTracker($piwik_config[site_id]); 362 | 363 | // This will be the entry page in Piwik 364 | $pt->setUrl($page_url); 365 | 366 | // This will fail silently if the token is not valid or if the user doesn't have admin rights 367 | if (!empty($piwik_config[token])) { 368 | $pt->setTokenAuth($piwik_config[token]); 369 | } 370 | 371 | // This shows up in the visitor logs and identify the source of the data 372 | $pt->setCustomVariable(1, 'App', 'Piwik plugin for YOURLS', 'visit'); 373 | 374 | // Some useful variables 375 | $pt->setCustomVariable(2, 'Domain landed', $domain_landed, 'page'); 376 | $pt->setCustomVariable(3, 'Keyword', $keyword, 'page'); 377 | 378 | // User defined custom variable 379 | if (!empty($piwik_config[customvar_name]) && !empty($piwik_config[customvar_value])) { 380 | $pt->setCustomVariable(4, $piwik_config[customvar_name], $piwik_config[customvar_value], $piwik_config[customvar_scope]); 381 | } 382 | 383 | // Track the visit in Piwik 384 | $title = yourls_get_keyword_title($keyword); 385 | @$pt->doTrackPageView($title); 386 | 387 | // The destination URL will show up as an outlink 388 | @$pt->doTrackAction($destination, 'link'); 389 | 390 | } catch (Exception $e) { 391 | error_log("ITFS_PIWIK: Error when trying to log the request with Piwik. " . $e->getMessage(), 0); 392 | return $return; 393 | } 394 | 395 | if ($piwik_config[disable_stats]) { 396 | //error_log("ITFS_PIWIK: NOT logging locally", 0); 397 | return; 398 | } else { 399 | //error_log("ITFS_PIWIK: Logging locally", 0); 400 | return $return; 401 | } 402 | } 403 | 404 | /** 405 | * Determines if a visitor is a bot 406 | * This is an improved version of "Don't Log Bots" from OZH 407 | * @return bool 408 | */ 409 | function itfs_piwik_is_bot() { 410 | // Get current User-Agent 411 | $current = strtolower($_SERVER['HTTP_USER_AGENT']); 412 | $current_ip = $_SERVER['REMOTE_ADDR']; 413 | 414 | // Array of known bot lowercase strings 415 | // Example: 'googlebot' will match 'Googlebot/2.1 (+http://www.googlebot.com/bot.html)' 416 | $bots = array( 417 | // general web bots 418 | 'googlebot', 'yahoo! slurp', 419 | 'dotbot', 'yeti', 'http://help.naver.com/robots/', 'scoutjet', 420 | 'http://yandex.com/bots', 'linkedinbot', 'mj12bot', 'http://www.80legs.com/spider.html', 421 | 'exabot', 'msnbot', 'yacybot', 'www.oneriot.com', 'http://flipboard.com/', 422 | 'baiduspider', 'mxbot', 'bingbot', 'wikiwix-bot', 'voyager', 'http://www.evri.com/evrinid', 423 | 'http://www.videosurf.com/bot.html', 'http://justsignal.com', 'http://labs.topsy.com/butterfly', 424 | 'www.metadatalabs.com/mlbot', 'twingly recon', 'kame-rt', 'urlresolver', 'http://www.twitmunin.com', 425 | 'http://code.google.com/appengine', 426 | 427 | // twitter specific bots 428 | 'http://thinglabs.com', 'js-kit url resolver', 'twitterbot', 'njuicebot', 'postrank.com', 429 | 'tweetmemebot', 'longurl api', 'paperlibot', 'http://postpo.st/crawlers', 430 | 431 | // Facebook 432 | 'facebookexternalhit', 433 | 434 | // URL testers 435 | 'metauri', 436 | 437 | //Undecided 438 | // buzzrank.de Birubot pycurl/7.18.2 may not be a bot 439 | ); 440 | 441 | $bots_ip = array( 442 | '65.52.0.146', '65.52.17.79', '65.52.2.212', 443 | ); 444 | 445 | 446 | // Check if the current UA string contains a know bot string 447 | $is_bot = (str_replace($bots, '', $current) != $current); 448 | $is_bot_ip = (str_replace($bots_ip, '', $current_ip) != $current_ip); 449 | 450 | $is_bot = $is_bot || $is_bot_ip; 451 | 452 | return $is_bot; 453 | } 454 | 455 | /********************** 456 | * STATS PAGE SECTION 457 | *********************/ 458 | 459 | if (file_exists(dirname(__FILE__) . '/donations.php')) { 460 | require_once dirname(__FILE__) . '/donations.php'; 461 | error_log("ITFS_PIWIK: Loading donations file", 0); 462 | } else if (file_exists(dirname(__FILE__) . '/pro.php')) { 463 | require_once dirname(__FILE__) . '/pro.php'; 464 | } 465 | 466 | -------------------------------------------------------------------------------- /themes/default/images/bg-input-focus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/interfasys/piwik-yourls/f378ffcc67f67973eac3d5ae460268586d01f37a/themes/default/images/bg-input-focus.png -------------------------------------------------------------------------------- /themes/default/images/bg-input.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/interfasys/piwik-yourls/f378ffcc67f67973eac3d5ae460268586d01f37a/themes/default/images/bg-input.png -------------------------------------------------------------------------------- /themes/default/images/sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/interfasys/piwik-yourls/f378ffcc67f67973eac3d5ae460268586d01f37a/themes/default/images/sprite.png -------------------------------------------------------------------------------- /themes/default/uniform.default.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Uniform Theme: Uniform Default 4 | Version: 1.8 5 | By: Josh Pyles 6 | License: MIT License 7 | --- 8 | For use with the Uniform plugin: 9 | http://pixelmatrixdesign.com/uniform/ 10 | --- 11 | Generated by Uniform Theme Generator: 12 | http://pixelmatrixdesign.com/uniform/themer.html 13 | 14 | */ 15 | 16 | /* Global Declaration */ 17 | 18 | div.selector, 19 | div.selector span, 20 | div.checker span, 21 | div.radio span, 22 | div.uploader, 23 | div.uploader span.action, 24 | div.button, 25 | div.button span { 26 | background-image: url(images/sprite.png); 27 | background-repeat: no-repeat; 28 | -webkit-font-smoothing: antialiased; 29 | } 30 | 31 | .selector, 32 | .radio, 33 | .checker, 34 | .uploader, 35 | .button, 36 | .selector *, 37 | .radio *, 38 | .checker *, 39 | .uploader *, 40 | .button *{ 41 | margin: 0; 42 | padding: 0; 43 | } 44 | 45 | /* INPUT & TEXTAREA */ 46 | 47 | input.text, 48 | input.email, 49 | input.search, 50 | input.tel, 51 | input.url, 52 | input.datetime, 53 | input.date, 54 | input.month, 55 | input.week, 56 | input.time, 57 | input.datetime-local, 58 | input.number, 59 | input.color, 60 | input.password, 61 | select.uniform-multiselect, 62 | textarea.uniform { 63 | font-size: 12px; 64 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 65 | font-weight: normal; 66 | padding: 3px; 67 | color: #777; 68 | background: url('images/bg-input.png') repeat-x 0px 0px; 69 | border-top: solid 1px #aaa; 70 | border-left: solid 1px #aaa; 71 | border-bottom: solid 1px #ccc; 72 | border-right: solid 1px #ccc; 73 | -webkit-border-radius: 3px; 74 | -moz-border-radius: 3px; 75 | border-radius: 3px; 76 | outline: 0; 77 | } 78 | 79 | /* remove default webkit and possible mozilla .search styles */ 80 | input.search, input.search:active { 81 | -moz-appearance: none; 82 | -webkit-appearance: none; 83 | } 84 | 85 | input.text:focus, 86 | input.email:focus, 87 | input.search:focus, 88 | input.tel:focus, 89 | input.url:focus, 90 | input.datetime:focus, 91 | input.date:focus, 92 | input.month:focus, 93 | input.week:focus, 94 | input.time:focus, 95 | input.datetime-local:focus, 96 | input.number:focus, 97 | input.color:focus, 98 | input.password:focus, 99 | select.uniform-multiselect:focus, 100 | textarea.uniform:focus { 101 | -webkit-box-shadow: 0px 0px 4px rgba(0,0,0,0.3); 102 | -moz-box-shadow: 0px 0px 4px rgba(0,0,0,0.3); 103 | box-shadow: 0px 0px 4px rgba(0,0,0,0.3); 104 | border-color: #999; 105 | background: url('images/bg-input-focus.png') repeat-x 0px 0px; 106 | } 107 | 108 | /* SPRITES */ 109 | 110 | /* Select */ 111 | 112 | div.selector { 113 | background-position: -483px -130px; 114 | line-height: 26px; 115 | height: 26px; 116 | } 117 | 118 | div.selector span { 119 | background-position: right 0px; 120 | height: 26px; 121 | line-height: 26px; 122 | } 123 | 124 | div.selector select { 125 | /* change these to adjust positioning of select element */ 126 | top: 0px; 127 | left: 0px; 128 | } 129 | 130 | div.selector:active, 131 | div.selector.active { 132 | background-position: -483px -156px; 133 | } 134 | 135 | div.selector:active span, 136 | div.selector.active span { 137 | background-position: right -26px; 138 | } 139 | 140 | div.selector.focus, div.selector.hover, div.selector:hover { 141 | background-position: -483px -182px; 142 | } 143 | 144 | div.selector.focus span, div.selector.hover span, div.selector:hover span { 145 | background-position: right -52px; 146 | } 147 | 148 | div.selector.focus:active, 149 | div.selector.focus.active, 150 | div.selector:hover:active, 151 | div.selector.active:hover { 152 | background-position: -483px -208px; 153 | } 154 | 155 | div.selector.focus:active span, 156 | div.selector:hover:active span, 157 | div.selector.active:hover span, 158 | div.selector.focus.active span { 159 | background-position: right -78px; 160 | } 161 | 162 | div.selector.disabled { 163 | background-position: -483px -234px; 164 | } 165 | 166 | div.selector.disabled span { 167 | background-position: right -104px; 168 | } 169 | 170 | /* Checkbox */ 171 | 172 | div.checker { 173 | width: 19px; 174 | height: 19px; 175 | } 176 | 177 | div.checker input { 178 | width: 19px; 179 | height: 19px; 180 | } 181 | 182 | div.checker span { 183 | background-position: 0px -260px; 184 | height: 19px; 185 | width: 19px; 186 | } 187 | 188 | div.checker:active span, 189 | div.checker.active span { 190 | background-position: -19px -260px; 191 | } 192 | 193 | div.checker.focus span, 194 | div.checker:hover span { 195 | background-position: -38px -260px; 196 | } 197 | 198 | div.checker.focus:active span, 199 | div.checker:active:hover span, 200 | div.checker.active:hover span, 201 | div.checker.focus.active span { 202 | background-position: -57px -260px; 203 | } 204 | 205 | div.checker span.checked { 206 | background-position: -76px -260px; 207 | } 208 | 209 | div.checker:active span.checked, 210 | div.checker.active span.checked { 211 | background-position: -95px -260px; 212 | } 213 | 214 | div.checker.focus span.checked, 215 | div.checker:hover span.checked { 216 | background-position: -114px -260px; 217 | } 218 | 219 | div.checker.focus:active span.checked, 220 | div.checker:hover:active span.checked, 221 | div.checker.active:hover span.checked, 222 | div.checker.active.focus span.checked { 223 | background-position: -133px -260px; 224 | } 225 | 226 | div.checker.disabled span, 227 | div.checker.disabled:active span, 228 | div.checker.disabled.active span { 229 | background-position: -152px -260px; 230 | } 231 | 232 | div.checker.disabled span.checked, 233 | div.checker.disabled:active span.checked, 234 | div.checker.disabled.active span.checked { 235 | background-position: -171px -260px; 236 | } 237 | 238 | /* Radio */ 239 | 240 | div.radio { 241 | width: 18px; 242 | height: 18px; 243 | } 244 | 245 | div.radio input { 246 | width: 18px; 247 | height: 18px; 248 | } 249 | 250 | div.radio span { 251 | height: 18px; 252 | width: 18px; 253 | background-position: 0px -279px; 254 | } 255 | 256 | div.radio:active span, 257 | div.radio.active span { 258 | background-position: -18px -279px; 259 | } 260 | 261 | div.radio.focus span, 262 | div.radio:hover span { 263 | background-position: -36px -279px; 264 | } 265 | 266 | div.radio.focus:active span, 267 | div.radio:active:hover span, 268 | div.radio.active:hover span, 269 | div.radio.active.focus span { 270 | background-position: -54px -279px; 271 | } 272 | 273 | div.radio span.checked { 274 | background-position: -72px -279px; 275 | } 276 | 277 | div.radio:active span.checked, 278 | div.radio.active span.checked { 279 | background-position: -90px -279px; 280 | } 281 | 282 | div.radio.focus span.checked, div.radio:hover span.checked { 283 | background-position: -108px -279px; 284 | } 285 | 286 | div.radio.focus:active span.checked, 287 | div.radio:hover:active span.checked, 288 | div.radio.focus.active span.checked, 289 | div.radio.active:hover span.checked { 290 | background-position: -126px -279px; 291 | } 292 | 293 | div.radio.disabled span, 294 | div.radio.disabled:active span, 295 | div.radio.disabled.active span { 296 | background-position: -144px -279px; 297 | } 298 | 299 | div.radio.disabled span.checked, 300 | div.radio.disabled:active span.checked, 301 | div.radio.disabled.active span.checked { 302 | background-position: -162px -279px; 303 | } 304 | 305 | /* Uploader */ 306 | 307 | div.uploader { 308 | background-position: 0px -297px; 309 | height: 28px; 310 | } 311 | 312 | div.uploader span.action { 313 | background-position: right -409px; 314 | height: 24px; 315 | line-height: 24px; 316 | } 317 | 318 | div.uploader span.filename { 319 | height: 24px; 320 | /* change this line to adjust positioning of filename area */ 321 | margin: 2px 0px 2px 2px; 322 | line-height: 24px; 323 | } 324 | 325 | div.uploader.focus, 326 | div.uploader.hover, 327 | div.uploader:hover { 328 | background-position: 0px -353px; 329 | } 330 | 331 | div.uploader.focus span.action, 332 | div.uploader.hover span.action, 333 | div.uploader:hover span.action { 334 | background-position: right -437px; 335 | } 336 | 337 | div.uploader.active span.action, 338 | div.uploader:active span.action { 339 | background-position: right -465px; 340 | } 341 | 342 | div.uploader.focus.active span.action, 343 | div.uploader:focus.active span.action, 344 | div.uploader.focus:active span.action, 345 | div.uploader:focus:active span.action { 346 | background-position: right -493px; 347 | } 348 | 349 | div.uploader.disabled { 350 | background-position: 0px -325px; 351 | } 352 | 353 | div.uploader.disabled span.action { 354 | background-position: right -381px; 355 | } 356 | 357 | div.button { 358 | background-position: 0px -523px; 359 | } 360 | 361 | div.button span { 362 | background-position: right -643px; 363 | } 364 | 365 | div.button.focus, 366 | div.button:focus, 367 | div.button:hover, 368 | div.button.hover { 369 | background-position: 0px -553px; 370 | } 371 | 372 | div.button.focus span, 373 | div.button:focus span, 374 | div.button:hover span, 375 | div.button.hover span { 376 | background-position: right -673px; 377 | } 378 | 379 | div.button.active, 380 | div.button:active { 381 | background-position: 0px -583px; 382 | } 383 | 384 | div.button.active span, 385 | div.button:active span { 386 | background-position: right -703px; 387 | color: #555; 388 | } 389 | 390 | div.button.disabled, 391 | div.button:disabled { 392 | background-position: 0px -613px; 393 | } 394 | 395 | div.button.disabled span, 396 | div.button:disabled span { 397 | background-position: right -733px; 398 | color: #bbb; 399 | cursor: default; 400 | } 401 | 402 | /* PRESENTATION */ 403 | 404 | /* Button */ 405 | 406 | div.button { 407 | height: 30px; 408 | } 409 | 410 | div.button span { 411 | margin-left: 13px; 412 | height: 22px; 413 | padding-top: 8px; 414 | font-weight: bold; 415 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 416 | font-size: 12px; 417 | letter-spacing: 1px; 418 | text-transform: uppercase; 419 | padding-left: 2px; 420 | padding-right: 15px; 421 | } 422 | 423 | /* Select */ 424 | div.selector { 425 | width: 190px; 426 | font-size: 12px; 427 | } 428 | 429 | div.selector select { 430 | min-width: 190px; 431 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 432 | font-size: 12px; 433 | border: solid 1px #fff; 434 | } 435 | 436 | div.selector span { 437 | padding: 0px 25px 0px 2px; 438 | cursor: pointer; 439 | } 440 | 441 | div.selector span { 442 | color: #666; 443 | width: 158px; 444 | text-shadow: 0 1px 0 #fff; 445 | } 446 | 447 | div.selector.disabled span { 448 | color: #bbb; 449 | } 450 | 451 | /* Checker */ 452 | div.checker { 453 | margin-right: 5px; 454 | } 455 | 456 | /* Radio */ 457 | div.radio { 458 | margin-right: 3px; 459 | } 460 | 461 | /* Uploader */ 462 | div.uploader { 463 | width: 190px; 464 | cursor: pointer; 465 | } 466 | 467 | div.uploader span.action { 468 | width: 85px; 469 | text-align: center; 470 | text-shadow: #fff 0px 1px 0px; 471 | background-color: #fff; 472 | font-size: 11px; 473 | font-weight: bold; 474 | } 475 | 476 | div.uploader span.filename { 477 | color: #777; 478 | width: 82px; 479 | border-right: solid 1px #bbb; 480 | font-size: 11px; 481 | } 482 | 483 | div.uploader input { 484 | width: 190px; 485 | } 486 | 487 | div.uploader.disabled span.action { 488 | color: #aaa; 489 | } 490 | 491 | div.uploader.disabled span.filename { 492 | border-color: #ddd; 493 | color: #aaa; 494 | } 495 | /* 496 | 497 | CORE FUNCTIONALITY 498 | 499 | Not advised to edit stuff below this line 500 | ----------------------------------------------------- 501 | */ 502 | 503 | .selector, 504 | .checker, 505 | .button, 506 | .radio, 507 | .uploader { 508 | display: -moz-inline-box; 509 | display: inline-block; 510 | vertical-align: middle; 511 | zoom: 1; 512 | *display: inline; 513 | } 514 | 515 | .selector select:focus, .radio input:focus, .checker input:focus, .uploader input:focus { 516 | outline: 0; 517 | } 518 | 519 | /* Button */ 520 | 521 | div.button a, 522 | div.button button, 523 | div.button input { 524 | position: absolute; 525 | } 526 | 527 | div.button { 528 | cursor: pointer; 529 | position: relative; 530 | } 531 | 532 | div.button span { 533 | display: -moz-inline-box; 534 | display: inline-block; 535 | line-height: 1; 536 | text-align: center; 537 | } 538 | 539 | /* Select */ 540 | 541 | div.selector { 542 | position: relative; 543 | padding-left: 10px; 544 | overflow: hidden; 545 | } 546 | 547 | div.selector span { 548 | display: block; 549 | overflow: hidden; 550 | text-overflow: ellipsis; 551 | white-space: nowrap; 552 | } 553 | 554 | div.selector select { 555 | position: absolute; 556 | opacity: 0; 557 | filter: alpha(opacity=0); 558 | -moz-opacity: 0; 559 | height: 25px; 560 | border: none; 561 | background: none; 562 | } 563 | 564 | /* Checker */ 565 | 566 | div.checker { 567 | position: relative; 568 | } 569 | 570 | div.checker span { 571 | display: -moz-inline-box; 572 | display: inline-block; 573 | text-align: center; 574 | } 575 | 576 | div.checker input { 577 | opacity: 0; 578 | filter: alpha(opacity=0); 579 | -moz-opacity: 0; 580 | display: inline-block; 581 | background: none; 582 | } 583 | 584 | /* Radio */ 585 | 586 | div.radio { 587 | position: relative; 588 | } 589 | 590 | div.radio span { 591 | display: -moz-inline-box; 592 | display: inline-block; 593 | text-align: center; 594 | } 595 | 596 | div.radio input { 597 | opacity: 0; 598 | filter: alpha(opacity=0); 599 | -moz-opacity: 0; 600 | text-align: center; 601 | display: inline-block; 602 | background: none; 603 | } 604 | 605 | /* Uploader */ 606 | 607 | div.uploader { 608 | position: relative; 609 | overflow: hidden; 610 | cursor: default; 611 | } 612 | 613 | div.uploader span.action { 614 | float: left; 615 | display: inline; 616 | padding: 2px 0px; 617 | overflow: hidden; 618 | cursor: pointer; 619 | } 620 | 621 | div.uploader span.filename { 622 | padding: 0px 10px; 623 | float: left; 624 | display: block; 625 | overflow: hidden; 626 | text-overflow: ellipsis; 627 | white-space: nowrap; 628 | cursor: default; 629 | } 630 | 631 | div.uploader input { 632 | opacity: 0; 633 | filter: alpha(opacity=0); 634 | -moz-opacity: 0; 635 | position: absolute; 636 | top: 0; 637 | right: 0; 638 | bottom: 0; 639 | float: right; 640 | height: 25px; 641 | border: none; 642 | cursor: default; 643 | } 644 | --------------------------------------------------------------------------------