Advertisement
Guest User

Untitled

a guest
Jan 22nd, 2013
173
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.37 KB | None | 0 0
  1. <?php
  2. /*
  3. * Copyright (c) 2011 University of Macau
  4. *
  5. * Licensed under the Educational Community License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License. You may
  7. * obtain a copy of the License at
  8. *
  9. * http://www.osedu.org/licenses/ECL-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing,
  12. * software distributed under the License is distributed on an "AS IS"
  13. * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
  14. * or implied. See the License for the specific language governing
  15. * permissions and limitations under the License.
  16. */
  17.  
  18. /* File name: AccessLog.body.php
  19. * Purpose: Implementation of AccessLog class
  20. * Author: Aleksandar Bojinovic, Peter Kin-Fong Fong
  21. */
  22.  
  23.  
  24. class AccessLog extends SpecialPage {
  25.  
  26. const DEFAULT_LOGS_PER_PAGE = 20;
  27. const DAY_IN_SECS = 86400;
  28.  
  29. private $logsPerPageOptions = array(10, 20, 50, 100, 250, 500, 1000, 2000);
  30. private $logsPerPage = self::DEFAULT_LOGS_PER_PAGE;
  31. private $fUser, $fAccess, $fNamespaces, $fDays, $fPage, $fAnons;
  32. private $totalResults, $totalPages, $page = 1;
  33.  
  34. function AccessLog() {
  35. SpecialPage::SpecialPage('AccessLog', 'protect');
  36. wfLoadExtensionMessages('AccessLog');
  37. }
  38.  
  39. /**
  40. * Generate the HTML fragment for disclamar on top of the log page.
  41. */
  42. private function getLogDisclaimer() {
  43. global $wgScriptPath, $wgEmergencyContact;
  44.  
  45. $out = Xml::openElement( 'table', array( 'border' => '0', 'cellpadding' => '0', 'cellspacing' => '0', 'width' => '100%' ) );
  46. $out .= Xml::openElement( 'tr' );
  47.  
  48. $out .= Xml::openElement( 'td', array( 'width' => '32px' ) );
  49. $out .= Xml::element( 'img', array ( 'src' => $wgScriptPath .'/extensions/UMEduWiki/images/log.png' ) );
  50. $out .= Xml::closeElement( 'td' );
  51.  
  52. $out .= Xml::openElement( 'td', array( 'bgcolor' => '#F5F5F5' ) );
  53. $out .= Xml::openElement( 'p', array( 'style' => 'margin-left: 5px; font-style: italic' ) );
  54. $out .= "This special page lists the access log. For any questions or troubleshooting, please <a href=\"mailto:$wgEmergencyContact\">contact</a> the administrator.";
  55. $out .= Xml::closeElement( 'p' );
  56. $out .= Xml::closeElement( 'td' );
  57.  
  58. $out .= Xml::closeElement( 'tr' );
  59. $out .= Xml::closeElement( 'table' );
  60.  
  61. return $out;
  62. }
  63.  
  64. /**
  65. * Generate the HTML fragment for control forms.
  66. */
  67. private function getLogControler() {
  68. global $wgScript, $wgTitle;
  69.  
  70. $dbr = wfGetDB( DB_SLAVE );
  71.  
  72. // Logs per page setting
  73. $out = Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) );
  74.  
  75. $out .= Html::hidden( 'title', $this->getTitle()->getPrefixedText() );
  76.  
  77. $out .= Xml::openElement( 'table', array( 'border' => '0', 'cellpadding' => '2', 'cellspacing' => '0' ) );
  78. $out .= Xml::openElement( 'tr' );
  79. $out .= Xml::openElement( 'td' );
  80. $out .= Xml::openElement( 'p' );
  81.  
  82. $out .= '<b>Display</b> ';
  83.  
  84. $out .= Xml::openElement( 'select', array( 'id' => 'limit', 'name' => 'limit' ) );
  85. {
  86. foreach ($this->logsPerPageOptions as $n) {
  87. $out .= Xml::option( $n, $n, $this->logsPerPage == $n );
  88. }
  89. }
  90. $out .= Xml::closeElement( 'select' );
  91.  
  92. $out .= ' logs per page ';
  93. $out .= Xml::submitButton( 'Update' );
  94.  
  95. $out .= Xml::closeElement( 'p' );
  96. $out .= Xml::closeElement( 'td' );
  97. $out .= Xml::closeElement( 'tr' );
  98. $out .= Xml::closeElement( 'table' );
  99.  
  100. // Filter setting
  101. $out .= Xml::openElement( 'table', array( 'border' => '0', 'cellpadding' => '2', 'cellspacing' => '0' ) );
  102. $out .= Xml::openElement( 'tr' );
  103. $out .= Xml::openElement( 'td', array( 'valign' => 'middle' ) );
  104. $out .= Xml::openElement( 'p' );
  105. $out .= '<b>Filter</b> ';
  106.  
  107. $out .= 'user: ';
  108. $out .= Xml::input('user', 20, $this->fUser);
  109.  
  110. $out .= '&nbsp;&nbsp; access:';
  111. $out .= Xml::openElement( 'select', array('size' => '1', 'name' => 'access' ) );
  112. $out .= Xml::option( '*', 'both', $this->fAccess == 'both' || $this->fAccess == '' );
  113. $out .= Xml::option( 'view', 'view', $this->fAccess == 'view' );
  114. $out .= Xml::option( 'edit', 'edit', $this->fAccess == 'edit' );
  115. $out .= Xml::closeElement( 'select' );
  116.  
  117. $out .= '&nbsp;&nbsp; page: ';
  118. $out .= Xml::input('pageviewed', 20, $this->fPage);
  119.  
  120. $out .= '&nbsp;&nbsp; namespace: ';
  121. $out .= Xml::openElement( 'select', array('size' => '1', 'name' => 'ns' ) );
  122. $out .= Xml::option( '*', 'all' );
  123. $namespaces = $this->getAllNamespaces();
  124. foreach( $namespaces as $ns => $name ) {
  125. $out .= Xml::option( $name, $name, $this->fNamespaces == $name );
  126. }
  127. $out .= Xml::closeElement( 'select' );
  128.  
  129.  
  130. $out .= '&nbsp;&nbsp; in last: ';
  131. $out .= Xml::input('days', 2, $this->fDays);
  132. $out .= ' day(s)<br>';
  133. $out .= Xml::check( 'anons', $this->fAnons);
  134. $out .= ' Display only anonymous users?';
  135. $out .= Xml::submitButton( 'Filter' );
  136.  
  137. $out .= Xml::closeElement( 'p' );
  138. $out .= Xml::closeElement( 'td' );
  139. $out .= Xml::closeElement( 'tr' );
  140. $out .= Xml::closeElement( 'table' );
  141.  
  142. $out .= Xml::closeElement( 'form' );
  143.  
  144. // Purge old logs
  145. $out .= Xml::openElement( 'form', array( 'method' => 'post', 'action' => $wgScript . '?title=' . $this->getTitle()->getPrefixedText() . '&action=purge&value=ask' ) );
  146. $out .= Xml::openElement( 'table', array( 'border' => '0', 'cellpadding' => '2', 'cellspacing' => '0' ) );
  147. $out .= Xml::openElement( 'tr' );
  148. $out .= Xml::openElement( 'td' );
  149.  
  150.  
  151. $out .= '<b>Purge log </b> older than ';
  152. $out .= Xml::input('pDays', 2);
  153. $out .= ' day(s) ';
  154. $out .= Xml::submitButton( 'Purge', array('name' => 'submit') );
  155.  
  156. $out .= Xml::closeElement( 'td' );
  157. $out .= Xml::closeElement( 'tr' );
  158. $out .= Xml::closeElement( 'table' );
  159.  
  160. $out .= Xml::closeElement( 'form' );
  161. $out .= "<p style=\"color: #999999\"><i><b> Note: </b>Click 'Filter' to apply filter settings to the log display.</i></p>";
  162.  
  163.  
  164. $out = Xml::fieldset( 'Log display control', $out );
  165.  
  166. return $out;
  167. }
  168.  
  169. /**
  170. * Ask for confirmation before purging the old log entries. Return a
  171. * page with a choice of "Yes" or "No".
  172. *
  173. * @param $pDays Number of days before execution time
  174. */
  175. private function displayAsk( $pDays ) {
  176. global $wgScriptPath, $wgScript;
  177.  
  178. $out = Xml::openElement( 'table', array( 'border' => '0', 'cellpadding' => '2', 'cellspacing' => '0' ) );
  179. $out .= Xml::openElement( 'tr' );
  180.  
  181. $out .= Xml::openElement( 'td', array( 'valign' => 'top' ) );
  182. $out .= Xml::element( 'img', array ( 'src' => $wgScriptPath .'/extensions/UMEduWiki/images/sure.png' ) );
  183. $out .= Xml::closeElement( 'td' );
  184.  
  185. $out .= Xml::openElement( 'td', array( 'valign' => 'top' ) );
  186. $out .= Xml::openElement( 'form', array( 'method' => 'post', 'action' => $wgScript . '?title=' . $this->getTitle()->getPrefixedText() . '&action=purge&value=confirmed' ) );
  187.  
  188. if ( empty($pDays) ) {
  189. $out .= "<p><b><i>Are you sure you want to purge the entire log?</i></b></p>";
  190. } else {
  191. $out .= "<p><b><i>Are you sure you want to purge entries older than $pDays day(s)?</i></b></p>";
  192. }
  193.  
  194. $out .= Xml::element( 'input' , array('type' => 'hidden', 'name' => 'pDays', 'value'=> $pDays) );
  195.  
  196. $out .= Xml::openElement( 'p' );
  197. $out .= Xml::submitButton( 'Yes', array('name' => 'submit') );
  198. $out .= Xml::submitButton( 'No', array('name' => 'submit') );
  199. $out .= Xml::closeElement( 'p' );
  200.  
  201. $out .= Xml::closeElement( 'form' );
  202. $out .= Xml::closeElement( 'td' );
  203. $out .= Xml::closeElement( 'tr' );
  204. $out .= Xml::closeElement( 'table' );
  205.  
  206. return $out;
  207. }
  208.  
  209. /**
  210. * Format the log entries into a table.
  211. *
  212. * @param $result An array with number of entries as $result[0] and the log entries in $result[1]
  213. * @return An HTML fragment that contains formatted log.
  214. */
  215. private function getLog( $result ) {
  216. global $wgScript;
  217.  
  218. $out = '<fieldset style="padding: 2"><legend><b>Log</b></legend>';
  219.  
  220. // Render navigation links (previous / next page)
  221. if ($this->page > 1) {
  222. $prev = $this->page - 1;
  223. $nav = '<span style="color: #C0C0C0"><a href="' . $wgScript .
  224. '?title=' . $this->getTitle()->getPrefixedText() .
  225. '&page=' . $prev .
  226. '&limit=' . $this->logsPerPage .
  227. ( empty($this->fUser) ? '' : ('&user=' . $this->fUser) ) .
  228. ( empty($this->fAccess) ? '' : ('&access=' . $this->fAccess) ) .
  229. ( empty($this->fNamespaces) ? '' : ('&ns=' . $this->fNamespaces) ) .
  230. ( ($this->fDays == null) ? '' : ('&days=' . $this->fDays) ) .
  231. '">Previous ' . $this->logsPerPage . ' results</a> | ';
  232. } else {
  233. $nav = '<span style="color: #C0C0C0">Previous ' . $this->logsPerPage . ' results | ';
  234. }
  235.  
  236. if ($this->page < $this->totalPages){
  237. $next = $this->page + 1;
  238. $nav .= '<a href="' . $wgScript .
  239. '?title=' . $this->getTitle()->getPrefixedText() .
  240. '&page=' . $next .
  241. '&limit=' . $this->logsPerPage .
  242. ( empty($this->fUser) ? '' : ('&user=' . $this->fUser) ) .
  243. ( empty($this->fAccess) ? '' : ('&access=' . $this->fAccess) ) .
  244. ( empty($this->fNamespaces) ? '' : ('&ns=' . $this->fNamespaces) ) .
  245. ( ($this->fDays == null) ? '' : ('&days=' . $this->fDays) ) .
  246. '">Next ' . $this->logsPerPage . ' results</a> of a total <b>' . $this->totalResults . '</b> results</span>';
  247. } else {
  248. $nav .= 'Next ' . $this->logsPerPage . ' results of a total <b>' . $this->totalResults .'</b> results</span>';
  249. }
  250.  
  251. $out .= $nav;
  252.  
  253. // Output log entries
  254. $out .= '<table border="0" cellpadding="2" cellspacing="0">';
  255.  
  256. if ($result[0] == 0) {
  257. $out .= '<p style="font-size: small">There are no entries in the log.</p>';
  258. }
  259.  
  260. foreach ($result[1] as $row) {
  261.  
  262. $time = wfTimestamp( TS_DB, $row->tw_log_timestamp );
  263. $user = $row->tw_log_username;
  264. $action = $row->tw_log_action;
  265. $title = Title::newFromText($row->tw_log_title);
  266.  
  267. $out .= '<tr>
  268. <td><span style="color: #808080"><b>' . $time . '</b></font></td>
  269. <td><a href="../' . $wgScript . '?title=User:' . $user . '">' . $user . '</a><i> has ' . $action . 'ed</i> <a href="' . $title->getLocalURL() .'">' . $title->getPrefixedText() . '</a></td>
  270. </tr>';
  271. }
  272.  
  273. $out .= '</table>';
  274.  
  275. $out .= $nav;
  276.  
  277. $out .= '</fieldset>';
  278.  
  279. return $out;
  280.  
  281. }
  282.  
  283. /**
  284. * Get all namespaces' name into an array.
  285. */
  286. private function getAllNamespaces() {
  287. global $wgContLang;
  288.  
  289. $arr = array();
  290. foreach( $wgContLang->getNamespaces() as $ns => $name ) {
  291. if ( $ns != 0 )
  292. $arr[$ns] = $name;
  293. else
  294. $arr[$ns] = 'Main';
  295. }
  296.  
  297. return $arr;
  298. }
  299.  
  300. /**
  301. * Check if a user is in a designated group.
  302. *
  303. * @param $userGroups Groups a user is belongs to
  304. * @param $designatedGroup The groups to check (string or array)
  305. */
  306. private function checkUser($userGroups, $designatedGroups) {
  307. if (!is_array($designatedGroups)) {
  308. $designatedGroups = array($designatedGroups);
  309. }
  310.  
  311. foreach ($userGroups as $userGroup) {
  312. foreach ($designatedGroups as $designatedGroup) {
  313. if (strcmp($userGroup, $designatedGroup) == 0) {
  314. return true;
  315. }
  316. }
  317. }
  318.  
  319. return false;
  320. }
  321.  
  322. /**
  323. * Returns true if the request contains POST data
  324. */
  325. private function isPosted() {
  326.  
  327. if (isset($_POST['submit'])) {
  328. return true;
  329. }
  330.  
  331. return false;
  332. }
  333.  
  334. /**
  335. * Main entry point of the special page.
  336. */
  337. function execute( $par ) {
  338. global $wgRequest, $wgOut, $wgUser, $wgAccessControlPanelAllowedGroup;
  339.  
  340. $this->setHeaders();
  341.  
  342. // Check user privileges
  343. $wgUser->load();
  344. $userGroups = $wgUser->getGroups();
  345.  
  346. if ( isset($wgAccessControlPanelAllowedGroup) ) {
  347. $checkGroup = array('sysop', $wgAccessControlPanelAllowedGroup);
  348. } else {
  349. $checkGroup = array('sysop');
  350. }
  351.  
  352. if ( ! $this->checkUser($userGroups, $checkGroup) ) {
  353. $wgOut->addHTML( "You don't have access rights to this page." );
  354. return;
  355. }
  356.  
  357. // Get a writable database instance
  358. $dbw = wfGetDB( DB_MASTER );
  359.  
  360. // Always get the header first
  361. $output = $this->getLogDisclaimer();
  362.  
  363. // Check if some contents was posted
  364. if ($this->isPosted()) {
  365. $action = $_GET['action'];
  366. $value = $_GET['value'];
  367.  
  368. # Purging log
  369. if ($action == 'purge' && $value == 'ask') {
  370.  
  371. $pDays = $_POST['pDays'];
  372.  
  373. $wgOut->addHTML( $this->displayAsk( $pDays ) );
  374. return;
  375.  
  376. } else if ($action == 'purge' && $value == 'confirmed') {
  377.  
  378. $confirm = $_POST['submit'];
  379. $pDays = $_POST['pDays'];
  380.  
  381. if ($confirm == 'Yes' && empty($pDays) ) {
  382. $dbw->delete('tw_accesslog', '*');
  383. } else if ($confirm == 'Yes' && !empty($pDays) ) {
  384. $cutoffTime = wfTimestamp( TS_MW, wfTimestamp() - intval($pDays) * self::DAY_IN_SECS );
  385. $dbw->delete('tw_accesslog', array("tw_log_timestamp < '$cutoffTime'") );
  386. }
  387.  
  388. }
  389. }
  390.  
  391. // Get all parameters
  392. $this->fUser = $wgRequest->getText( 'user', '' );
  393. $this->fAccess = $wgRequest->getText( 'access', 'both' );
  394. $this->fNamespaces = $wgRequest->getText( 'ns', 'all' );
  395. $this->fDays = $wgRequest->getIntOrNull( 'days' );
  396. $this->fPage = $wgRequest->getText ( 'pageviewed', '' );
  397. $this->fAnons = $wgRequest->getCheck ( 'anons' ) ? 1 : 0;
  398.  
  399. $this->page = $wgRequest->getInt( 'page', 1 );
  400. $this->logsPerPage = max( 1, $wgRequest->getInt( 'limit', $wgUser->getOption( 'rclimit' ) ) );
  401.  
  402. // Now we read the proper controler display
  403. $output .= $this->getLogControler();
  404.  
  405. # And at this time we are able to read the proper section from database,
  406. # including all the settings
  407.  
  408. # All the query conditions in WHERE clause is recorded in $queryConds
  409. $queryConds = array();
  410.  
  411. if ( !empty($this->fUser) ) {
  412. $queryConds['tw_log_username'] = $this->fUser;
  413. }
  414.  
  415. if ( $this->fAnons > 0 ) {
  416. $queryConds[] = "tw_log_username REGEXP '^([0-9]{1,3})[.period.]([0-9]{1,3})[.period.]([0-9]{1,3})[.period.]([0-9]{1,3})'";
  417. }
  418.  
  419. if ( !empty($this->fAccess) && $this->fAccess != 'both' ) {
  420. $queryConds['tw_log_action'] = $this->fAccess;
  421. }
  422.  
  423.  
  424. if ( !empty($this->fNamespaces) && $this->fNamespaces != 'all' ) {
  425. if ($this->fNamespaces == 'Main') {
  426. $queryConds[] = "tw_log_title NOT LIKE '%:%'";
  427. }
  428. else {
  429. $queryConds[] = "tw_log_title LIKE '$this->fNamespaces:%'";
  430. }
  431. }
  432.  
  433. if ( !empty($this->fPage) ) {
  434. $queryConds[] = "tw_log_title LIKE '%$this->fPage$'";
  435. }
  436.  
  437. if ( $this->fDays !== null ) {
  438. $prevTs = wfTimestamp( TS_MW, wfTimestamp() - intval($this->fDays) * self::DAY_IN_SECS );
  439. $queryConds[] = "tw_log_timestamp > '$prevTs'";
  440. }
  441.  
  442. $from = ($this->page - 1) * $this->logsPerPage;
  443.  
  444. # We need this result, to render properly Previous | Next links
  445. $this->totalResults = $dbw->selectField('tw_accesslog', 'COUNT(*)', $queryConds);
  446.  
  447. $query = $dbw->select('tw_accesslog', '*', $queryConds, __METHOD__, array('ORDER BY' => 'tw_log_timestamp DESC', 'OFFSET' => $from, 'LIMIT' => $this->logsPerPage) );
  448. $result = array($query->numRows(), $query);
  449.  
  450. # Total number of pages will be like this
  451. $this->totalPages = ceil($this->totalResults / $this->logsPerPage);
  452.  
  453. # Now, I send the array object to getLog function, that should render the output properly
  454. $output .= $this->getLog($result);
  455.  
  456. # Finally, generate the acquired content
  457. $wgOut->addHTML( $output );
  458. }
  459.  
  460. }
  461. ?>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement