popularity.php 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. <?php
  2. namespace Grav\Plugin;
  3. use Grav\Common\Config\Config;
  4. use Grav\Common\Grav;
  5. use Grav\Common\Plugins;
  6. use Grav\Common\Themes;
  7. use Grav\Common\Page\Page;
  8. use Grav\Common\Data;
  9. use Grav\Common\GravTrait;
  10. class Popularity
  11. {
  12. use GravTrait;
  13. /** @var Config */
  14. protected $config;
  15. protected $data_path;
  16. protected $daily_file;
  17. protected $monthly_file;
  18. protected $totals_file;
  19. protected $visitors_file;
  20. protected $daily_data;
  21. protected $monthly_data;
  22. protected $totals_data;
  23. protected $visitors_data;
  24. const DAILY_FORMAT = 'd-m-Y';
  25. const MONTHLY_FORMAT = 'm-Y';
  26. const DAILY_FILE = 'daily.json';
  27. const MONTHLY_FILE = 'monthly.json';
  28. const TOTALS_FILE = 'totals.json';
  29. const VISITORS_FILE = 'visitors.json';
  30. public function __construct()
  31. {
  32. $this->config = self::getGrav()['config'];
  33. $this->data_path = self::$grav['locator']->findResource('log://popularity', true, true);
  34. $this->daily_file = $this->data_path.'/'.self::DAILY_FILE;
  35. $this->monthly_file = $this->data_path.'/'.self::MONTHLY_FILE;
  36. $this->totals_file = $this->data_path.'/'.self::TOTALS_FILE;
  37. $this->visitors_file = $this->data_path.'/'.self::VISITORS_FILE;
  38. }
  39. public function trackHit()
  40. {
  41. // Don't track bot or crawler requests
  42. if (!self::getGrav()['browser']->isHuman()) {
  43. return;
  44. }
  45. /** @var Page $page */
  46. $page = self::getGrav()['page'];
  47. $relative_url = str_replace(self::getGrav()['base_url_relative'], '', $page->url());
  48. // Don't track error pages or pages that have no route
  49. if ($page->template() == 'error' || !$page->route()) {
  50. return;
  51. }
  52. // Make sure no 'widcard-style' ignore matches this url
  53. foreach ((array) $this->config->get('plugins.admin.popularity.ignore') as $ignore) {
  54. if (fnmatch($ignore, $relative_url)) {
  55. return;
  56. }
  57. }
  58. // initial creation if it doesn't exist
  59. if (!file_exists($this->data_path)) {
  60. mkdir($this->data_path);
  61. $this->flushPopularity();
  62. }
  63. // Update the data we want to track
  64. $this->updateDaily();
  65. $this->updateMonthly();
  66. $this->updateTotals($page->route());
  67. $this->updateVisitors(self::getGrav()['uri']->ip());
  68. }
  69. protected function updateDaily()
  70. {
  71. if (!$this->daily_data) {
  72. $this->daily_data = $this->getData($this->daily_file);
  73. }
  74. $day_month_year = date(self::DAILY_FORMAT);
  75. // get the daily access count
  76. if (array_key_exists($day_month_year, $this->daily_data)) {
  77. $this->daily_data[$day_month_year] = intval($this->daily_data[$day_month_year]) + 1;
  78. } else {
  79. $this->daily_data[$day_month_year] = 1;
  80. }
  81. // keep correct number as set by history
  82. $count = intval($this->config->get('plugins.admin.popularity.history.daily', 30));
  83. $total = count($this->daily_data);
  84. if ($total > $count) {
  85. $this->daily_data = array_slice($this->daily_data, -$count, $count, true);
  86. }
  87. file_put_contents($this->daily_file, json_encode($this->daily_data));
  88. }
  89. public function getDailyChartData()
  90. {
  91. if (!$this->daily_data) {
  92. $this->daily_data = $this->getData($this->daily_file);
  93. }
  94. $limit = intval($this->config->get('plugins.admin.popularity.dashboard.days_of_stats', 7));
  95. $chart_data = array_slice($this->daily_data, -$limit, $limit);
  96. $labels = array();
  97. $data = array();
  98. foreach ($chart_data as $date => $count) {
  99. $labels[] = self::getGrav()['grav']['admin']->translate(['PLUGIN_ADMIN.' . strtoupper(date('D', strtotime($date)))]);
  100. $data[] = $count;
  101. }
  102. return array('labels' => json_encode($labels), 'data' => json_encode($data));
  103. }
  104. public function getDailyTotal()
  105. {
  106. if (!$this->daily_data) {
  107. $this->daily_data = $this->getData($this->daily_file);
  108. }
  109. if (isset($this->daily_data[date(self::DAILY_FORMAT)])) {
  110. return $this->daily_data[date(self::DAILY_FORMAT)];
  111. } else {
  112. return 0;
  113. }
  114. }
  115. public function getWeeklyTotal()
  116. {
  117. if (!$this->daily_data) {
  118. $this->daily_data = $this->getData($this->daily_file);
  119. }
  120. $day = 0;
  121. $total = 0;
  122. foreach (array_reverse($this->daily_data) as $daily) {
  123. $total += $daily;
  124. $day++;
  125. if ($day == 7) break;
  126. }
  127. return $total;
  128. }
  129. public function getMonthlyTotal()
  130. {
  131. if (!$this->monthly_data) {
  132. $this->monthly_data = $this->getData($this->monthly_file);
  133. }
  134. if (isset($this->monthly_data[date(self::MONTHLY_FORMAT)])) {
  135. return $this->monthly_data[date(self::MONTHLY_FORMAT)];
  136. } else {
  137. return 0;
  138. }
  139. }
  140. protected function updateMonthly()
  141. {
  142. if (!$this->monthly_data) {
  143. $this->monthly_data = $this->getData($this->monthly_file);
  144. }
  145. $month_year = date(self::MONTHLY_FORMAT);
  146. // get the monthly access count
  147. if (array_key_exists($month_year, $this->monthly_data)) {
  148. $this->monthly_data[$month_year] = intval($this->monthly_data[$month_year]) + 1;
  149. } else {
  150. $this->monthly_data[$month_year] = 1;
  151. }
  152. // keep correct number as set by history
  153. $count = intval($this->config->get('plugins.admin.popularity.history.monthly', 12));
  154. $total = count($this->monthly_data);
  155. $this->monthly_data = array_slice($this->monthly_data, $total - $count, $count);
  156. file_put_contents($this->monthly_file, json_encode($this->monthly_data));
  157. }
  158. protected function getMonthyChartData()
  159. {
  160. if (!$this->monthly_data) {
  161. $this->monthly_data = $this->getData($this->monthly_file);
  162. }
  163. $labels = array();
  164. $data = array();
  165. foreach ($this->monthly_data as $date => $count) {
  166. $labels[] = date('M', strtotime($date));
  167. $data[] = $count;
  168. }
  169. return array('labels' => $labels, 'data' => $data);
  170. }
  171. protected function updateTotals($url)
  172. {
  173. if (!$this->totals_data) {
  174. $this->totals_data = $this->getData($this->totals_file);
  175. }
  176. // get the totals for this url
  177. if (array_key_exists($url, $this->totals_data)) {
  178. $this->totals_data[$url] = intval($this->totals_data[$url]) + 1;
  179. } else {
  180. $this->totals_data[$url] = 1;
  181. }
  182. file_put_contents($this->totals_file, json_encode($this->totals_data));
  183. }
  184. protected function updateVisitors($ip)
  185. {
  186. if (!$this->visitors_data) {
  187. $this->visitors_data = $this->getData($this->visitors_file);
  188. }
  189. // update with current timestamp
  190. $this->visitors_data[$ip] = time();
  191. $visitors = $this->visitors_data;
  192. arsort($visitors);
  193. $count = intval($this->config->get('plugins.admin.popularity.history.visitors', 20));
  194. $this->visitors_data = array_slice($visitors, 0, $count, true);
  195. file_put_contents($this->visitors_file, json_encode($this->visitors_data));
  196. }
  197. protected function getData($path)
  198. {
  199. return (array) @json_decode(file_get_contents($path), true);
  200. }
  201. public function flushPopularity()
  202. {
  203. file_put_contents($this->daily_file, array());
  204. file_put_contents($this->monthly_file, array());
  205. file_put_contents($this->totals_file, array());
  206. file_put_contents($this->visitors_file, array());
  207. }
  208. }