api.eventview.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. <?php
  2. /**
  3. * Logs viewing and searching basic class
  4. */
  5. class EventView {
  6. /**
  7. * System messages object placeholder
  8. *
  9. * @var object
  10. */
  11. protected $messages = '';
  12. /**
  13. * Default events limit to display
  14. *
  15. * @var int
  16. */
  17. protected $eventLimit = 100;
  18. /**
  19. * Contains current instance administrator login filter
  20. *
  21. * @var string
  22. */
  23. protected $filterAdmin = '';
  24. /**
  25. * Contains current instance event text filter
  26. *
  27. * @var string
  28. */
  29. protected $filterEventText = '';
  30. /**
  31. * Contains current instance date filter
  32. *
  33. * @var string
  34. */
  35. protected $filterDate = '';
  36. /**
  37. * Contains current instance ip filter
  38. *
  39. * @var string
  40. */
  41. protected $filterIp = '';
  42. /**
  43. * weblogs table database abstraction layer
  44. *
  45. * @var object
  46. */
  47. protected $weblogsDb = '';
  48. /**
  49. * Contains available render limits presets
  50. *
  51. * @var array
  52. */
  53. protected $renderLimits = array();
  54. /**
  55. * Highlight user profiles flag
  56. *
  57. * @var bool
  58. */
  59. protected $profileLinksFlag = false;
  60. /**
  61. * System caching object placeholder
  62. *
  63. * @var object
  64. */
  65. protected $cache = '';
  66. /**
  67. * Database stats caching timeout in seconds
  68. *
  69. * @var int
  70. */
  71. protected $cacheTimeout = 3600;
  72. /**
  73. * Predefined tables,routes, URLs, etc...
  74. */
  75. const TABLE_DATASOURCE = 'weblogs';
  76. const TABLE_USERREG = 'userreg';
  77. const CACHE_KEY = 'EVENTVIEWSTATS';
  78. const URL_ME = '?module=eventview';
  79. const ROUTE_LIMIT = 'onpage';
  80. const ROUTE_STATS = 'eventstats';
  81. const ROUTE_ZEN = 'zenmode';
  82. const ROUTE_DROPCACHE = 'forcecache';
  83. const ROUTE_ZENPROFILES = 'zenprofiles';
  84. const PROUTE_FILTERADMIN = 'eventadmin';
  85. const PROUTE_FILTEREVENTTEXT = 'eventsearch';
  86. const PROUTE_FILTERDATE = 'eventdate';
  87. const PROUTE_FILTERIP = 'eventip';
  88. const PROUTE_PROFILELINKS = 'profilelinks';
  89. // .-.
  90. // |_:_|
  91. // /(_Y_)\
  92. //. ( \/M\/ )
  93. // '. _.'-/'-'\-'._
  94. // ': _/.--'[[[[]'--.\_
  95. // ': /_' : |::"| : '.\
  96. // ': // ./ |oUU| \.' :\
  97. // ': _:'..' \_|___|_/ : :|
  98. // ':. .' |_[___]_| :.':\
  99. // [::\ | : | | : ; : \
  100. // '-' \/'.| |.' \ .;.' |
  101. // |\_ \ '-' : |
  102. // | \ \ .: : | |
  103. // | \ | '. : \ |
  104. // / \ :. .; |
  105. // / | | :__/ : \\
  106. // | | | \: | \ | ||
  107. // / \ : : |: / |__| /|
  108. // | : : :_/_| /'._\ '--|_\
  109. // /___.-/_|-' \ \
  110. // '-'
  111. /**
  112. * Creates new EventView instance
  113. */
  114. public function __construct() {
  115. $this->initMessages();
  116. $this->initCache();
  117. $this->setRenderLimits();
  118. $this->setLimit();
  119. $this->setFilterDate();
  120. $this->setFilterAdmin();
  121. $this->setFilterIp();
  122. $this->setFilterEventText();
  123. $this->setProfileLinks();
  124. $this->initDatabase();
  125. }
  126. /**
  127. * Inits system messages helper
  128. *
  129. * @return void
  130. */
  131. protected function initMessages() {
  132. $this->messages = new UbillingMessageHelper();
  133. }
  134. /**
  135. * Inits system cahe instance for further usage
  136. *
  137. * @return void
  138. */
  139. protected function initCache() {
  140. $this->cache = new UbillingCache();
  141. }
  142. /**
  143. * Sets events render limit if required
  144. *
  145. * @return void
  146. */
  147. protected function setLimit() {
  148. $eventLimitRaw = ubRouting::get(self::ROUTE_LIMIT, 'int') ? ubRouting::get(self::ROUTE_LIMIT, 'int') : 100;
  149. //prevent memory overusage
  150. if (isset($this->renderLimits[$eventLimitRaw])) {
  151. $this->eventLimit = $eventLimitRaw;
  152. }
  153. }
  154. /**
  155. * Sets current instance administrator filter if required
  156. *
  157. * @return void
  158. */
  159. protected function setFilterAdmin() {
  160. $this->filterAdmin = ubRouting::post(self::PROUTE_FILTERADMIN, 'mres') ? ubRouting::post(self::PROUTE_FILTERADMIN, 'mres') : '';
  161. }
  162. /**
  163. * Sets current instance event text filter if required
  164. *
  165. * @return void
  166. */
  167. protected function setFilterEventText() {
  168. $this->filterEventText = ubRouting::post(self::PROUTE_FILTEREVENTTEXT, 'mres') ? ubRouting::post(self::PROUTE_FILTEREVENTTEXT, 'mres') : '';
  169. }
  170. /**
  171. * Sets current instance date filter if required
  172. *
  173. * @return void
  174. */
  175. protected function setFilterDate() {
  176. $rawDate = ubRouting::post(self::PROUTE_FILTERDATE, 'mres') ? ubRouting::post(self::PROUTE_FILTERDATE, 'mres') : '';
  177. if (!empty($rawDate)) {
  178. if (zb_checkDate($rawDate)) {
  179. $this->filterDate = $rawDate;
  180. }
  181. }
  182. }
  183. /**
  184. * Sets current instance ip filter if required
  185. *
  186. * @return void
  187. */
  188. protected function setFilterIp() {
  189. $this->filterIp = ubRouting::post(self::PROUTE_FILTERIP, 'mres') ? ubRouting::post(self::PROUTE_FILTERIP, 'mres') : '';
  190. }
  191. /**
  192. * Sets possible render limits values
  193. *
  194. * @return void
  195. */
  196. protected function setRenderLimits() {
  197. $this->renderLimits = array(
  198. 50 => 50,
  199. 100 => 100,
  200. 200 => 200,
  201. 500 => 500,
  202. 800 => 800,
  203. 1000 => 1000
  204. );
  205. }
  206. /**
  207. * Profile links highlight flag setup
  208. *
  209. * @return void
  210. */
  211. protected function setProfileLinks() {
  212. if (ubRouting::checkPost(self::PROUTE_PROFILELINKS) or ubRouting::checkGet(self::ROUTE_ZENPROFILES)) {
  213. $this->profileLinksFlag = true;
  214. }
  215. }
  216. /**
  217. * Inits weblogs database abstraction layer
  218. *
  219. * @return void
  220. */
  221. protected function initDatabase() {
  222. $this->weblogsDb = new NyanORM(self::TABLE_DATASOURCE);
  223. }
  224. /**
  225. * Renders available event limits switching controls
  226. *
  227. * @return string
  228. */
  229. protected function renderEventLimits() {
  230. $result = '';
  231. if (!empty($this->renderLimits)) {
  232. $result .= __('On page') . ': ';
  233. foreach ($this->renderLimits as $io => $each) {
  234. $hs = '';
  235. $he = '';
  236. if ($each == $this->eventLimit) {
  237. $hs = wf_tag('b');
  238. $he = wf_tag('b', true);
  239. }
  240. $result .= $hs . wf_Link(self::URL_ME . '&' . self::ROUTE_LIMIT . '=' . $each, $each, false) . $he . ' ';
  241. }
  242. }
  243. return ($result);
  244. }
  245. /**
  246. * Preloads all events from database, applying all of required filters
  247. *
  248. * @return array
  249. */
  250. protected function getAllEventsFiltered() {
  251. $result = array();
  252. $this->weblogsDb->orderBy('id', 'DESC'); //from newest to oldest
  253. //
  254. //date filters ignores default render limits
  255. if (!empty($this->filterDate)) {
  256. $this->eventLimit = 0; //show all of events by selected date
  257. $this->weblogsDb->where('date', 'LIKE', $this->filterDate . '%');
  258. }
  259. //apply administrator filter
  260. if (!empty($this->filterAdmin)) {
  261. $this->weblogsDb->where('admin', '=', $this->filterAdmin);
  262. }
  263. //apply ip filter
  264. if (!empty($this->filterIp)) {
  265. $this->weblogsDb->where('ip', 'LIKE', '%' . $this->filterIp . '%');
  266. }
  267. //apply event-text filter
  268. if (!empty($this->filterEventText)) {
  269. $this->weblogsDb->where('event', 'LIKE', '%' . $this->filterEventText . '%');
  270. }
  271. //setting query limits
  272. if (!empty($this->eventLimit)) {
  273. $this->weblogsDb->limit($this->eventLimit);
  274. }
  275. //getting events from database
  276. $result = $this->weblogsDb->getAll();
  277. return ($result);
  278. }
  279. /**
  280. * Returns selector of administrator logins
  281. *
  282. * @return string
  283. */
  284. protected function adminSelector() {
  285. $all = rcms_scandir(USERS_PATH);
  286. $allLogins = array('' => '-');
  287. if (!empty($all)) {
  288. foreach ($all as $each) {
  289. $allLogins[$each] = $each;
  290. }
  291. }
  292. $allLogins['external'] = 'external';
  293. $allLogins['guest'] = 'guest';
  294. $result = wf_Selector(self::PROUTE_FILTERADMIN, $allLogins, __('Administrator'), $this->filterAdmin, false);
  295. return ($result);
  296. }
  297. /**
  298. * Renders form for setting event filters
  299. *
  300. * @return string
  301. */
  302. protected function renderSearchForm() {
  303. $result = '';
  304. $inputs = __('By date') . ': ';
  305. $inputs .= wf_DatePickerPreset(self::PROUTE_FILTERDATE, $this->filterDate, true) . ' '; //date filter
  306. $inputs .= $this->adminSelector() . ' '; //administrator filter
  307. $inputs .= wf_TextInput(self::PROUTE_FILTERIP, __('IP'), $this->filterIp, false, '15', 'ip') . ' ';
  308. $inputs .= wf_CheckInput(self::PROUTE_PROFILELINKS, __('Highlight profiles'), false, $this->profileLinksFlag) . ' '; // profile links checkbox
  309. $inputs .= wf_TextInput(self::PROUTE_FILTEREVENTTEXT, __('Event'), $this->filterEventText, false, 30) . ' '; //event text mask
  310. $inputs .= wf_Submit(__('Search'));
  311. $result = wf_Form('', 'POST', $inputs, 'glamour');
  312. return ($result);
  313. }
  314. /**
  315. * Renders weblogs search results
  316. *
  317. * @return string
  318. */
  319. public function renderEventsReport() {
  320. $result = '';
  321. $zenMode = ubRouting::checkGet(self::ROUTE_ZEN) ? true : false;
  322. if ($zenMode) {
  323. $this->eventLimit = 50;
  324. } else {
  325. $result .= $this->renderEventLimits();
  326. $result .= wf_delimiter(0);
  327. $result .= $this->renderSearchForm();
  328. }
  329. $allEvents = $this->getAllEventsFiltered();
  330. if (!empty($allEvents)) {
  331. $tablecells = wf_TableCell(__('ID'));
  332. $tablecells .= wf_TableCell(__('Date'));
  333. $tablecells .= wf_TableCell(__('Admin'));
  334. $tablecells .= wf_TableCell(__('IP'));
  335. $tablecells .= wf_TableCell(__('Event'));
  336. $tablerows = wf_TableRow($tablecells, 'row1');
  337. foreach ($allEvents as $io => $eachevent) {
  338. $event = htmlspecialchars($eachevent['event']);
  339. if ($this->profileLinksFlag) {
  340. if (preg_match('!\((.*?)\)!si', $event, $tmpLoginMatches)) {
  341. @$loginExtracted = $tmpLoginMatches[1];
  342. if (!empty($loginExtracted)) {
  343. if (!ispos($event, '((') and !ispos($event, 'SWITCH')) { // ignore UKV user id-s and switch locations
  344. $userProfileLink = wf_Link('?module=userprofile&username=' . $loginExtracted, web_profile_icon() . ' ' . $loginExtracted);
  345. $event = str_replace($loginExtracted, $userProfileLink, $event);
  346. }
  347. }
  348. }
  349. }
  350. $tablecells = wf_TableCell($eachevent['id']);
  351. $tablecells .= wf_TableCell($eachevent['date']);
  352. $tablecells .= wf_TableCell($eachevent['admin']);
  353. $tablecells .= wf_TableCell($eachevent['ip']);
  354. $tablecells .= wf_TableCell($event);
  355. $tablerows .= wf_TableRow($tablecells, 'row5');
  356. }
  357. $result .= wf_TableBody($tablerows, '100%', 0, 'sortable');
  358. } else {
  359. $result .= $this->messages->getStyledMessage(__('Nothing found'), 'info');
  360. }
  361. return ($result);
  362. }
  363. /**
  364. * Renders module controls panel
  365. *
  366. * @return string
  367. */
  368. public function renderControls() {
  369. $result = '';
  370. $result .= wf_Link(self::URL_ME, wf_img('skins/log_icon_small.png', __('Events')) . ' ' . __('Events'), false, 'ubButton') . ' ';
  371. $result .= wf_Link(self::URL_ME . '&' . self::ROUTE_STATS . '=true', web_icon_charts() . ' ' . __('Stats'), false, 'ubButton') . ' ';
  372. $result .= wf_Link(self::URL_ME . '&' . self::ROUTE_ZEN . '=true', wf_img('skins/zen.png', __('Zen')) . ' ' . __('Zen'), false, 'ubButton') . ' ';
  373. return ($result);
  374. }
  375. /**
  376. * Returns database stats and performs it caching
  377. *
  378. * @return array
  379. */
  380. protected function getEventStats() {
  381. $result = array();
  382. $cmonth = date("Y-m-");
  383. $cday = date("d");
  384. //force cache cleanup
  385. if (ubRouting::checkGet(self::ROUTE_DROPCACHE)) {
  386. $this->cache->delete(self::CACHE_KEY);
  387. ubRouting::nav(self::URL_ME . '&' . self::ROUTE_STATS . '=true');
  388. }
  389. $cachedStats = $this->cache->get(self::CACHE_KEY, $this->cacheTimeout);
  390. //is cache expired?
  391. if (empty($cachedStats)) {
  392. $rawData = array();
  393. /**
  394. * Using direct MySQL queries here instead of NyanORM - due memory economy purposes and preventing
  395. * of multiple arrays reordering overheads. And laziness of course :P
  396. */
  397. $reg_q = "SELECT COUNT(`id`) from `userreg` WHERE `date` LIKE '" . $cmonth . "%'";
  398. $regc = simple_query($reg_q);
  399. $regc = $regc['COUNT(`id`)'];
  400. $mac_q = "SELECT COUNT(`id`) from `weblogs` WHERE `date` LIKE '" . $cmonth . "%' AND `event` LIKE 'CHANGE MultiNetHostMac%'";
  401. $macc = simple_query($mac_q);
  402. $macc = $macc['COUNT(`id`)'];
  403. $events_q = "SELECT COUNT(`id`) from `weblogs` WHERE `date` LIKE '" . $cmonth . "%'";
  404. $eventsc = simple_query($events_q);
  405. $eventsc = $eventsc['COUNT(`id`)'];
  406. $switch_q = "SELECT COUNT(`id`) from `weblogs` WHERE `date` LIKE '" . $cmonth . "%' AND `event` LIKE 'SWITCH ADD%'";
  407. $switchc = simple_query($switch_q);
  408. $switchc = $switchc['COUNT(`id`)'];
  409. $switchch_q = "SELECT COUNT(`id`) from `weblogs` WHERE `date` LIKE '" . $cmonth . "%' AND `event` LIKE 'SWITCH REPLACE%'";
  410. $switchcch = simple_query($switchch_q);
  411. $switchcch = $switchcch['COUNT(`id`)'];
  412. $credit_q = "SELECT COUNT(`id`) from `weblogs` WHERE `date` LIKE '" . $cmonth . "%' AND `event` LIKE 'CHANGE Credit%' AND `event` NOT LIKE '%CreditExpire%'";
  413. $creditc = simple_query($credit_q);
  414. $creditc = $creditc['COUNT(`id`)'];
  415. $pay_q = "SELECT COUNT(`id`) from `payments` WHERE `date` LIKE '" . $cmonth . "%' AND `summ`>0";
  416. $payc = simple_query($pay_q);
  417. $payc = $payc['COUNT(`id`)'];
  418. $tarch_q = "SELECT COUNT(`id`) from`weblogs` WHERE `date` LIKE '" . $cmonth . "%' AND `event` LIKE 'CHANGE TariffNM%'";
  419. $tarchc = simple_query($tarch_q);
  420. $tarchc = $tarchc['COUNT(`id`)'];
  421. $current_monthlog = "logs_" . date("m") . "_" . date("Y") . "";
  422. //is current month logs table exists?
  423. if (zb_CheckTableExists($current_monthlog)) {
  424. $stg_q = "SELECT COUNT(`unid`) from `logs_" . date("m") . "_" . date("Y") . "`";
  425. $stgc = simple_query($stg_q);
  426. $stgc = $stgc['COUNT(`unid`)'];
  427. } else {
  428. $stgc = 0;
  429. }
  430. $sms_q = "SELECT COUNT(`id`) from`weblogs` WHERE `date` LIKE '" . $cmonth . "%' AND `event` LIKE 'USMS SEND SMS %'";
  431. $smsc = simple_query($sms_q);
  432. $smsc = $smsc['COUNT(`id`)'];
  433. $rawData['regc'] = $regc;
  434. $rawData['macc'] = $macc;
  435. $rawData['eventsc'] = $eventsc;
  436. $rawData['switchc'] = $switchc;
  437. $rawData['switchcch'] = $switchcch;
  438. $rawData['creditc'] = $creditc;
  439. $rawData['payc'] = $payc;
  440. $rawData['tarchc'] = $tarchc;
  441. $rawData['stgc'] = $stgc;
  442. $rawData['smsc'] = $smsc;
  443. //put new data to cache
  444. $result = $rawData;
  445. $this->cache->set(self::CACHE_KEY, $rawData, $this->cacheTimeout);
  446. } else {
  447. //just returning cached data
  448. $result = $cachedStats;
  449. }
  450. return ($result);
  451. }
  452. /**
  453. * Renders event stats by current month
  454. *
  455. * @return string
  456. */
  457. public function renderEventStats() {
  458. $result = '';
  459. $eventStats = $this->getEventStats();
  460. if (!empty($eventStats)) {
  461. $cmonth = date("Y-m-");
  462. $cday = date("d");
  463. // workdays fix
  464. $weeks = ($cday / 7);
  465. $weeks = intval($weeks);
  466. if ($weeks >= 1) {
  467. $cday = $cday - (2 * $weeks);
  468. }
  469. $cells = wf_TableCell(__('What done') . '?');
  470. $cells .= wf_TableCell(__('Current month'));
  471. $cells .= wf_TableCell(__('Average per day'));
  472. $rows = wf_TableRow($cells, 'row1');
  473. $cells = wf_TableCell(__('Current month signups'));
  474. $cells .= wf_TableCell($eventStats['regc']);
  475. $cells .= wf_TableCell(round($eventStats['regc'] / $cday, 2));
  476. $rows .= wf_TableRow($cells, 'row3');
  477. $cells = wf_TableCell(__('MAC changes'));
  478. $cells .= wf_TableCell(($eventStats['macc'] - $eventStats['regc']));
  479. $cells .= wf_TableCell(round((($eventStats['macc'] - $eventStats['regc']) / $cday), 2));
  480. $rows .= wf_TableRow($cells, 'row3');
  481. $cells = wf_TableCell(__('Switches added'));
  482. $cells .= wf_TableCell(($eventStats['switchc']));
  483. $cells .= wf_TableCell(round(($eventStats['switchc'] / $cday), 2));
  484. $rows .= wf_TableRow($cells, 'row3');
  485. $cells = wf_TableCell(__('Switches replaced'));
  486. $cells .= wf_TableCell(($eventStats['switchcch']));
  487. $cells .= wf_TableCell(round(($eventStats['switchcch'] / $cday), 2));
  488. $rows .= wf_TableRow($cells, 'row3');
  489. $cells = wf_TableCell(__('Credits set'));
  490. $cells .= wf_TableCell($eventStats['creditc']);
  491. $cells .= wf_TableCell(round(($eventStats['creditc'] / $cday), 2));
  492. $rows .= wf_TableRow($cells, 'row3');
  493. $cells = wf_TableCell(__('Payments processed'));
  494. $cells .= wf_TableCell($eventStats['payc']);
  495. $cells .= wf_TableCell(round(($eventStats['payc'] / $cday), 2));
  496. $rows .= wf_TableRow($cells, 'row3');
  497. $cells = wf_TableCell(__('Planned changes to tariffs'));
  498. $cells .= wf_TableCell($eventStats['tarchc']);
  499. $cells .= wf_TableCell(round(($eventStats['tarchc'] / $cday), 2));
  500. $rows .= wf_TableRow($cells, 'row3');
  501. $cells = wf_TableCell(__('SMS sended'));
  502. $cells .= wf_TableCell($eventStats['smsc']);
  503. $cells .= wf_TableCell(round(($eventStats['smsc'] / $cday), 2));
  504. $rows .= wf_TableRow($cells, 'row3');
  505. $cells = wf_TableCell(__('External billing events'));
  506. $cells .= wf_TableCell($eventStats['eventsc']);
  507. $cells .= wf_TableCell(round(($eventStats['eventsc'] / $cday), 2));
  508. $rows .= wf_TableRow($cells, 'row3');
  509. if ($eventStats['stgc'] != 0) {
  510. $cells = wf_TableCell(__('Internal billing events'));
  511. $cells .= wf_TableCell($eventStats['stgc']);
  512. $cells .= wf_TableCell(round(($eventStats['stgc'] / $cday), 2));
  513. $rows .= wf_TableRow($cells, 'row3');
  514. }
  515. $result = wf_TableBody($rows, '100%', 0);
  516. $cacheCleanRoute = self::URL_ME . '&' . self::ROUTE_STATS . '=true' . '&' . self::ROUTE_DROPCACHE . '=true';
  517. $result .= __('From cache') . ' ' . wf_Link($cacheCleanRoute, wf_img('skins/icon_cleanup.png', __('Renew')));
  518. } else {
  519. $result .= $this->messages->getStyledMessage(__('Something went wrong'), 'error');
  520. }
  521. return ($result);
  522. }
  523. }