diff -r de56132c008d -r bdac73ed481e includes/pageutils.php
--- a/includes/pageutils.php Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/pageutils.php Sun Mar 28 23:10:46 2010 -0400
@@ -13,2446 +13,2446 @@
*/
class PageUtils {
-
- /**
- * Tell if a username is used or not.
- * @param $name the name to check for
- * @return string
- */
-
- public static function checkusername($name)
- {
- global $db, $session, $paths, $template, $plugins; // Common objects
- $name = str_replace('_', ' ', $name);
- $q = $db->sql_query('SELECT username FROM ' . table_prefix.'users WHERE username=\'' . $db->escape(rawurldecode($name)) . '\'');
- if ( !$q )
- {
- die($db->get_error());
- }
- if ( $db->numrows() < 1)
- {
- $db->free_result(); return('good');
- }
- else
- {
- $db->free_result(); return('bad');
- }
- }
-
- /**
- * Get the wiki formatting source for a page
- * @param $page the full page id (Namespace:Pagename)
- * @return string
- * @todo (DONE) Make it require a password (just for security purposes)
- */
-
- public static function getsource($page, $password = false)
- {
- global $db, $session, $paths, $template, $plugins; // Common objects
- if ( !isPage($page) )
- {
- return '';
- }
-
- list($page_id, $namespace) = RenderMan::strToPageID($page);
- $ns = namespace_factory($page_id, $namespace);
- $cdata = $ns->get_cdata();
-
- if ( strlen($cdata['password']) == 40 )
- {
- if(!$password || ( $password != $cdata['password']))
- {
- return 'invalid_password';
- }
- }
-
- if(!$session->get_permissions('view_source')) // Dependencies handle this for us - this also checks for read privileges
- return 'access_denied';
- $pid = RenderMan::strToPageID($page);
- if($pid[1] == 'Special' || $pid[1] == 'Admin')
- {
- die('This type of page (' . $paths->nslist[$pid[1]] . ') cannot be edited because the page source code is not stored in the database.');
- }
-
- $e = $db->sql_query('SELECT page_text,char_tag FROM ' . table_prefix.'page_text WHERE page_id=\'' . $pid[0] . '\' AND namespace=\'' . $pid[1] . '\'');
- if ( !$e )
- {
- $db->_die('The page text could not be selected.');
- }
- if( $db->numrows() < 1 )
- {
- return ''; //$db->_die('There were no rows in the text table that matched the page text query.');
- }
-
- $r = $db->fetchrow();
- $db->free_result();
- $message = $r['page_text'];
-
- return htmlspecialchars($message);
- }
-
- /**
- * DEPRECATED. Previously returned the full rendered contents of a page.
- * @param $page the full page id (Namespace:Pagename)
- * @param $send_headers true if the theme headers should be sent (still dependent on current page settings), false otherwise
- * @return string
- */
-
- public static function getpage($page, $send_headers = false, $hist_id = false)
- {
- die('PageUtils->getpage is deprecated.');
- }
-
- /**
- * Writes page data to the database, after verifying permissions and running the XSS filter
- * @param $page_id the page ID
- * @param $namespace the namespace
- * @param $message the text to save
- * @return string
- */
-
- public static function savepage($page_id, $namespace, $message, $summary = 'No edit summary given', $minor = false)
- {
- global $db, $session, $paths, $template, $plugins; // Common objects
-
- $page = new PageProcessor($page_id, $namespace);
- $cdata = $page->ns->get_cdata();
- return $page->update_page($message, $summary, $minor, $cdata['page_format']);
- }
-
- /**
- * Creates a page, both in memory and in the database.
- * @param string $page_id
- * @param string $namespace
- * @return bool true on success, false on failure
- */
-
- public static function createPage($page_id, $namespace, $name = false, $visible = 1)
- {
- global $db, $session, $paths, $template, $plugins; // Common objects
- if(in_array($namespace, Array('Special', 'Admin')))
- {
- // echo 'Notice: PageUtils::createPage: You can\'t create a special page in the database
';
- return 'You can\'t create a special page in the database';
- }
-
- if(!isset($paths->nslist[$namespace]))
- {
- // echo 'Notice: PageUtils::createPage: Couldn\'t look up the namespace
';
- return 'Couldn\'t look up the namespace';
- }
-
- $pname = $paths->nslist[$namespace] . $page_id;
- if(isPage($pname))
- {
- // echo 'Notice: PageUtils::createPage: Page already exists
';
- return 'Page already exists';
- }
-
- if(!$session->get_permissions('create_page'))
- {
- // echo 'Notice: PageUtils::createPage: Not authorized to create pages
';
- return 'Not authorized to create pages';
- }
-
- if($session->user_level < USER_LEVEL_ADMIN && $namespace == 'System')
- {
- // echo 'Notice: PageUtils::createPage: Not authorized to create system messages
';
- return 'Not authorized to create system messages';
- }
-
- if ( substr($page_id, 0, 8) == 'Project:' )
- {
- // echo 'Notice: PageUtils::createPage: Prefix "Project:" is reserved
';
- return 'The prefix "Project:" is reserved for a parser shortcut; if a page was created using this prefix, it would not be possible to link to it.';
- }
-
- /*
- // Dunno why this was here. Enano can handle more flexible names than this...
- $regex = '#^([A-z0-9 _\-\.\/\!\@\(\)]*)$#is';
- if(!preg_match($regex, $name))
- {
- //echo 'Notice: PageUtils::createPage: Name contains invalid characters
';
- return 'Name contains invalid characters';
- }
- */
-
- $page_id = dirtify_page_id($page_id);
-
- if ( !$name )
- $name = str_replace('_', ' ', $page_id);
-
- $page_id = sanitize_page_id( $page_id );
-
- $prot = ( $namespace == 'System' ) ? 1 : 0;
-
- $ips = array(
- 'ip' => array(),
- 'u' => array()
- );
-
- $page_data = Array(
- 'name'=>$name,
- 'urlname'=>$page_id,
- 'namespace'=>$namespace,
- 'special'=>0,'visible'=>1,'comments_on'=>0,'protected'=>$prot,'delvotes'=>0,'delvote_ips'=>serialize($ips),'wiki_mode'=>2,
- );
-
- // die('PageUtils::createpage: Creating page with this data:
' . print_r($page_data, true) . ''); - - $paths->add_page($page_data); - - $qa = $db->sql_query('INSERT INTO ' . table_prefix.'pages(name,urlname,namespace,visible,protected,delvote_ips) VALUES(\'' . $db->escape($name) . '\', \'' . $db->escape($page_id) . '\', \'' . $namespace . '\', '. ( $visible ? '1' : '0' ) .', ' . $prot . ', \'' . $db->escape(serialize($ips)) . '\');'); - $qb = $db->sql_query('INSERT INTO ' . table_prefix.'page_text(page_id,namespace) VALUES(\'' . $db->escape($page_id) . '\', \'' . $namespace . '\');'); - $qc = $db->sql_query('INSERT INTO ' . table_prefix.'logs(time_id,date_string,log_type,action,author,author_uid,page_id,namespace) VALUES('.time().', \'DEPRECATED\', \'page\', \'create\', \'' . $session->username . '\', ' . $session->user_id . ', \'' . $db->escape($page_id) . '\', \'' . $namespace . '\');'); - - if($qa && $qb && $qc) - return 'good'; - else - { - return $db->get_error(); - } - } - - /** - * Sets the protection level on a page. - * @param $page_id string the page ID - * @param $namespace string the namespace - * @param $level int level of protection - 0 is off, 1 is full, 2 is semi - * @param $reason string why the page is being (un)protected - * @return string - "good" on success, in all other cases, an error string (on query failure, calls $db->_die() ) - */ - public static function protect($page_id, $namespace, $level, $reason) - { - global $db, $session, $paths, $template, $plugins; // Common objects - - $page = new PageProcessor($page_id, $namespace); - return $page->protect_page($level, $reason); - } - - /** - * Generates an HTML table with history information in it. - * @param string the page ID - * @param string the namespace - * @param string page password - * @return string - */ - - public static function histlist($page_id, $namespace, $password = false) - { - global $db, $session, $paths, $template, $plugins; // Common objects - global $lang; - - if(!$session->get_permissions('history_view')) - return 'Access denied'; - - ob_start(); - - $pname = $paths->get_pathskey($page_id, $namespace); - $ns = namespace_factory($page_id, $namespace); - $cdata = $ns->get_cdata(); - - if ( !isPage($pname) ) - { - return 'DNE'; - } - - if ( isPage($pname) ) - { - $password_exists = ( !empty($cdata['password']) && $cdata['password'] !== sha1('') ); - if ( $password_exists && $password !== $cdata['password'] ) - { - return '
' . $lang->get('history_err_wrong_password') . '
'; - } - } - - $wiki = ( ( $cdata['wiki_mode'] == 2 && getConfig('wiki_mode') == '1') || $cdata['wiki_mode'] == 1) ? true : false; - $prot = ( ( $cdata['protected'] == 2 && $session->user_logged_in && $session->reg_time + 60*60*24*4 < time() ) || $cdata['protected'] == 1) ? true : false; - - $q = 'SELECT log_id,time_id,date_string,page_id,namespace,author,author_uid,u.username,edit_summary,minor_edit FROM ' . table_prefix . "logs AS l\n" - . " LEFT JOIN " . table_prefix . "users AS u\n" - . " ON ( u.user_id = l.author_uid OR u.user_id IS NULL )\n" - . " WHERE log_type='page' AND action='edit' AND page_id='$page_id' AND namespace='$namespace' AND is_draft != 1 ORDER BY time_id DESC;"; - - if ( !($q = $db->sql_query($q)) ) - $db->_die('The history data for the page "' . $paths->cpage['name'] . '" could not be selected.'); - - echo $lang->get('history_page_subtitle') . ' -' . $lang->get('history_col_datetime') . ' | -' . $lang->get('history_col_user') . ' | -' . $lang->get('history_col_minor') . ' | -' . $lang->get('history_col_action_taken') . ' | -' . $lang->get('history_col_extra') . ' | -- | ||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
' . enano_date(ED_DATE | ED_TIME, intval($r['time_id'])) . ' | '; - - // User - $real_username = $r['author_uid'] > 1 && !empty($r['username']) ? $r['username'] : $r['author']; - $rank_info = $session->get_user_rank($r['author_uid']); - if ( $session->get_permissions('mod_misc') && is_valid_ip($r['author']) && $r['author_uid'] == 1 ) - { - $rc = ' style="cursor: pointer;" title="' . $lang->get('history_tip_rdns') . '" onclick="ajaxReverseDNS(this, \'' . $r['author'] . '\');"'; - } - else - { - $rc = ''; - } - echo 'nslist['User'] . sanitize_page_id($real_username)) ) - { - echo 'class="wikilink-nonexistent"'; - } - echo 'style="' . $rank_info['rank_style'] . '">' . htmlspecialchars($real_username) . ' | '."\n"; - - - // Minor edit - echo ''. (( $r['minor_edit'] ) ? 'M' : '' ) .' | '; - - // Action taken - echo ''; - // Some of these are sanitized at insert-time. Others follow the newer Enano policy of stripping HTML at runtime. - if ($r['action']=='prot') echo $lang->get('history_log_protect') . ' | ' . $lang->get('history_extra_reason') . ' ' . ( $r['edit_summary'] === '__REVERSION__' ? $lang->get('history_extra_protection_reversion') : htmlspecialchars($r['edit_summary']) ); - elseif($r['action']=='unprot') echo $lang->get('history_log_unprotect') . ' | ' . $lang->get('history_extra_reason') . ' ' . ( $r['edit_summary'] === '__REVERSION__' ? $lang->get('history_extra_protection_reversion') : htmlspecialchars($r['edit_summary']) ); - elseif($r['action']=='semiprot') echo $lang->get('history_log_semiprotect') . ' | ' . $lang->get('history_extra_reason') . ' ' . ( $r['edit_summary'] === '__REVERSION__' ? $lang->get('history_extra_protection_reversion') : htmlspecialchars($r['edit_summary']) ); - elseif($r['action']=='rename') echo $lang->get('history_log_rename') . ' | ' . $lang->get('history_extra_oldtitle') . ' '.htmlspecialchars($r['edit_summary']); - elseif($r['action']=='create') echo $lang->get('history_log_create') . ' | '; - elseif($r['action']=='delete') echo $lang->get('history_log_delete') . ' | ' . $lang->get('history_extra_reason') . ' ' . $r['edit_summary']; - elseif($r['action']=='reupload') echo $lang->get('history_log_uploadnew') . ' | ' . $lang->get('history_extra_reason') . ' ' . ( $r['edit_summary'] === '__ROLLBACK__' ? $lang->get('history_extra_upload_reversion') : htmlspecialchars($r['edit_summary']) ); - elseif($r['action']=='votereset')echo $lang->get('history_log_votereset') . ' | ' . $lang->get('history_extra_numvotes') . ' ' . $r['edit_summary']; - echo ' | '; - - // Actions! - echo '' . $lang->get('history_action_contrib') . ' | '; - echo '' . $lang->get('history_action_revert') . ' | '; - - echo '
'; - $_ob .= ( $n == 0 ) ? $lang->get('comment_msg_count_zero', $subst) : ( $n == 1 ? $lang->get('comment_msg_count_one', $subst) : $lang->get('comment_msg_count_plural', $subst) ); - - if ( $session->get_permissions('mod_comments') && $num_unapp > 0 ) - { - $_ob .= ' ' . $lang->get('comment_msg_count_unapp_mod', array( 'num_unapp' => $num_unapp )) . ''; - } - else if ( !$session->get_permissions('mod_comments') && $num_unapp > 0 ) - { - $ls = ( $num_unapp == 1 ) ? 'comment_msg_count_unapp_one' : 'comment_msg_count_unapp_plural'; - $_ob .= ' ' . $lang->get($ls, array( 'num_unapp' => $num_unapp )) . ''; - } - $_ob .= '
'; - $list = 'list = { '; - // _die(htmlspecialchars($ttext)); - $i = -1; - while ( $row = $db->fetchrow($lq) ) - { - $i++; - $strings = Array(); - $bool = Array(); - if ( $session->get_permissions('mod_comments') || $row['approved'] == COMMENT_APPROVED ) - { - $list .= $i . ' : { \'comment\' : unescape(\''.rawurlencode($row['comment_data']).'\'), \'name\' : unescape(\''.rawurlencode($row['name']).'\'), \'subject\' : unescape(\''.rawurlencode($row['subject']).'\'), }, '; - - // Comment ID (used in the Javascript apps) - $strings['ID'] = (string)$i; - - // Determine the name, and whether to link to the user page or not - $name = ''; - if($row['user_id'] > 1) $name .= ''; - $name .= $row['name']; - if($row['user_id'] > 1) $name .= ''; - $strings['NAME'] = $name; unset($name); - - // Subject - $s = $row['subject']; - if(!$row['approved']) $s .= ' ' . $lang->get('comment_msg_note_unapp') . ''; - $strings['SUBJECT'] = $s; - - // Date and time - $strings['DATETIME'] = enano_date(ED_DATE | ED_TIME, $row['time']); - - // User level - switch($row['user_level']) - { - default: - case USER_LEVEL_GUEST: - $l = $lang->get('user_type_guest'); - break; - case USER_LEVEL_MEMBER: - case USER_LEVEL_CHPREF: - $l = $lang->get('user_type_member'); - break; - case USER_LEVEL_MOD: - $l = $lang->get('user_type_mod'); - break; - case USER_LEVEL_ADMIN: - $l = $lang->get('user_type_admin'); - break; - } - $strings['USER_LEVEL'] = $l; unset($l); - - // The actual comment data - $strings['DATA'] = RenderMan::render($row['comment_data']); - - if($session->get_permissions('edit_comments')) - { - // Edit link - $strings['EDIT_LINK'] = '' . $lang->get('comment_btn_edit') . ''; - - // Delete link - $strings['DELETE_LINK'] = '' . $lang->get('comment_btn_delete') . ''; - } - else - { - // Edit link - $strings['EDIT_LINK'] = ''; - - // Delete link - $strings['DELETE_LINK'] = ''; - } - - // Send PM link - $strings['SEND_PM_LINK'] = ( $session->user_logged_in && $row['user_id'] > 1 ) ? '' . $lang->get('comment_btn_send_privmsg') . 'You need to be logged in to post comments. Log in
'; - } - $list .= '};'; - echo 'document.getElementById(\'ajaxEditContainer\').innerHTML = unescape(\''. rawurlencode($_ob) .'\'); - ' . $list; - echo 'Fat.fade_all(); document.getElementById(\'mdgCommentForm\').style.display = \'none\'; document.getElementById(\'mdgCommentFormLink\').style.display="inline";'; - - $ret = ob_get_contents(); - ob_end_clean(); - return Array($ret, $_ob); - - } - - /** - * Generates ready-to-execute Javascript code to be eval'ed by the user's browser to display comments - * @param $page_id the page ID - * @param $namespace the namespace - * @param $action administrative action to perform, default is false - * @param $flags additional info for $action, shouldn't be used except when deleting/approving comments, etc. - * @param $_ob text to prepend to output, used by PageUtils::addcomment - * @return string - */ - - public static function comments($page_id, $namespace, $action = false, $id = -1, $_ob = '') - { - global $db, $session, $paths, $template, $plugins; // Common objects - $r = PageUtils::comments_raw($page_id, $namespace, $action, $id, $_ob); - return $r[0]; - } - - /** - * Generates HTML code for comments - used in browser compatibility mode - * @param $page_id the page ID - * @param $namespace the namespace - * @param $action administrative action to perform, default is false - * @param $flags additional info for $action, shouldn't be used except when deleting/approving comments, etc. - * @param $_ob text to prepend to output, used by PageUtils::addcomment - * @return string - */ - - public static function comments_html($page_id, $namespace, $action = false, $id = -1, $_ob = '') - { - global $db, $session, $paths, $template, $plugins; // Common objects - $r = PageUtils::comments_raw($page_id, $namespace, $action, $id, $_ob); - return $r[1]; - } - - /** - * Updates comment data. - * @param $page_id the page ID - * @param $namespace the namespace - * @param $subject new subject - * @param $text new text - * @param $old_subject the old subject, unprocessed and identical to the value in the DB - * @param $old_text the old text, unprocessed and identical to the value in the DB - * @param $id the javascript list ID, used internally by the client-side app - * @return string - */ - - public static function savecomment($page_id, $namespace, $subject, $text, $old_subject, $old_text, $id = -1) - { - global $db, $session, $paths, $template, $plugins; // Common objects - if(!$session->get_permissions('edit_comments')) - return 'result="BAD";error="Access denied"'; - // Avoid SQL injection - $old_text = $db->escape($old_text); - $old_subject = $db->escape($old_subject); - // Safety check - username/login - if(!$session->get_permissions('mod_comments')) // allow mods to edit comments - { - if(!$session->user_logged_in) _die('AJAX comment save safety check failed because you are not logged in. Sometimes this can happen because you are using a browser that does not send cookies as part of AJAX requests.'.htmlspecialchars($q).''); - $r = $db->fetchrow($s); - $db->free_result(); - if($db->numrows() < 1 || $r['name'] != $session->username) _die('Safety check failed, probably due to a hacking attempt.'); - } - $s = RenderMan::preprocess_text($subject); - $t = RenderMan::preprocess_text($text); - $sql = 'UPDATE ' . table_prefix.'comments SET subject=\'' . $s . '\',comment_data=\'' . $t . '\' WHERE comment_data=\'' . $old_text . '\' AND subject=\'' . $old_subject . '\' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\''; - $result = $db->sql_query($sql); - if($result) - { - return 'result="GOOD"; - list[' . $id . '][\'subject\'] = unescape(\''.str_replace('%5Cn', '%0A', rawurlencode(str_replace('{{EnAnO:Newline}}', '\\n', stripslashes(str_replace('\\n', '{{EnAnO:Newline}}', $s))))).'\'); - list[' . $id . '][\'comment\'] = unescape(\''.str_replace('%5Cn', '%0A', rawurlencode(str_replace('{{EnAnO:Newline}}', '\\n', stripslashes(str_replace('\\n', '{{EnAnO:Newline}}', $t))))).'\'); id = ' . $id . '; - s = unescape(\''.rawurlencode($s).'\'); - t = unescape(\''.str_replace('%5Cn', '
'.htmlspecialchars($q).''); - $r = $db->fetchrow($s); - if($db->numrows() < 1 || $r['name'] != $session->username) _die('Safety check failed, probably due to a hacking attempt.'); - $db->free_result(); - } - $s = RenderMan::preprocess_text($subject); - $t = RenderMan::preprocess_text($text); - $sql = 'UPDATE ' . table_prefix.'comments SET subject=\'' . $s . '\',comment_data=\'' . $t . '\' WHERE comment_id=' . $id . ' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\''; - $result = $db->sql_query($sql); - if($result) - return 'good'; - else return 'Enano encountered a problem whilst saving the comment. - Performed SQL: - ' . $sql . ' - - Error returned by MySQL: '.$db->get_error(); - } - - /** - * Deletes a comment. - * @param $page_id the page ID - * @param $namespace the namespace - * @param $name the name the user posted under - * @param $subj the subject of the comment to be deleted - * @param $text the text of the comment to be deleted - * @param $id the javascript list ID, used internally by the client-side app - * @return string - */ - - public static function deletecomment($page_id, $namespace, $name, $subj, $text, $id) - { - global $db, $session, $paths, $template, $plugins; // Common objects - - if(!$session->get_permissions('edit_comments')) - return 'alert("Access to delete/edit comments is denied");'; - - if(!preg_match('#^([0-9]+)$#', (string)$id)) die('$_GET[id] is improperly formed.'); - $n = $db->escape($name); - $s = $db->escape($subj); - $t = $db->escape($text); - - // Safety check - username/login - if(!$session->get_permissions('mod_comments')) // allows mods to delete comments - { - if(!$session->user_logged_in) _die('AJAX comment save safety check failed because you are not logged in. Sometimes this can happen because you are using a browser that does not send cookies as part of AJAX requests.
'.htmlspecialchars($q).''); - $r = $db->fetchrow($s); - if($db->numrows() < 1 || $r['name'] != $session->username) _die('Safety check failed, probably due to a hacking attempt.'); - $db->free_result(); - } - $q = 'DELETE FROM ' . table_prefix.'comments WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND name=\'' . $n . '\' AND subject=\'' . $s . '\' AND comment_data=\'' . $t . '\' LIMIT 1;'; - $e=$db->sql_query($q); - if(!$e) return('alert(unesape(\''.rawurlencode('Error during query: '.$db->get_error().'\n\nQuery:\n' . $q) . '\'));'); - return('good'); - } - - /** - * Deletes a comment in a cleaner fashion. - * @param $page_id the page ID - * @param $namespace the namespace - * @param $id the comment ID (primary key) - * @return string - */ - - public static function deletecomment_neater($page_id, $namespace, $id) - { - global $db, $session, $paths, $template, $plugins; // Common objects - - if(!preg_match('#^([0-9]+)$#', (string)$id)) die('$_GET[id] is improperly formed.'); - - if(!$session->get_permissions('edit_comments')) - return 'alert("Access to delete/edit comments is denied");'; - - // Safety check - username/login - if(!$session->get_permissions('mod_comments')) // allows mods to delete comments - { - if(!$session->user_logged_in) _die('AJAX comment save safety check failed because you are not logged in. Sometimes this can happen because you are using a browser that does not send cookies as part of AJAX requests.
'.htmlspecialchars($q).''); - $r = $db->fetchrow($s); - if($db->numrows() < 1 || $r['name'] != $session->username) _die('Safety check failed, probably due to a hacking attempt.'); - $db->free_result(); - } - $q = 'DELETE FROM ' . table_prefix.'comments WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND comment_id=' . $id . ' LIMIT 1;'; - $e=$db->sql_query($q); - if(!$e) return('alert(unesape(\''.rawurlencode('Error during query: '.$db->get_error().'\n\nQuery:\n' . $q) . '\'));'); - return('good'); - } - - /** - * Renames a page. - * @param $page_id the page ID - * @param $namespace the namespace - * @param $name the new name for the page - * @return string error string or success message - */ - - public static function rename($page_id, $namespace, $name) - { - global $db, $session, $paths, $template, $plugins; // Common objects - global $lang; - - $page = new PageProcessor($page_id, $namespace); - return $page->rename_page($name); - } - - /** - * Flushes (clears) the action logs for a given page - * @param $page_id the page ID - * @param $namespace the namespace - * @return string error/success string - */ - - public static function flushlogs($page_id, $namespace) - { - global $db, $session, $paths, $template, $plugins; // Common objects - global $lang; - if ( !is_object($lang) && defined('IN_ENANO_INSTALL') ) - { - // This is a special exception for the Enano installer, which doesn't init languages yet. - $lang = new Language('eng'); - } - if(!$session->get_permissions('clear_logs') && !defined('IN_ENANO_INSTALL')) - { - return $lang->get('etc_access_denied'); - } - if ( !$session->sid_super ) - { - return $lang->get('etc_access_denied_need_reauth'); - } - - $page_id_db = $db->escape($page_id); - $namespace_db = $db->escape($namespace); - - // If we're flushing a file, also clear all revisions before the current - if ( $namespace == 'File' ) - { - $q = $db->sql_query('SELECT file_id FROM ' . table_prefix . "files WHERE page_id='$page_id_db' ORDER BY time_id DESC;"); - if ( !$q ) - $db->_die(); - // discard first row (current revision) - $db->fetchrow(); - $id_list = array(); - while ( $row = $db->fetchrow() ) - $id_list[] = $row['file_id']; - - require_once(ENANO_ROOT . '/includes/namespaces/file.php'); - - // clear out each file - foreach ( $id_list as $id ) - Namespace_File::delete_file($id); - } - - $q = $db->sql_query('DELETE FROM ' . table_prefix . "logs WHERE page_id='$page_id_db' AND namespace='$namespace';"); - if ( !$q ) - $db->_die('The log entries could not be deleted.'); - - // If the page exists, make a backup of it in case it gets spammed/vandalized - // If not, the admin's probably deleting a trash page - if ( isPage($paths->get_pathskey($page_id, $namespace)) ) - { - $q = $db->sql_query('SELECT page_text,char_tag FROM ' . table_prefix . "page_text WHERE page_id='$page_id_db' AND namespace='$namespace_db';"); - if ( !$q ) - $db->_die('The current page text could not be selected; as a result, creating the backup of the page failed. Please make a backup copy of the page by clicking Edit this page and then clicking Save Changes.'); - $row = $db->fetchrow(); - $db->free_result(); - $minor_edit = ( ENANO_DBLAYER == 'MYSQL' ) ? 'false' : '0'; - $username = $db->escape($session->username); - $q = 'INSERT INTO ' . table_prefix . "logs ( log_type, action, time_id, date_string, page_id, namespace, page_text, char_tag, author, author_uid, edit_summary, minor_edit ) VALUES\n" - . " ('page', 'edit', " . time() . ", 'DEPRECATED', '$page_id', '$namespace', '" . $db->escape($row['page_text']) . "', '', '{$username}', $session->user_id, '" . $lang->get('page_flushlogs_backup_summary') . "', $minor_edit);"; - if ( !$db->sql_query($q) ) - $db->_die('The history (log) entry could not be inserted into the logs table.'); - } - - return $lang->get('ajax_clearlogs_success'); - } - - /** - * Deletes a page. - * @param string $page_id the condemned page ID - * @param string $namespace the condemned namespace - * @param string The reason for deleting the page in question - * @return string - */ - - public static function deletepage($page_id, $namespace, $reason) - { - global $db, $session, $paths, $template, $plugins; // Common objects - global $lang; - global $cache; - $perms = $session->fetch_page_acl($page_id, $namespace); - $x = trim($reason); - if ( empty($x) ) - { - return $lang->get('ajax_delete_need_reason'); - } - if(!$perms->get_permissions('delete_page')) return('Administrative privileges are required to delete pages, you loser.'); - - if ( !$session->sid_super ) - { - return $lang->get('etc_access_denied_need_reauth'); - } - - $e = $db->sql_query('INSERT INTO ' . table_prefix.'logs(time_id,date_string,log_type,action,page_id,namespace,author,author_uid,edit_summary) VALUES('.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \'page\', \'delete\', \'' . $page_id . '\', \'' . $namespace . '\', \'' . $session->username . '\', ' . $session->user_id . ', \'' . $db->escape(htmlspecialchars($reason)) . '\')'); - if(!$e) $db->_die('The page log entry could not be inserted.'); - $e = $db->sql_query('DELETE FROM ' . table_prefix.'categories WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\''); - if(!$e) $db->_die('The page categorization entries could not be deleted.'); - $e = $db->sql_query('DELETE FROM ' . table_prefix.'comments WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\''); - if(!$e) $db->_die('The page comments could not be deleted.'); - $e = $db->sql_query('DELETE FROM ' . table_prefix.'page_text WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\''); - if(!$e) $db->_die('The page text entry could not be deleted.'); - $e = $db->sql_query('DELETE FROM ' . table_prefix.'pages WHERE urlname=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\''); - if(!$e) $db->_die('The page entry could not be deleted.'); - if ( $namespace == 'File' ) - { - $e = $db->sql_query('DELETE FROM ' . table_prefix.'files WHERE page_id=\'' . $page_id . '\''); - if(!$e) $db->_die('The file entry could not be deleted.'); - } - $cache->purge('page_meta'); - return $lang->get('ajax_delete_success'); - } - - /** - * Deletes files associated with a File page. - * @param string Page ID - */ - - public static function delete_page_files($page_id) - { - global $db, $session, $paths, $template, $plugins; // Common objects - - $q = $db->sql_query('SELECT file_id, filename, file_key, time_id, file_extension FROM ' . table_prefix . "files WHERE page_id = '{$db->escape($page_id)}';"); - if ( !$q ) - $db->_die(); - - while ( $row = $db->fetchrow() ) - { - // wipe original file - foreach ( array( - ENANO_ROOT . "/files/{$row['file_key']}_{$row['time_id']}{$row['file_extension']}", - ENANO_ROOT . "/files/{$row['file_key']}{$row['file_extension']}" - ) as $orig_file ) - { - if ( file_exists($orig_file) ) - @unlink($orig_file); - } - - // wipe cached files - if ( $dr = @opendir(ENANO_ROOT . '/cache/') ) - { - // lol404.jpg-1217958283-200x320.jpg - while ( $dh = @readdir($dr) ) - { - $regexp = ':^' . preg_quote("{$row['filename']}-{$row['time_id']}-") . '[0-9]+x[0-9]+\.' . ltrim($row['file_extension'], '.') . '$:'; - if ( preg_match($regexp, $dh) ) - { - @unlink(ENANO_ROOT . "/cache/$dh"); - } - } - @closedir($dr); - } - } - - $q = $db->sql_query('DELETE FROM ' . table_prefix . "files WHERE page_id = '{$db->escape($page_id)}';"); - if ( !$q ) - $db->die(); - - return true; - } - - /** - * Increments the deletion votes for a page by 1, and adds the current username/IP to the list of users that have voted for the page to prevent dual-voting - * @param $page_id the page ID - * @param $namespace the namespace - * @return string - */ - - public static function delvote($page_id, $namespace) - { - global $db, $session, $paths, $template, $plugins; // Common objects - global $lang; - global $cache; - - if ( !$session->get_permissions('vote_delete') ) - { - return $lang->get('etc_access_denied'); - } - - if ( $namespace == 'Admin' || $namespace == 'Special' || $namespace == 'System' ) - { - return 'Special pages and system messages can\'t be voted for deletion.'; - } - - $pname = $paths->nslist[$namespace] . sanitize_page_id($page_id); - - if ( !isPage($pname) ) - { - return 'The page does not exist.'; - } - - $ns = namespace_factory($page_id, $namespace); - $cdata = $ns->get_cdata(); - - $cv =& $cdata['delvotes']; - $ips =& $cdata['delvote_ips']; - - if ( empty($ips) ) - { - $ips = array( - 'ip' => array(), - 'u' => array() - ); - } - else - { - $ips = @unserialize($ips); - if ( !$ips ) - { - $ips = array( - 'ip' => array(), - 'u' => array() - ); - } - } - - if ( in_array($session->username, $ips['u']) || in_array($_SERVER['REMOTE_ADDR'], $ips['ip']) ) - { - return $lang->get('ajax_delvote_already_voted'); - } - - $ips['u'][] = $session->username; - $ips['ip'][] = $_SERVER['REMOTE_ADDR']; - $ips = $db->escape( serialize($ips) ); - - $cv++; - - $q = 'UPDATE ' . table_prefix.'pages SET delvotes=' . $cv . ',delvote_ips=\'' . $ips . '\' WHERE urlname=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\''; - $w = $db->sql_query($q); - if ( !$w ) - $db->_die(); - - // all done, flush page cache to mark it up - $cache->purge('page_meta'); - - return $lang->get('ajax_delvote_success'); - } - - /** - * Resets the number of votes against a page to 0. - * @param $page_id the page ID - * @param $namespace the namespace - * @return string - */ - - public static function resetdelvotes($page_id, $namespace) - { - global $db, $session, $paths, $template, $plugins; // Common objects - global $lang; - global $cache; - - if ( !$session->get_permissions('vote_reset') ) - { - return $lang->get('etc_access_denied'); - } - - $page_id = $db->escape($page_id); - $namespace = $db->escape($namespace); - - // pull existing info - $q = $db->sql_query('SELECT delvotes, delvote_ips FROM ' . table_prefix . "pages WHERE urlname = '$page_id' AND namespace = '$namespace'"); - if ( !$q ) - $db->_die(); - if ( $db->numrows() < 1 ) - return $lang->get('page_err_page_not_exist'); - - list($delvotes, $delvote_ips) = $db->fetchrow_num(); - $db->free_result(); - $delvote_ips = $db->escape($delvote_ips); - $username = $db->escape($session->username); - - // log action - $time = time(); - $q = $db->sql_query('INSERT INTO ' . table_prefix . "logs (time_id, log_type, action, edit_summary, page_text, author, author_uid, page_id, namespace) VALUES\n" - . " ( $time, 'page', 'votereset', '$delvotes', '$delvote_ips', '$username', $session->user_id, '$page_id', '$namespace' )"); - if ( !$q ) - $db->_die(); - - // reset votes - $empty_vote_record = $db->escape(serialize(array('ip'=>array(),'u'=>array()))); - $q = 'UPDATE ' . table_prefix.'pages SET delvotes=0,delvote_ips=\'' . $empty_vote_record . '\' WHERE urlname=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\''; - $e = $db->sql_query($q); - if ( !$e ) - { - $db->_die('The number of delete votes was not reset.'); - } - else - { - $cache->purge('page_meta'); - return $lang->get('ajax_delvote_reset_success'); - } - } - - /** - * Gets a list of styles for a given theme name. As of Banshee, this returns JSON. - * @param $id the name of the directory for the theme - * @return string JSON string with an array containing a list of themes - */ - - public static function getstyles() - { - - if ( !preg_match('/^([a-z0-9_-]+)$/', $_GET['id']) ) - return enano_json_encode(false); - - $dir = './themes/' . $_GET['id'] . '/css/'; - $list = Array(); - // Open a known directory, and proceed to read its contents - if (is_dir($dir)) { - if ($dh = opendir($dir)) { - while (($file = readdir($dh)) !== false) { - if ( preg_match('#^(.*?)\.css$#is', $file) && $file != '_printable.css' ) // _printable.css should be included with every theme - { // it should be a copy of the original style, but - // mostly black and white - // Note to self: document this - $list[] = substr($file, 0, strlen($file)-4); - } - } - closedir($dh); - } - } - else - { - return(enano_json_encode(Array('mode' => 'error', 'error' => $dir.' is not a dir'))); - } - - return enano_json_encode($list); - } - - /** - * Assembles a Javascript app with category information - * @param $page_id the page ID - * @param $namespace the namespace - * @return string Javascript code - */ - - public static function catedit($page_id, $namespace) - { - $d = PageUtils::catedit_raw($page_id, $namespace); - return $d[0] . ' /* BEGIN CONTENT */ document.getElementById("ajaxEditContainer").innerHTML = unescape(\''.rawurlencode($d[1]).'\');'; - } - - /** - * Does the actual HTML/javascript generation for cat editing, but returns an array - * @access private - */ - - public static function catedit_raw($page_id, $namespace) - { - global $db, $session, $paths, $template, $plugins; // Common objects - global $lang; - - ob_start(); - $_ob = ''; - $e = $db->sql_query('SELECT category_id FROM ' . table_prefix.'categories WHERE page_id=\'' . $paths->page_id . '\' AND namespace=\'' . $paths->namespace . '\''); - if(!$e) jsdie('Error selecting category information for current page: '.$db->get_error()); - $cat_current = Array(); - while($r = $db->fetchrow()) - { - $cat_current[] = $r; - } - $db->free_result(); - - $cat_all = array(); - $q = $db->sql_query('SELECT * FROM ' . table_prefix . 'pages WHERE namespace = \'Category\';'); - if ( !$q ) - $db->_die(); - - while ( $row = $db->fetchrow() ) - { - $cat_all[] = Namespace_Default::bake_cdata($row); - } - - // Make $cat_all an associative array, like $paths->pages - $sz = sizeof($cat_all); - for($i=0;$i<$sz;$i++) - { - $cat_all[$cat_all[$i]['urlname_nons']] = $cat_all[$i]; - } - // Now, the "zipper" function - join the list of categories with the list of cats that this page is a part of - $cat_info = $cat_all; - for($i=0;$i
" . $lang->get('history_lbl_comparingrevisions') . " {$time1} → {$time2}
- "; - // Free some memory - unset($row1, $row2, $q1, $q2); - - $_ob .= RenderMan::diff($text1, $text2); - return $_ob; - } - - /** - * Gets ACL information about the selected page for target type X and target ID Y. - * @param array $parms What to select. This is an array purely for JSON compatibility. It should be an associative array with keys target_type and target_id. - * @return array - */ - - public static function acl_editor($parms = Array()) - { - global $db, $session, $paths, $template, $plugins; // Common objects - global $lang; - - if(!$session->get_permissions('edit_acl') && ( $session->user_level < USER_LEVEL_ADMIN || !defined('ACL_ALWAYS_ALLOW_ADMIN_EDIT_ACL')) ) - { - return Array( - 'mode' => 'error', - 'error' => $lang->get('acl_err_access_denied') - ); - } - if ( !$session->sid_super ) - { - return Array( - 'mode' => 'error', - 'error' => $lang->get('etc_access_denied_need_reauth') - ); - } - $parms['page_id'] = ( isset($parms['page_id']) ) ? $parms['page_id'] : false; - $parms['namespace'] = ( isset($parms['namespace']) ) ? $parms['namespace'] : false; - $page_id =& $parms['page_id']; - $namespace =& $parms['namespace']; - $page_where_clause = ( empty($page_id) || empty($namespace) ) ? 'AND a.page_id IS NULL AND a.namespace IS NULL' : 'AND a.page_id=\'' . $db->escape($page_id) . '\' AND a.namespace=\'' . $db->escape($namespace) . '\''; - $page_where_clause_lite = ( empty($page_id) || empty($namespace) ) ? 'AND page_id IS NULL AND namespace IS NULL' : 'AND page_id=\'' . $db->escape($page_id) . '\' AND namespace=\'' . $db->escape($namespace) . '\''; - //die(print_r($page_id,true)); - $template->load_theme(); - // $perms_obj = $session->fetch_page_acl($page_id, $namespace); - $perms_obj =& $session; - $return = Array(); - if ( !file_exists(ENANO_ROOT . '/themes/' . $session->theme . '/acledit.tpl') ) - { - return Array( - 'mode' => 'error', - 'error' => $lang->get('acl_err_missing_template'), - ); - } - $return['template'] = $template->extract_vars('acledit.tpl'); - $return['page_id'] = $page_id; - $return['namespace'] = $namespace; - if(isset($parms['mode'])) - { - switch($parms['mode']) - { - case 'listgroups': - $return['groups'] = Array(); - $q = $db->sql_query('SELECT group_id,group_name FROM ' . table_prefix.'groups ORDER BY group_name ASC;'); - while($row = $db->fetchrow()) - { - $return['groups'][] = Array( - 'id' => $row['group_id'], - 'name' => $row['group_name'], - ); - } - $db->free_result(); - $return['page_groups'] = Array(); - $q = $db->sql_query('SELECT pg_id,pg_name FROM ' . table_prefix.'page_groups ORDER BY pg_name ASC;'); - if ( !$q ) - return Array( - 'mode' => 'error', - 'error' => $db->get_error() - ); - while ( $row = $db->fetchrow() ) - { - $return['page_groups'][] = Array( - 'id' => $row['pg_id'], - 'name' => $row['pg_name'] - ); - } - break; - case 'seltarget_id': - if ( !is_int($parms['target_id']) ) - { - return Array( - 'mode' => 'error', - 'error' => 'Expected parameter target_id type int' - ); - } - $q = $db->sql_query('SELECT target_id, target_type, page_id, namespace, rules FROM ' . table_prefix . "acl WHERE rule_id = {$parms['target_id']};"); - if ( !$q ) - return Array( - 'mode' => 'error', - 'error' => $db->get_error() - ); - if ( $db->numrows() < 1 ) - return Array( - 'mode' => 'error', - 'error' => "No rule with ID {$parms['target_id']} found" - ); - $parms = $db->fetchrow(); - $db->free_result(); - - // regenerate page selection - $parms['page_id'] = ( isset($parms['page_id']) ) ? $parms['page_id'] : false; - $parms['namespace'] = ( isset($parms['namespace']) ) ? $parms['namespace'] : false; - $parms['mode'] = 'seltarget_id'; - $page_id =& $parms['page_id']; - $namespace =& $parms['namespace']; - $page_where_clause = ( empty($page_id) || empty($namespace) ) ? 'AND a.page_id IS NULL AND a.namespace IS NULL' : 'AND a.page_id=\'' . $db->escape($page_id) . '\' AND a.namespace=\'' . $db->escape($namespace) . '\''; - $page_where_clause_lite = ( empty($page_id) || empty($namespace) ) ? 'AND page_id IS NULL AND namespace IS NULL' : 'AND page_id=\'' . $db->escape($page_id) . '\' AND namespace=\'' . $db->escape($namespace) . '\''; - - $return['page_id'] = $parms['page_id']; - $return['namespace'] = $parms['namespace']; - - // From here, let the seltarget handler take over - case 'seltarget': - $return['mode'] = 'seltarget'; - $return['acl_types'] = $perms_obj->acl_types; - $return['acl_deps'] = $perms_obj->acl_deps; - $return['acl_descs'] = $perms_obj->acl_descs; - $return['target_type'] = $parms['target_type']; - $return['target_id'] = $parms['target_id']; - switch($parms['target_type']) - { - case ACL_TYPE_USER: - $user_col = ( $parms['mode'] == 'seltarget_id' ) ? 'user_id' : 'username'; - $q = $db->sql_query('SELECT a.rules,u.user_id,u.username FROM ' . table_prefix.'users AS u - LEFT JOIN ' . table_prefix.'acl AS a - ON a.target_id=u.user_id - WHERE a.target_type='.ACL_TYPE_USER.' - AND u.' . $user_col . ' = \'' . $db->escape($parms['target_id']) . '\' - ' . $page_where_clause . ';'); - if(!$q) - return(Array('mode'=>'error','error'=>$db->get_error())); - if($db->numrows() < 1) - { - $return['type'] = 'new'; - $q = $db->sql_query('SELECT user_id,username FROM ' . table_prefix.'users WHERE username=\'' . $db->escape($parms['target_id']) . '\';'); - if(!$q) - return(Array('mode'=>'error','error'=>$db->get_error())); - if($db->numrows() < 1) - return Array('mode'=>'error','error'=>$lang->get('acl_err_user_not_found'),'debug' => $db->sql_backtrace()); - $row = $db->fetchrow(); - $return['target_name'] = $row['username']; - $return['target_id'] = intval($row['user_id']); - $return['current_perms'] = array(); - } - else - { - $return['type'] = 'edit'; - $row = $db->fetchrow(); - $return['target_name'] = $row['username']; - $return['target_id'] = intval($row['user_id']); - $return['current_perms'] = $session->string_to_perm($row['rules']); - } - $db->free_result(); - // Eliminate types that don't apply to this namespace - if ( $namespace && $namespace != '__PageGroup' ) - { - foreach ( $return['current_perms'] AS $i => $perm ) - { - if ( ( $page_id != null && $namespace != null ) && ( !in_array ( $namespace, $session->acl_scope[$i] ) && !in_array('All', $session->acl_scope[$i]) ) ) - { - // echo "// SCOPE CONTROL: eliminating: $i\n"; - unset($return['current_perms'][$i]); - unset($return['acl_types'][$i]); - unset($return['acl_descs'][$i]); - unset($return['acl_deps'][$i]); - } - } - } - break; - case ACL_TYPE_GROUP: - $q = $db->sql_query('SELECT a.rules,g.group_name,g.group_id FROM ' . table_prefix.'groups AS g - LEFT JOIN ' . table_prefix.'acl AS a - ON a.target_id=g.group_id - WHERE a.target_type='.ACL_TYPE_GROUP.' - AND g.group_id=\''.intval($parms['target_id']).'\' - ' . $page_where_clause . ';'); - if(!$q) - return(Array('mode'=>'error','error'=>$db->get_error())); - if($db->numrows() < 1) - { - $return['type'] = 'new'; - $q = $db->sql_query('SELECT group_id,group_name FROM ' . table_prefix.'groups WHERE group_id=\''.intval($parms['target_id']).'\';'); - if(!$q) - return(Array('mode'=>'error','error'=>$db->get_error())); - if($db->numrows() < 1) - return Array('mode'=>'error','error'=>$lang->get('acl_err_bad_group_id')); - $row = $db->fetchrow(); - $return['target_name'] = $row['group_name']; - $return['target_id'] = intval($row['group_id']); - $return['current_perms'] = array(); - } - else - { - $return['type'] = 'edit'; - $row = $db->fetchrow(); - $return['target_name'] = $row['group_name']; - $return['target_id'] = intval($row['group_id']); - $return['current_perms'] = $session->string_to_perm($row['rules']); - } - $db->free_result(); - // Eliminate types that don't apply to this namespace - if ( $namespace && $namespace != '__PageGroup' ) - { - foreach ( $return['current_perms'] AS $i => $perm ) - { - if ( ( $page_id != false && $namespace != false ) && ( !in_array ( $namespace, $session->acl_scope[$i] ) && !in_array('All', $session->acl_scope[$i]) ) ) - { - // echo "// SCOPE CONTROL: eliminating: $i\n"; //; ".print_r($namespace,true).":".print_r($page_id,true)."\n"; - unset($return['current_perms'][$i]); - unset($return['acl_types'][$i]); - unset($return['acl_descs'][$i]); - unset($return['acl_deps'][$i]); - } - } - } - //return Array('mode'=>'debug','text'=>print_r($return, true)); - break; - default: - return Array('mode'=>'error','error','Invalid ACL type ID'); - break; - } - return $return; - break; - case 'save_new': - case 'save_edit': - if ( defined('ENANO_DEMO_MODE') ) - { - return Array('mode'=>'error','error'=>$lang->get('acl_err_demo')); - } - $q = $db->sql_query('DELETE FROM ' . table_prefix.'acl WHERE target_type='.intval($parms['target_type']).' AND target_id='.intval($parms['target_id']).' - ' . $page_where_clause_lite . ';'); - if(!$q) - return Array('mode'=>'error','error'=>$db->get_error()); - if ( sizeof ( $parms['perms'] ) < 1 ) - { - // As of 1.1.x, this returns success because the rule length is zero if the user selected "inherit" in all columns - return Array( - 'mode' => 'success', - 'target_type' => $parms['target_type'], - 'target_id' => $parms['target_id'], - 'target_name' => $parms['target_name'], - 'page_id' => $page_id, - 'namespace' => $namespace, - ); - } - $rules = $session->perm_to_string($parms['perms']); - $q = ($page_id && $namespace) ? 'INSERT INTO ' . table_prefix.'acl ( target_type, target_id, page_id, namespace, rules ) - VALUES( '.intval($parms['target_type']).', '.intval($parms['target_id']).', \'' . $db->escape($page_id) . '\', \'' . $db->escape($namespace) . '\', \'' . $db->escape($rules) . '\' )' : - 'INSERT INTO ' . table_prefix.'acl ( target_type, target_id, rules ) - VALUES( '.intval($parms['target_type']).', '.intval($parms['target_id']).', \'' . $db->escape($rules) . '\' )'; - if(!$db->sql_query($q)) return Array('mode'=>'error','error'=>$db->get_error()); - return Array( - 'mode' => 'success', - 'target_type' => $parms['target_type'], - 'target_id' => $parms['target_id'], - 'target_name' => $parms['target_name'], - 'page_id' => $page_id, - 'namespace' => $namespace, - ); - break; - case 'delete': - if ( defined('ENANO_DEMO_MODE') ) - { - return Array('mode'=>'error','error'=>$lang->get('acl_err_demo')); - } - $sql = 'DELETE FROM ' . table_prefix.'acl WHERE target_type='.intval($parms['target_type']).' AND target_id='.intval($parms['target_id']).' - ' . $page_where_clause_lite . ';'; - $q = $db->sql_query($sql); - if(!$q) - return Array('mode'=>'error','error'=>$db->get_error()); - return Array( - 'mode' => 'delete', - 'target_type' => $parms['target_type'], - 'target_id' => $parms['target_id'], - 'target_name' => $parms['target_name'], - 'page_id' => $page_id, - 'namespace' => $namespace, - ); - break; - case 'list_existing': - - $return = array( - 'mode' => 'list_existing', - 'key' => acl_list_draw_key(), - 'rules' => array() - ); - - $acl_columns = 'a.' . implode(', a.', $db->columns_in(table_prefix . 'acl')); - $users_columns = 'u.' . implode(', u.', $db->columns_in(table_prefix . 'users')); - $groups_columns = 'g.' . implode(', g.', $db->columns_in(table_prefix . 'groups')); - $pg_columns = 'p.' . implode(', p.', array('pg_id', 'pg_type', 'pg_name', 'pg_target')); - - $q = $db->sql_query("SELECT a.rule_id, u.username, g.group_name, a.target_type, a.target_id, a.page_id, a.namespace, a.rules, p.pg_name\n" - . " FROM " . table_prefix . "acl AS a\n" - . " LEFT JOIN " . table_prefix . "users AS u\n" - . " ON ( (a.target_type = " . ACL_TYPE_USER . " AND a.target_id = u.user_id) OR (u.user_id IS NULL) )\n" - . " LEFT JOIN " . table_prefix . "groups AS g\n" - . " ON ( (a.target_type = " . ACL_TYPE_GROUP . " AND a.target_id = g.group_id) OR (g.group_id IS NULL) )\n" - . " LEFT JOIN " . table_prefix . "page_groups as p\n" - . " ON ( (a.namespace = '__PageGroup' AND a.page_id = CAST(p.pg_id AS CHAR)) OR (p.pg_id IS NULL) )\n" - . " WHERE ( a.target_type = " . ACL_TYPE_USER . " OR a.target_type = " . ACL_TYPE_GROUP . " )\n" - . " GROUP BY a.rule_id, $acl_columns, $users_columns, $groups_columns, $pg_columns\n" - . " ORDER BY a.target_type ASC, a.rule_id ASC;" - ); - - if ( !$q ) - $db->_die(); - - while ( $row = $db->fetchrow($q) ) - { - if ( $row['target_type'] == ACL_TYPE_USER && empty($row['username']) ) - { - // This is only done if we have an ACL affecting a user that doesn't exist. - // Nice little bit of maintenance to have. - if ( !$db->sql_query("DELETE FROM " . table_prefix . "acl WHERE rule_id = {$row['rule_id']};") ) - $db->_die(); - continue; - } - $score = get_acl_rule_score($row['rules']); - $deep_limit = ACL_SCALE_MINIMAL_SHADE; - // Determine background color of cell by score - if ( $score > 5 ) - { - // high score, show in green - $color = 2.5 * $score; - if ( $color > 255 ) - $color = 255; - $color = round($color); - // blend with the colordepth limit - $color = $deep_limit + ( ( 0xFF - $deep_limit ) - ( ( $color / 0xFF ) * ( 0xFF - $deep_limit ) ) ); - $color = dechex($color); - $color = "{$color}ff{$color}"; - } - else if ( $score < -5 ) - { - // low score, show in red - $color = 0 - $score; - $color = 2.5 * $color; - if ( $color > 255 ) - $color = 255; - $color = round($color); - // blend with the colordepth limit - $color = $deep_limit + ( ( 0xFF - $deep_limit ) - ( ( $color / 0xFF ) * ( 0xFF - $deep_limit ) ) ); - $color = dechex($color); - $color = "ff{$color}{$color}"; - } - else - { - $color = 'efefef'; - } - - // Rate rule textually based on its score - if ( $score >= 70 ) - $desc = $lang->get('acl_msg_scale_allow'); - else if ( $score >= 50 ) - $desc = $lang->get('acl_msg_scale_mostly_allow'); - else if ( $score >= 25 ) - $desc = $lang->get('acl_msg_scale_some_allow'); - else if ( $score >= -25 ) - $desc = $lang->get('acl_msg_scale_mixed'); - else if ( $score <= -70 ) - $desc = $lang->get('acl_msg_scale_deny'); - else if ( $score <= -50 ) - $desc = $lang->get('acl_msg_scale_mostly_deny'); - else if ( $score <= -25 ) - $desc = $lang->get('acl_msg_scale_some_deny'); - - // group and user target info - $info = ''; - if ( $row['target_type'] == ACL_TYPE_USER ) - $info = $lang->get('acl_msg_list_user', array( 'username' => $row['username'] )); // "(User: {$row['username']})"; - else if ( $row['target_type'] == ACL_TYPE_GROUP ) - $info = $lang->get('acl_msg_list_group', array( 'group' => $row['group_name'] )); - - // affected pages info - if ( $row['page_id'] && $row['namespace'] && $row['namespace'] != '__PageGroup' ) - $info .= $lang->get('acl_msg_list_on_page', array( 'page_name' => "{$row['namespace']}:{$row['page_id']}" )); - else if ( $row['page_id'] && $row['namespace'] && $row['namespace'] == '__PageGroup' ) - $info .= $lang->get('acl_msg_list_on_page_group', array( 'page_group' => $row['pg_name'] )); - else - $info .= $lang->get('acl_msg_list_entire_site'); - - $score_string = $lang->get('acl_msg_list_score', array - ( - 'score' => $score, - 'desc' => $desc, - 'info' => $info - )); - $return['rules'][] = array( - 'score_string' => $score_string, - 'rule_id' => $row['rule_id'], - 'color' => $color - ); - } - - break; - case 'list_presets': - $presets = array(); - $q = $db->sql_query('SELECT page_id AS preset_name, rule_id, rules FROM ' . table_prefix . "acl WHERE target_type = " . ACL_TYPE_PRESET . ";"); - if ( !$q ) - $db->die_json(); - - while ( $row = $db->fetchrow() ) - { - $row['rules'] = $session->string_to_perm($row['rules']); - $presets[] = $row; - } - - return array( - 'mode' => 'list_existing', - 'presets' => $presets - ); - break; - case 'save_preset': - if ( empty($parms['preset_name']) ) - { - return array( - 'mode' => 'error', - 'error' => $lang->get('acl_err_preset_name_empty') - ); - } - $preset_name = $db->escape($parms['preset_name']); - $q = $db->sql_query('DELETE FROM ' . table_prefix . "acl WHERE target_type = " . ACL_TYPE_PRESET . " AND page_id = '$preset_name';"); - if ( !$q ) - $db->die_json(); - - $perms = $session->perm_to_string($parms['perms']); - if ( !$perms ) - { - return array( - 'mode' => 'error', - 'error' => $lang->get('acl_err_preset_is_blank') - ); - } - - $perms = $db->escape($perms); - $q = $db->sql_query('INSERT INTO ' . table_prefix . "acl(page_id, target_type, rules) VALUES\n" - . " ( '$preset_name', " . ACL_TYPE_PRESET . ", '$perms' );"); - if ( !$q ) - $db->die_json(); - - return array( - 'mode' => 'success' - ); - break; - case 'trace': - list($targetpid, $targetns) = RenderMan::strToPageID($parms['page']); - try - { - $perms = $session->fetch_page_acl_user($parms['user'], $targetpid, $targetns); - $perm_table = array( - AUTH_ALLOW => 'acl_lbl_field_allow', - AUTH_WIKIMODE => 'acl_lbl_field_wikimode', - AUTH_DISALLOW => 'acl_lbl_field_disallow', - AUTH_DENY => 'acl_lbl_field_deny' - ); - - $return = array( - 'mode' => 'trace', - 'perms' => array() - ); - - foreach ( $perms->perm_resolve_table as $perm_type => $lookup_data ) - { - if ( !$session->check_acl_scope($perm_type, $targetns) ) - continue; - - $src_l10n = $lang->get($session->acl_inherit_lang_table[$lookup_data['src']], $lookup_data); - $divclass = preg_replace('/^acl_inherit_/', '', $session->acl_inherit_lang_table[$lookup_data['src']]); - $perm_string = $lang->get($perm_table[$perms->perms[$perm_type]]); - $perm_name = $lang->get($session->acl_descs[$perm_type]); - - $return['perms'][$perm_type] = array( - 'divclass' => "acl_inherit acl_$divclass", - 'perm_type' => $perm_type, - 'perm_name' => $perm_name, - 'perm_value' => $perm_string, - 'perm_src' => $src_l10n, - 'rule_id' => intval($lookup_data['rule_id']), - 'bad_deps' => $perms->acl_check_deps($perm_type, true) - ); - } - - // group rules if possible - $return['groups'] = array(); - foreach ( $return['perms'] as $rule ) - { - if ( !isset($return['groups'][$rule['rule_id']]) ) - { - $return['groups'][$rule['rule_id']] = array(); - } - $return['groups'][$rule['rule_id']][] = $rule['perm_type']; - } - } - catch ( Exception $e ) - { - $return = array( - 'mode' => 'error', - 'error' => $e->getMessage() - ); - } - - break; - default: - return Array('mode'=>'error','error'=>'Hacking attempt'); - break; - } - } - return $return; - } - - /** - * Same as PageUtils::acl_editor(), but the parms are a JSON string instead of an array. This also returns a JSON string. - * @param string $parms Same as PageUtils::acl_editor/$parms, but should be a valid JSON string. - * @return string - */ - - public static function acl_json($parms = '{ }') - { - global $db, $session, $paths, $template, $plugins; // Common objects - try - { - $parms = enano_json_decode($parms); - } - catch ( Zend_Json_Exception $e ) - { - $parms = array(); - } - $ret = PageUtils::acl_editor($parms); - $ret = enano_json_encode($ret); - return $ret; - } - - /** - * A non-Javascript frontend for the ACL API. - * @param array The request data, if any, this should be in the format required by PageUtils::acl_editor() - */ - - public static function aclmanager($parms) - { - global $db, $session, $paths, $template, $plugins; // Common objects - global $lang; - ob_start(); - // Convenience - $formstart = ''; - $parms = PageUtils::acl_preprocess($parms); - $response = PageUtils::acl_editor($parms); - $response = PageUtils::acl_postprocess($response); - - //die('' . htmlspecialchars(print_r($response, true)) . ''); - - switch($response['mode']) - { - case 'debug': - echo '
' . htmlspecialchars($response['text']) . ''; - break; - case 'stage1': - echo '
' . $lang->get('acl_lbl_welcome_body') . '
'; - echo $formstart; - echo ' - - -' . $template->username_field('data[target_id_user]') . '
-' . $lang->get('acl_lbl_scope') . '
- - ' . $groupsel . ' - -Error returned by permissions API:
' . htmlspecialchars($response['error']) . ''); - break; - } - $ret = ob_get_contents(); - ob_end_clean(); - echo - $template->getHeader() . - $ret . - $template->getFooter(); - } - - /** - * Preprocessor to turn the form-submitted data from the ACL editor into something the backend can handle - * @param array The posted data - * @return array - * @access private - */ - - public static function acl_preprocess($parms) - { - if ( !isset($parms['mode']) ) - // Nothing to do - return $parms; - switch ( $parms['mode'] ) - { - case 'seltarget': - - // Who's affected? - $parms['target_type'] = intval( $parms['target_type'] ); - $parms['target_id'] = ( $parms['target_type'] == ACL_TYPE_GROUP ) ? $parms['target_id_grp'] : $parms['target_id_user']; - - case 'save_edit': - case 'save_new': - if ( isset($parms['act_delete_rule']) ) - { - $parms['mode'] = 'delete'; - } - - // Scope (just this page or entire site?) - if ( $parms['scope'] == 'entire_site' || ( $parms['page_id'] == 'false' && $parms['namespace'] == 'false' ) ) - { - $parms['page_id'] = false; - $parms['namespace'] = false; - } - else if ( $parms['scope'] == 'page_group' ) - { - $parms['page_id'] = $parms['pg_id']; - $parms['namespace'] = '__PageGroup'; - } - - break; - } - - if ( isset($parms['act_go_stage1']) ) - { - $parms = array( - 'mode' => 'listgroups' - ); - } - - return $parms; - } - - public static function acl_postprocess($response) - { - if(!isset($response['mode'])) - { - if ( isset($response['groups']) ) - $response['mode'] = 'stage1'; - else - $response = Array( - 'mode' => 'error', - 'error' => 'Invalid action passed by API backend.', - ); - } - return $response; - } - + + /** + * Tell if a username is used or not. + * @param $name the name to check for + * @return string + */ + + public static function checkusername($name) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $name = str_replace('_', ' ', $name); + $q = $db->sql_query('SELECT username FROM ' . table_prefix.'users WHERE username=\'' . $db->escape(rawurldecode($name)) . '\''); + if ( !$q ) + { + die($db->get_error()); + } + if ( $db->numrows() < 1) + { + $db->free_result(); return('good'); + } + else + { + $db->free_result(); return('bad'); + } + } + + /** + * Get the wiki formatting source for a page + * @param $page the full page id (Namespace:Pagename) + * @return string + * @todo (DONE) Make it require a password (just for security purposes) + */ + + public static function getsource($page, $password = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if ( !isPage($page) ) + { + return ''; + } + + list($page_id, $namespace) = RenderMan::strToPageID($page); + $ns = namespace_factory($page_id, $namespace); + $cdata = $ns->get_cdata(); + + if ( strlen($cdata['password']) == 40 ) + { + if(!$password || ( $password != $cdata['password'])) + { + return 'invalid_password'; + } + } + + if(!$session->get_permissions('view_source')) // Dependencies handle this for us - this also checks for read privileges + return 'access_denied'; + $pid = RenderMan::strToPageID($page); + if($pid[1] == 'Special' || $pid[1] == 'Admin') + { + die('This type of page (' . $paths->nslist[$pid[1]] . ') cannot be edited because the page source code is not stored in the database.'); + } + + $e = $db->sql_query('SELECT page_text,char_tag FROM ' . table_prefix.'page_text WHERE page_id=\'' . $pid[0] . '\' AND namespace=\'' . $pid[1] . '\''); + if ( !$e ) + { + $db->_die('The page text could not be selected.'); + } + if( $db->numrows() < 1 ) + { + return ''; //$db->_die('There were no rows in the text table that matched the page text query.'); + } + + $r = $db->fetchrow(); + $db->free_result(); + $message = $r['page_text']; + + return htmlspecialchars($message); + } + + /** + * DEPRECATED. Previously returned the full rendered contents of a page. + * @param $page the full page id (Namespace:Pagename) + * @param $send_headers true if the theme headers should be sent (still dependent on current page settings), false otherwise + * @return string + */ + + public static function getpage($page, $send_headers = false, $hist_id = false) + { + die('PageUtils->getpage is deprecated.'); + } + + /** + * Writes page data to the database, after verifying permissions and running the XSS filter + * @param $page_id the page ID + * @param $namespace the namespace + * @param $message the text to save + * @return string + */ + + public static function savepage($page_id, $namespace, $message, $summary = 'No edit summary given', $minor = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $page = new PageProcessor($page_id, $namespace); + $cdata = $page->ns->get_cdata(); + return $page->update_page($message, $summary, $minor, $cdata['page_format']); + } + + /** + * Creates a page, both in memory and in the database. + * @param string $page_id + * @param string $namespace + * @return bool true on success, false on failure + */ + + public static function createPage($page_id, $namespace, $name = false, $visible = 1) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(in_array($namespace, Array('Special', 'Admin'))) + { + // echo 'Notice: PageUtils::createPage: You can\'t create a special page in the database
' . print_r($page_data, true) . ''); + + $paths->add_page($page_data); + + $qa = $db->sql_query('INSERT INTO ' . table_prefix.'pages(name,urlname,namespace,visible,protected,delvote_ips) VALUES(\'' . $db->escape($name) . '\', \'' . $db->escape($page_id) . '\', \'' . $namespace . '\', '. ( $visible ? '1' : '0' ) .', ' . $prot . ', \'' . $db->escape(serialize($ips)) . '\');'); + $qb = $db->sql_query('INSERT INTO ' . table_prefix.'page_text(page_id,namespace) VALUES(\'' . $db->escape($page_id) . '\', \'' . $namespace . '\');'); + $qc = $db->sql_query('INSERT INTO ' . table_prefix.'logs(time_id,date_string,log_type,action,author,author_uid,page_id,namespace) VALUES('.time().', \'DEPRECATED\', \'page\', \'create\', \'' . $session->username . '\', ' . $session->user_id . ', \'' . $db->escape($page_id) . '\', \'' . $namespace . '\');'); + + if($qa && $qb && $qc) + return 'good'; + else + { + return $db->get_error(); + } + } + + /** + * Sets the protection level on a page. + * @param $page_id string the page ID + * @param $namespace string the namespace + * @param $level int level of protection - 0 is off, 1 is full, 2 is semi + * @param $reason string why the page is being (un)protected + * @return string - "good" on success, in all other cases, an error string (on query failure, calls $db->_die() ) + */ + public static function protect($page_id, $namespace, $level, $reason) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $page = new PageProcessor($page_id, $namespace); + return $page->protect_page($level, $reason); + } + + /** + * Generates an HTML table with history information in it. + * @param string the page ID + * @param string the namespace + * @param string page password + * @return string + */ + + public static function histlist($page_id, $namespace, $password = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + global $lang; + + if(!$session->get_permissions('history_view')) + return 'Access denied'; + + ob_start(); + + $pname = $paths->get_pathskey($page_id, $namespace); + $ns = namespace_factory($page_id, $namespace); + $cdata = $ns->get_cdata(); + + if ( !isPage($pname) ) + { + return 'DNE'; + } + + if ( isPage($pname) ) + { + $password_exists = ( !empty($cdata['password']) && $cdata['password'] !== sha1('') ); + if ( $password_exists && $password !== $cdata['password'] ) + { + return '
' . $lang->get('history_err_wrong_password') . '
'; + } + } + + $wiki = ( ( $cdata['wiki_mode'] == 2 && getConfig('wiki_mode') == '1') || $cdata['wiki_mode'] == 1) ? true : false; + $prot = ( ( $cdata['protected'] == 2 && $session->user_logged_in && $session->reg_time + 60*60*24*4 < time() ) || $cdata['protected'] == 1) ? true : false; + + $q = 'SELECT log_id,time_id,date_string,page_id,namespace,author,author_uid,u.username,edit_summary,minor_edit FROM ' . table_prefix . "logs AS l\n" + . " LEFT JOIN " . table_prefix . "users AS u\n" + . " ON ( u.user_id = l.author_uid OR u.user_id IS NULL )\n" + . " WHERE log_type='page' AND action='edit' AND page_id='$page_id' AND namespace='$namespace' AND is_draft != 1 ORDER BY time_id DESC;"; + + if ( !($q = $db->sql_query($q)) ) + $db->_die('The history data for the page "' . $paths->cpage['name'] . '" could not be selected.'); + + echo $lang->get('history_page_subtitle') . ' +' . $lang->get('history_col_datetime') . ' | +' . $lang->get('history_col_user') . ' | +' . $lang->get('history_col_minor') . ' | +' . $lang->get('history_col_action_taken') . ' | +' . $lang->get('history_col_extra') . ' | ++ | ||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
' . enano_date(ED_DATE | ED_TIME, intval($r['time_id'])) . ' | '; + + // User + $real_username = $r['author_uid'] > 1 && !empty($r['username']) ? $r['username'] : $r['author']; + $rank_info = $session->get_user_rank($r['author_uid']); + if ( $session->get_permissions('mod_misc') && is_valid_ip($r['author']) && $r['author_uid'] == 1 ) + { + $rc = ' style="cursor: pointer;" title="' . $lang->get('history_tip_rdns') . '" onclick="ajaxReverseDNS(this, \'' . $r['author'] . '\');"'; + } + else + { + $rc = ''; + } + echo 'nslist['User'] . sanitize_page_id($real_username)) ) + { + echo 'class="wikilink-nonexistent"'; + } + echo 'style="' . $rank_info['rank_style'] . '">' . htmlspecialchars($real_username) . ' | '."\n"; + + + // Minor edit + echo ''. (( $r['minor_edit'] ) ? 'M' : '' ) .' | '; + + // Action taken + echo ''; + // Some of these are sanitized at insert-time. Others follow the newer Enano policy of stripping HTML at runtime. + if ($r['action']=='prot') echo $lang->get('history_log_protect') . ' | ' . $lang->get('history_extra_reason') . ' ' . ( $r['edit_summary'] === '__REVERSION__' ? $lang->get('history_extra_protection_reversion') : htmlspecialchars($r['edit_summary']) ); + elseif($r['action']=='unprot') echo $lang->get('history_log_unprotect') . ' | ' . $lang->get('history_extra_reason') . ' ' . ( $r['edit_summary'] === '__REVERSION__' ? $lang->get('history_extra_protection_reversion') : htmlspecialchars($r['edit_summary']) ); + elseif($r['action']=='semiprot') echo $lang->get('history_log_semiprotect') . ' | ' . $lang->get('history_extra_reason') . ' ' . ( $r['edit_summary'] === '__REVERSION__' ? $lang->get('history_extra_protection_reversion') : htmlspecialchars($r['edit_summary']) ); + elseif($r['action']=='rename') echo $lang->get('history_log_rename') . ' | ' . $lang->get('history_extra_oldtitle') . ' '.htmlspecialchars($r['edit_summary']); + elseif($r['action']=='create') echo $lang->get('history_log_create') . ' | '; + elseif($r['action']=='delete') echo $lang->get('history_log_delete') . ' | ' . $lang->get('history_extra_reason') . ' ' . $r['edit_summary']; + elseif($r['action']=='reupload') echo $lang->get('history_log_uploadnew') . ' | ' . $lang->get('history_extra_reason') . ' ' . ( $r['edit_summary'] === '__ROLLBACK__' ? $lang->get('history_extra_upload_reversion') : htmlspecialchars($r['edit_summary']) ); + elseif($r['action']=='votereset')echo $lang->get('history_log_votereset') . ' | ' . $lang->get('history_extra_numvotes') . ' ' . $r['edit_summary']; + echo ' | '; + + // Actions! + echo '' . $lang->get('history_action_contrib') . ' | '; + echo '' . $lang->get('history_action_revert') . ' | '; + + echo '
'; + $_ob .= ( $n == 0 ) ? $lang->get('comment_msg_count_zero', $subst) : ( $n == 1 ? $lang->get('comment_msg_count_one', $subst) : $lang->get('comment_msg_count_plural', $subst) ); + + if ( $session->get_permissions('mod_comments') && $num_unapp > 0 ) + { + $_ob .= ' ' . $lang->get('comment_msg_count_unapp_mod', array( 'num_unapp' => $num_unapp )) . ''; + } + else if ( !$session->get_permissions('mod_comments') && $num_unapp > 0 ) + { + $ls = ( $num_unapp == 1 ) ? 'comment_msg_count_unapp_one' : 'comment_msg_count_unapp_plural'; + $_ob .= ' ' . $lang->get($ls, array( 'num_unapp' => $num_unapp )) . ''; + } + $_ob .= '
'; + $list = 'list = { '; + // _die(htmlspecialchars($ttext)); + $i = -1; + while ( $row = $db->fetchrow($lq) ) + { + $i++; + $strings = Array(); + $bool = Array(); + if ( $session->get_permissions('mod_comments') || $row['approved'] == COMMENT_APPROVED ) + { + $list .= $i . ' : { \'comment\' : unescape(\''.rawurlencode($row['comment_data']).'\'), \'name\' : unescape(\''.rawurlencode($row['name']).'\'), \'subject\' : unescape(\''.rawurlencode($row['subject']).'\'), }, '; + + // Comment ID (used in the Javascript apps) + $strings['ID'] = (string)$i; + + // Determine the name, and whether to link to the user page or not + $name = ''; + if($row['user_id'] > 1) $name .= ''; + $name .= $row['name']; + if($row['user_id'] > 1) $name .= ''; + $strings['NAME'] = $name; unset($name); + + // Subject + $s = $row['subject']; + if(!$row['approved']) $s .= ' ' . $lang->get('comment_msg_note_unapp') . ''; + $strings['SUBJECT'] = $s; + + // Date and time + $strings['DATETIME'] = enano_date(ED_DATE | ED_TIME, $row['time']); + + // User level + switch($row['user_level']) + { + default: + case USER_LEVEL_GUEST: + $l = $lang->get('user_type_guest'); + break; + case USER_LEVEL_MEMBER: + case USER_LEVEL_CHPREF: + $l = $lang->get('user_type_member'); + break; + case USER_LEVEL_MOD: + $l = $lang->get('user_type_mod'); + break; + case USER_LEVEL_ADMIN: + $l = $lang->get('user_type_admin'); + break; + } + $strings['USER_LEVEL'] = $l; unset($l); + + // The actual comment data + $strings['DATA'] = RenderMan::render($row['comment_data']); + + if($session->get_permissions('edit_comments')) + { + // Edit link + $strings['EDIT_LINK'] = '' . $lang->get('comment_btn_edit') . ''; + + // Delete link + $strings['DELETE_LINK'] = '' . $lang->get('comment_btn_delete') . ''; + } + else + { + // Edit link + $strings['EDIT_LINK'] = ''; + + // Delete link + $strings['DELETE_LINK'] = ''; + } + + // Send PM link + $strings['SEND_PM_LINK'] = ( $session->user_logged_in && $row['user_id'] > 1 ) ? '' . $lang->get('comment_btn_send_privmsg') . 'You need to be logged in to post comments. Log in
'; + } + $list .= '};'; + echo 'document.getElementById(\'ajaxEditContainer\').innerHTML = unescape(\''. rawurlencode($_ob) .'\'); + ' . $list; + echo 'Fat.fade_all(); document.getElementById(\'mdgCommentForm\').style.display = \'none\'; document.getElementById(\'mdgCommentFormLink\').style.display="inline";'; + + $ret = ob_get_contents(); + ob_end_clean(); + return Array($ret, $_ob); + + } + + /** + * Generates ready-to-execute Javascript code to be eval'ed by the user's browser to display comments + * @param $page_id the page ID + * @param $namespace the namespace + * @param $action administrative action to perform, default is false + * @param $flags additional info for $action, shouldn't be used except when deleting/approving comments, etc. + * @param $_ob text to prepend to output, used by PageUtils::addcomment + * @return string + */ + + public static function comments($page_id, $namespace, $action = false, $id = -1, $_ob = '') + { + global $db, $session, $paths, $template, $plugins; // Common objects + $r = PageUtils::comments_raw($page_id, $namespace, $action, $id, $_ob); + return $r[0]; + } + + /** + * Generates HTML code for comments - used in browser compatibility mode + * @param $page_id the page ID + * @param $namespace the namespace + * @param $action administrative action to perform, default is false + * @param $flags additional info for $action, shouldn't be used except when deleting/approving comments, etc. + * @param $_ob text to prepend to output, used by PageUtils::addcomment + * @return string + */ + + public static function comments_html($page_id, $namespace, $action = false, $id = -1, $_ob = '') + { + global $db, $session, $paths, $template, $plugins; // Common objects + $r = PageUtils::comments_raw($page_id, $namespace, $action, $id, $_ob); + return $r[1]; + } + + /** + * Updates comment data. + * @param $page_id the page ID + * @param $namespace the namespace + * @param $subject new subject + * @param $text new text + * @param $old_subject the old subject, unprocessed and identical to the value in the DB + * @param $old_text the old text, unprocessed and identical to the value in the DB + * @param $id the javascript list ID, used internally by the client-side app + * @return string + */ + + public static function savecomment($page_id, $namespace, $subject, $text, $old_subject, $old_text, $id = -1) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$session->get_permissions('edit_comments')) + return 'result="BAD";error="Access denied"'; + // Avoid SQL injection + $old_text = $db->escape($old_text); + $old_subject = $db->escape($old_subject); + // Safety check - username/login + if(!$session->get_permissions('mod_comments')) // allow mods to edit comments + { + if(!$session->user_logged_in) _die('AJAX comment save safety check failed because you are not logged in. Sometimes this can happen because you are using a browser that does not send cookies as part of AJAX requests.'.htmlspecialchars($q).''); + $r = $db->fetchrow($s); + $db->free_result(); + if($db->numrows() < 1 || $r['name'] != $session->username) _die('Safety check failed, probably due to a hacking attempt.'); + } + $s = RenderMan::preprocess_text($subject); + $t = RenderMan::preprocess_text($text); + $sql = 'UPDATE ' . table_prefix.'comments SET subject=\'' . $s . '\',comment_data=\'' . $t . '\' WHERE comment_data=\'' . $old_text . '\' AND subject=\'' . $old_subject . '\' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\''; + $result = $db->sql_query($sql); + if($result) + { + return 'result="GOOD"; + list[' . $id . '][\'subject\'] = unescape(\''.str_replace('%5Cn', '%0A', rawurlencode(str_replace('{{EnAnO:Newline}}', '\\n', stripslashes(str_replace('\\n', '{{EnAnO:Newline}}', $s))))).'\'); + list[' . $id . '][\'comment\'] = unescape(\''.str_replace('%5Cn', '%0A', rawurlencode(str_replace('{{EnAnO:Newline}}', '\\n', stripslashes(str_replace('\\n', '{{EnAnO:Newline}}', $t))))).'\'); id = ' . $id . '; + s = unescape(\''.rawurlencode($s).'\'); + t = unescape(\''.str_replace('%5Cn', '
'.htmlspecialchars($q).''); + $r = $db->fetchrow($s); + if($db->numrows() < 1 || $r['name'] != $session->username) _die('Safety check failed, probably due to a hacking attempt.'); + $db->free_result(); + } + $s = RenderMan::preprocess_text($subject); + $t = RenderMan::preprocess_text($text); + $sql = 'UPDATE ' . table_prefix.'comments SET subject=\'' . $s . '\',comment_data=\'' . $t . '\' WHERE comment_id=' . $id . ' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\''; + $result = $db->sql_query($sql); + if($result) + return 'good'; + else return 'Enano encountered a problem whilst saving the comment. + Performed SQL: + ' . $sql . ' + + Error returned by MySQL: '.$db->get_error(); + } + + /** + * Deletes a comment. + * @param $page_id the page ID + * @param $namespace the namespace + * @param $name the name the user posted under + * @param $subj the subject of the comment to be deleted + * @param $text the text of the comment to be deleted + * @param $id the javascript list ID, used internally by the client-side app + * @return string + */ + + public static function deletecomment($page_id, $namespace, $name, $subj, $text, $id) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + if(!$session->get_permissions('edit_comments')) + return 'alert("Access to delete/edit comments is denied");'; + + if(!preg_match('#^([0-9]+)$#', (string)$id)) die('$_GET[id] is improperly formed.'); + $n = $db->escape($name); + $s = $db->escape($subj); + $t = $db->escape($text); + + // Safety check - username/login + if(!$session->get_permissions('mod_comments')) // allows mods to delete comments + { + if(!$session->user_logged_in) _die('AJAX comment save safety check failed because you are not logged in. Sometimes this can happen because you are using a browser that does not send cookies as part of AJAX requests.
'.htmlspecialchars($q).''); + $r = $db->fetchrow($s); + if($db->numrows() < 1 || $r['name'] != $session->username) _die('Safety check failed, probably due to a hacking attempt.'); + $db->free_result(); + } + $q = 'DELETE FROM ' . table_prefix.'comments WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND name=\'' . $n . '\' AND subject=\'' . $s . '\' AND comment_data=\'' . $t . '\' LIMIT 1;'; + $e=$db->sql_query($q); + if(!$e) return('alert(unesape(\''.rawurlencode('Error during query: '.$db->get_error().'\n\nQuery:\n' . $q) . '\'));'); + return('good'); + } + + /** + * Deletes a comment in a cleaner fashion. + * @param $page_id the page ID + * @param $namespace the namespace + * @param $id the comment ID (primary key) + * @return string + */ + + public static function deletecomment_neater($page_id, $namespace, $id) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + if(!preg_match('#^([0-9]+)$#', (string)$id)) die('$_GET[id] is improperly formed.'); + + if(!$session->get_permissions('edit_comments')) + return 'alert("Access to delete/edit comments is denied");'; + + // Safety check - username/login + if(!$session->get_permissions('mod_comments')) // allows mods to delete comments + { + if(!$session->user_logged_in) _die('AJAX comment save safety check failed because you are not logged in. Sometimes this can happen because you are using a browser that does not send cookies as part of AJAX requests.
'.htmlspecialchars($q).''); + $r = $db->fetchrow($s); + if($db->numrows() < 1 || $r['name'] != $session->username) _die('Safety check failed, probably due to a hacking attempt.'); + $db->free_result(); + } + $q = 'DELETE FROM ' . table_prefix.'comments WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND comment_id=' . $id . ' LIMIT 1;'; + $e=$db->sql_query($q); + if(!$e) return('alert(unesape(\''.rawurlencode('Error during query: '.$db->get_error().'\n\nQuery:\n' . $q) . '\'));'); + return('good'); + } + + /** + * Renames a page. + * @param $page_id the page ID + * @param $namespace the namespace + * @param $name the new name for the page + * @return string error string or success message + */ + + public static function rename($page_id, $namespace, $name) + { + global $db, $session, $paths, $template, $plugins; // Common objects + global $lang; + + $page = new PageProcessor($page_id, $namespace); + return $page->rename_page($name); + } + + /** + * Flushes (clears) the action logs for a given page + * @param $page_id the page ID + * @param $namespace the namespace + * @return string error/success string + */ + + public static function flushlogs($page_id, $namespace) + { + global $db, $session, $paths, $template, $plugins; // Common objects + global $lang; + if ( !is_object($lang) && defined('IN_ENANO_INSTALL') ) + { + // This is a special exception for the Enano installer, which doesn't init languages yet. + $lang = new Language('eng'); + } + if(!$session->get_permissions('clear_logs') && !defined('IN_ENANO_INSTALL')) + { + return $lang->get('etc_access_denied'); + } + if ( !$session->sid_super ) + { + return $lang->get('etc_access_denied_need_reauth'); + } + + $page_id_db = $db->escape($page_id); + $namespace_db = $db->escape($namespace); + + // If we're flushing a file, also clear all revisions before the current + if ( $namespace == 'File' ) + { + $q = $db->sql_query('SELECT file_id FROM ' . table_prefix . "files WHERE page_id='$page_id_db' ORDER BY time_id DESC;"); + if ( !$q ) + $db->_die(); + // discard first row (current revision) + $db->fetchrow(); + $id_list = array(); + while ( $row = $db->fetchrow() ) + $id_list[] = $row['file_id']; + + require_once(ENANO_ROOT . '/includes/namespaces/file.php'); + + // clear out each file + foreach ( $id_list as $id ) + Namespace_File::delete_file($id); + } + + $q = $db->sql_query('DELETE FROM ' . table_prefix . "logs WHERE page_id='$page_id_db' AND namespace='$namespace';"); + if ( !$q ) + $db->_die('The log entries could not be deleted.'); + + // If the page exists, make a backup of it in case it gets spammed/vandalized + // If not, the admin's probably deleting a trash page + if ( isPage($paths->get_pathskey($page_id, $namespace)) ) + { + $q = $db->sql_query('SELECT page_text,char_tag FROM ' . table_prefix . "page_text WHERE page_id='$page_id_db' AND namespace='$namespace_db';"); + if ( !$q ) + $db->_die('The current page text could not be selected; as a result, creating the backup of the page failed. Please make a backup copy of the page by clicking Edit this page and then clicking Save Changes.'); + $row = $db->fetchrow(); + $db->free_result(); + $minor_edit = ( ENANO_DBLAYER == 'MYSQL' ) ? 'false' : '0'; + $username = $db->escape($session->username); + $q = 'INSERT INTO ' . table_prefix . "logs ( log_type, action, time_id, date_string, page_id, namespace, page_text, char_tag, author, author_uid, edit_summary, minor_edit ) VALUES\n" + . " ('page', 'edit', " . time() . ", 'DEPRECATED', '$page_id', '$namespace', '" . $db->escape($row['page_text']) . "', '', '{$username}', $session->user_id, '" . $lang->get('page_flushlogs_backup_summary') . "', $minor_edit);"; + if ( !$db->sql_query($q) ) + $db->_die('The history (log) entry could not be inserted into the logs table.'); + } + + return $lang->get('ajax_clearlogs_success'); + } + + /** + * Deletes a page. + * @param string $page_id the condemned page ID + * @param string $namespace the condemned namespace + * @param string The reason for deleting the page in question + * @return string + */ + + public static function deletepage($page_id, $namespace, $reason) + { + global $db, $session, $paths, $template, $plugins; // Common objects + global $lang; + global $cache; + $perms = $session->fetch_page_acl($page_id, $namespace); + $x = trim($reason); + if ( empty($x) ) + { + return $lang->get('ajax_delete_need_reason'); + } + if(!$perms->get_permissions('delete_page')) return('Administrative privileges are required to delete pages, you loser.'); + + if ( !$session->sid_super ) + { + return $lang->get('etc_access_denied_need_reauth'); + } + + $e = $db->sql_query('INSERT INTO ' . table_prefix.'logs(time_id,date_string,log_type,action,page_id,namespace,author,author_uid,edit_summary) VALUES('.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \'page\', \'delete\', \'' . $page_id . '\', \'' . $namespace . '\', \'' . $session->username . '\', ' . $session->user_id . ', \'' . $db->escape(htmlspecialchars($reason)) . '\')'); + if(!$e) $db->_die('The page log entry could not be inserted.'); + $e = $db->sql_query('DELETE FROM ' . table_prefix.'categories WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\''); + if(!$e) $db->_die('The page categorization entries could not be deleted.'); + $e = $db->sql_query('DELETE FROM ' . table_prefix.'comments WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\''); + if(!$e) $db->_die('The page comments could not be deleted.'); + $e = $db->sql_query('DELETE FROM ' . table_prefix.'page_text WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\''); + if(!$e) $db->_die('The page text entry could not be deleted.'); + $e = $db->sql_query('DELETE FROM ' . table_prefix.'pages WHERE urlname=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\''); + if(!$e) $db->_die('The page entry could not be deleted.'); + if ( $namespace == 'File' ) + { + $e = $db->sql_query('DELETE FROM ' . table_prefix.'files WHERE page_id=\'' . $page_id . '\''); + if(!$e) $db->_die('The file entry could not be deleted.'); + } + $cache->purge('page_meta'); + return $lang->get('ajax_delete_success'); + } + + /** + * Deletes files associated with a File page. + * @param string Page ID + */ + + public static function delete_page_files($page_id) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $q = $db->sql_query('SELECT file_id, filename, file_key, time_id, file_extension FROM ' . table_prefix . "files WHERE page_id = '{$db->escape($page_id)}';"); + if ( !$q ) + $db->_die(); + + while ( $row = $db->fetchrow() ) + { + // wipe original file + foreach ( array( + ENANO_ROOT . "/files/{$row['file_key']}_{$row['time_id']}{$row['file_extension']}", + ENANO_ROOT . "/files/{$row['file_key']}{$row['file_extension']}" + ) as $orig_file ) + { + if ( file_exists($orig_file) ) + @unlink($orig_file); + } + + // wipe cached files + if ( $dr = @opendir(ENANO_ROOT . '/cache/') ) + { + // lol404.jpg-1217958283-200x320.jpg + while ( $dh = @readdir($dr) ) + { + $regexp = ':^' . preg_quote("{$row['filename']}-{$row['time_id']}-") . '[0-9]+x[0-9]+\.' . ltrim($row['file_extension'], '.') . '$:'; + if ( preg_match($regexp, $dh) ) + { + @unlink(ENANO_ROOT . "/cache/$dh"); + } + } + @closedir($dr); + } + } + + $q = $db->sql_query('DELETE FROM ' . table_prefix . "files WHERE page_id = '{$db->escape($page_id)}';"); + if ( !$q ) + $db->die(); + + return true; + } + + /** + * Increments the deletion votes for a page by 1, and adds the current username/IP to the list of users that have voted for the page to prevent dual-voting + * @param $page_id the page ID + * @param $namespace the namespace + * @return string + */ + + public static function delvote($page_id, $namespace) + { + global $db, $session, $paths, $template, $plugins; // Common objects + global $lang; + global $cache; + + if ( !$session->get_permissions('vote_delete') ) + { + return $lang->get('etc_access_denied'); + } + + if ( $namespace == 'Admin' || $namespace == 'Special' || $namespace == 'System' ) + { + return 'Special pages and system messages can\'t be voted for deletion.'; + } + + $pname = $paths->nslist[$namespace] . sanitize_page_id($page_id); + + if ( !isPage($pname) ) + { + return 'The page does not exist.'; + } + + $ns = namespace_factory($page_id, $namespace); + $cdata = $ns->get_cdata(); + + $cv =& $cdata['delvotes']; + $ips =& $cdata['delvote_ips']; + + if ( empty($ips) ) + { + $ips = array( + 'ip' => array(), + 'u' => array() + ); + } + else + { + $ips = @unserialize($ips); + if ( !$ips ) + { + $ips = array( + 'ip' => array(), + 'u' => array() + ); + } + } + + if ( in_array($session->username, $ips['u']) || in_array($_SERVER['REMOTE_ADDR'], $ips['ip']) ) + { + return $lang->get('ajax_delvote_already_voted'); + } + + $ips['u'][] = $session->username; + $ips['ip'][] = $_SERVER['REMOTE_ADDR']; + $ips = $db->escape( serialize($ips) ); + + $cv++; + + $q = 'UPDATE ' . table_prefix.'pages SET delvotes=' . $cv . ',delvote_ips=\'' . $ips . '\' WHERE urlname=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\''; + $w = $db->sql_query($q); + if ( !$w ) + $db->_die(); + + // all done, flush page cache to mark it up + $cache->purge('page_meta'); + + return $lang->get('ajax_delvote_success'); + } + + /** + * Resets the number of votes against a page to 0. + * @param $page_id the page ID + * @param $namespace the namespace + * @return string + */ + + public static function resetdelvotes($page_id, $namespace) + { + global $db, $session, $paths, $template, $plugins; // Common objects + global $lang; + global $cache; + + if ( !$session->get_permissions('vote_reset') ) + { + return $lang->get('etc_access_denied'); + } + + $page_id = $db->escape($page_id); + $namespace = $db->escape($namespace); + + // pull existing info + $q = $db->sql_query('SELECT delvotes, delvote_ips FROM ' . table_prefix . "pages WHERE urlname = '$page_id' AND namespace = '$namespace'"); + if ( !$q ) + $db->_die(); + if ( $db->numrows() < 1 ) + return $lang->get('page_err_page_not_exist'); + + list($delvotes, $delvote_ips) = $db->fetchrow_num(); + $db->free_result(); + $delvote_ips = $db->escape($delvote_ips); + $username = $db->escape($session->username); + + // log action + $time = time(); + $q = $db->sql_query('INSERT INTO ' . table_prefix . "logs (time_id, log_type, action, edit_summary, page_text, author, author_uid, page_id, namespace) VALUES\n" + . " ( $time, 'page', 'votereset', '$delvotes', '$delvote_ips', '$username', $session->user_id, '$page_id', '$namespace' )"); + if ( !$q ) + $db->_die(); + + // reset votes + $empty_vote_record = $db->escape(serialize(array('ip'=>array(),'u'=>array()))); + $q = 'UPDATE ' . table_prefix.'pages SET delvotes=0,delvote_ips=\'' . $empty_vote_record . '\' WHERE urlname=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\''; + $e = $db->sql_query($q); + if ( !$e ) + { + $db->_die('The number of delete votes was not reset.'); + } + else + { + $cache->purge('page_meta'); + return $lang->get('ajax_delvote_reset_success'); + } + } + + /** + * Gets a list of styles for a given theme name. As of Banshee, this returns JSON. + * @param $id the name of the directory for the theme + * @return string JSON string with an array containing a list of themes + */ + + public static function getstyles() + { + + if ( !preg_match('/^([a-z0-9_-]+)$/', $_GET['id']) ) + return enano_json_encode(false); + + $dir = './themes/' . $_GET['id'] . '/css/'; + $list = Array(); + // Open a known directory, and proceed to read its contents + if (is_dir($dir)) { + if ($dh = opendir($dir)) { + while (($file = readdir($dh)) !== false) { + if ( preg_match('#^(.*?)\.css$#is', $file) && $file != '_printable.css' ) // _printable.css should be included with every theme + { // it should be a copy of the original style, but + // mostly black and white + // Note to self: document this + $list[] = substr($file, 0, strlen($file)-4); + } + } + closedir($dh); + } + } + else + { + return(enano_json_encode(Array('mode' => 'error', 'error' => $dir.' is not a dir'))); + } + + return enano_json_encode($list); + } + + /** + * Assembles a Javascript app with category information + * @param $page_id the page ID + * @param $namespace the namespace + * @return string Javascript code + */ + + public static function catedit($page_id, $namespace) + { + $d = PageUtils::catedit_raw($page_id, $namespace); + return $d[0] . ' /* BEGIN CONTENT */ document.getElementById("ajaxEditContainer").innerHTML = unescape(\''.rawurlencode($d[1]).'\');'; + } + + /** + * Does the actual HTML/javascript generation for cat editing, but returns an array + * @access private + */ + + public static function catedit_raw($page_id, $namespace) + { + global $db, $session, $paths, $template, $plugins; // Common objects + global $lang; + + ob_start(); + $_ob = ''; + $e = $db->sql_query('SELECT category_id FROM ' . table_prefix.'categories WHERE page_id=\'' . $paths->page_id . '\' AND namespace=\'' . $paths->namespace . '\''); + if(!$e) jsdie('Error selecting category information for current page: '.$db->get_error()); + $cat_current = Array(); + while($r = $db->fetchrow()) + { + $cat_current[] = $r; + } + $db->free_result(); + + $cat_all = array(); + $q = $db->sql_query('SELECT * FROM ' . table_prefix . 'pages WHERE namespace = \'Category\';'); + if ( !$q ) + $db->_die(); + + while ( $row = $db->fetchrow() ) + { + $cat_all[] = Namespace_Default::bake_cdata($row); + } + + // Make $cat_all an associative array, like $paths->pages + $sz = sizeof($cat_all); + for($i=0;$i<$sz;$i++) + { + $cat_all[$cat_all[$i]['urlname_nons']] = $cat_all[$i]; + } + // Now, the "zipper" function - join the list of categories with the list of cats that this page is a part of + $cat_info = $cat_all; + for($i=0;$i
" . $lang->get('history_lbl_comparingrevisions') . " {$time1} → {$time2}
+ "; + // Free some memory + unset($row1, $row2, $q1, $q2); + + $_ob .= RenderMan::diff($text1, $text2); + return $_ob; + } + + /** + * Gets ACL information about the selected page for target type X and target ID Y. + * @param array $parms What to select. This is an array purely for JSON compatibility. It should be an associative array with keys target_type and target_id. + * @return array + */ + + public static function acl_editor($parms = Array()) + { + global $db, $session, $paths, $template, $plugins; // Common objects + global $lang; + + if(!$session->get_permissions('edit_acl') && ( $session->user_level < USER_LEVEL_ADMIN || !defined('ACL_ALWAYS_ALLOW_ADMIN_EDIT_ACL')) ) + { + return Array( + 'mode' => 'error', + 'error' => $lang->get('acl_err_access_denied') + ); + } + if ( !$session->sid_super ) + { + return Array( + 'mode' => 'error', + 'error' => $lang->get('etc_access_denied_need_reauth') + ); + } + $parms['page_id'] = ( isset($parms['page_id']) ) ? $parms['page_id'] : false; + $parms['namespace'] = ( isset($parms['namespace']) ) ? $parms['namespace'] : false; + $page_id =& $parms['page_id']; + $namespace =& $parms['namespace']; + $page_where_clause = ( empty($page_id) || empty($namespace) ) ? 'AND a.page_id IS NULL AND a.namespace IS NULL' : 'AND a.page_id=\'' . $db->escape($page_id) . '\' AND a.namespace=\'' . $db->escape($namespace) . '\''; + $page_where_clause_lite = ( empty($page_id) || empty($namespace) ) ? 'AND page_id IS NULL AND namespace IS NULL' : 'AND page_id=\'' . $db->escape($page_id) . '\' AND namespace=\'' . $db->escape($namespace) . '\''; + //die(print_r($page_id,true)); + $template->load_theme(); + // $perms_obj = $session->fetch_page_acl($page_id, $namespace); + $perms_obj =& $session; + $return = Array(); + if ( !file_exists(ENANO_ROOT . '/themes/' . $session->theme . '/acledit.tpl') ) + { + return Array( + 'mode' => 'error', + 'error' => $lang->get('acl_err_missing_template'), + ); + } + $return['template'] = $template->extract_vars('acledit.tpl'); + $return['page_id'] = $page_id; + $return['namespace'] = $namespace; + if(isset($parms['mode'])) + { + switch($parms['mode']) + { + case 'listgroups': + $return['groups'] = Array(); + $q = $db->sql_query('SELECT group_id,group_name FROM ' . table_prefix.'groups ORDER BY group_name ASC;'); + while($row = $db->fetchrow()) + { + $return['groups'][] = Array( + 'id' => $row['group_id'], + 'name' => $row['group_name'], + ); + } + $db->free_result(); + $return['page_groups'] = Array(); + $q = $db->sql_query('SELECT pg_id,pg_name FROM ' . table_prefix.'page_groups ORDER BY pg_name ASC;'); + if ( !$q ) + return Array( + 'mode' => 'error', + 'error' => $db->get_error() + ); + while ( $row = $db->fetchrow() ) + { + $return['page_groups'][] = Array( + 'id' => $row['pg_id'], + 'name' => $row['pg_name'] + ); + } + break; + case 'seltarget_id': + if ( !is_int($parms['target_id']) ) + { + return Array( + 'mode' => 'error', + 'error' => 'Expected parameter target_id type int' + ); + } + $q = $db->sql_query('SELECT target_id, target_type, page_id, namespace, rules FROM ' . table_prefix . "acl WHERE rule_id = {$parms['target_id']};"); + if ( !$q ) + return Array( + 'mode' => 'error', + 'error' => $db->get_error() + ); + if ( $db->numrows() < 1 ) + return Array( + 'mode' => 'error', + 'error' => "No rule with ID {$parms['target_id']} found" + ); + $parms = $db->fetchrow(); + $db->free_result(); + + // regenerate page selection + $parms['page_id'] = ( isset($parms['page_id']) ) ? $parms['page_id'] : false; + $parms['namespace'] = ( isset($parms['namespace']) ) ? $parms['namespace'] : false; + $parms['mode'] = 'seltarget_id'; + $page_id =& $parms['page_id']; + $namespace =& $parms['namespace']; + $page_where_clause = ( empty($page_id) || empty($namespace) ) ? 'AND a.page_id IS NULL AND a.namespace IS NULL' : 'AND a.page_id=\'' . $db->escape($page_id) . '\' AND a.namespace=\'' . $db->escape($namespace) . '\''; + $page_where_clause_lite = ( empty($page_id) || empty($namespace) ) ? 'AND page_id IS NULL AND namespace IS NULL' : 'AND page_id=\'' . $db->escape($page_id) . '\' AND namespace=\'' . $db->escape($namespace) . '\''; + + $return['page_id'] = $parms['page_id']; + $return['namespace'] = $parms['namespace']; + + // From here, let the seltarget handler take over + case 'seltarget': + $return['mode'] = 'seltarget'; + $return['acl_types'] = $perms_obj->acl_types; + $return['acl_deps'] = $perms_obj->acl_deps; + $return['acl_descs'] = $perms_obj->acl_descs; + $return['target_type'] = $parms['target_type']; + $return['target_id'] = $parms['target_id']; + switch($parms['target_type']) + { + case ACL_TYPE_USER: + $user_col = ( $parms['mode'] == 'seltarget_id' ) ? 'user_id' : 'username'; + $q = $db->sql_query('SELECT a.rules,u.user_id,u.username FROM ' . table_prefix.'users AS u + LEFT JOIN ' . table_prefix.'acl AS a + ON a.target_id=u.user_id + WHERE a.target_type='.ACL_TYPE_USER.' + AND u.' . $user_col . ' = \'' . $db->escape($parms['target_id']) . '\' + ' . $page_where_clause . ';'); + if(!$q) + return(Array('mode'=>'error','error'=>$db->get_error())); + if($db->numrows() < 1) + { + $return['type'] = 'new'; + $q = $db->sql_query('SELECT user_id,username FROM ' . table_prefix.'users WHERE username=\'' . $db->escape($parms['target_id']) . '\';'); + if(!$q) + return(Array('mode'=>'error','error'=>$db->get_error())); + if($db->numrows() < 1) + return Array('mode'=>'error','error'=>$lang->get('acl_err_user_not_found'),'debug' => $db->sql_backtrace()); + $row = $db->fetchrow(); + $return['target_name'] = $row['username']; + $return['target_id'] = intval($row['user_id']); + $return['current_perms'] = array(); + } + else + { + $return['type'] = 'edit'; + $row = $db->fetchrow(); + $return['target_name'] = $row['username']; + $return['target_id'] = intval($row['user_id']); + $return['current_perms'] = $session->string_to_perm($row['rules']); + } + $db->free_result(); + // Eliminate types that don't apply to this namespace + if ( $namespace && $namespace != '__PageGroup' ) + { + foreach ( $return['current_perms'] AS $i => $perm ) + { + if ( ( $page_id != null && $namespace != null ) && ( !in_array ( $namespace, $session->acl_scope[$i] ) && !in_array('All', $session->acl_scope[$i]) ) ) + { + // echo "// SCOPE CONTROL: eliminating: $i\n"; + unset($return['current_perms'][$i]); + unset($return['acl_types'][$i]); + unset($return['acl_descs'][$i]); + unset($return['acl_deps'][$i]); + } + } + } + break; + case ACL_TYPE_GROUP: + $q = $db->sql_query('SELECT a.rules,g.group_name,g.group_id FROM ' . table_prefix.'groups AS g + LEFT JOIN ' . table_prefix.'acl AS a + ON a.target_id=g.group_id + WHERE a.target_type='.ACL_TYPE_GROUP.' + AND g.group_id=\''.intval($parms['target_id']).'\' + ' . $page_where_clause . ';'); + if(!$q) + return(Array('mode'=>'error','error'=>$db->get_error())); + if($db->numrows() < 1) + { + $return['type'] = 'new'; + $q = $db->sql_query('SELECT group_id,group_name FROM ' . table_prefix.'groups WHERE group_id=\''.intval($parms['target_id']).'\';'); + if(!$q) + return(Array('mode'=>'error','error'=>$db->get_error())); + if($db->numrows() < 1) + return Array('mode'=>'error','error'=>$lang->get('acl_err_bad_group_id')); + $row = $db->fetchrow(); + $return['target_name'] = $row['group_name']; + $return['target_id'] = intval($row['group_id']); + $return['current_perms'] = array(); + } + else + { + $return['type'] = 'edit'; + $row = $db->fetchrow(); + $return['target_name'] = $row['group_name']; + $return['target_id'] = intval($row['group_id']); + $return['current_perms'] = $session->string_to_perm($row['rules']); + } + $db->free_result(); + // Eliminate types that don't apply to this namespace + if ( $namespace && $namespace != '__PageGroup' ) + { + foreach ( $return['current_perms'] AS $i => $perm ) + { + if ( ( $page_id != false && $namespace != false ) && ( !in_array ( $namespace, $session->acl_scope[$i] ) && !in_array('All', $session->acl_scope[$i]) ) ) + { + // echo "// SCOPE CONTROL: eliminating: $i\n"; //; ".print_r($namespace,true).":".print_r($page_id,true)."\n"; + unset($return['current_perms'][$i]); + unset($return['acl_types'][$i]); + unset($return['acl_descs'][$i]); + unset($return['acl_deps'][$i]); + } + } + } + //return Array('mode'=>'debug','text'=>print_r($return, true)); + break; + default: + return Array('mode'=>'error','error','Invalid ACL type ID'); + break; + } + return $return; + break; + case 'save_new': + case 'save_edit': + if ( defined('ENANO_DEMO_MODE') ) + { + return Array('mode'=>'error','error'=>$lang->get('acl_err_demo')); + } + $q = $db->sql_query('DELETE FROM ' . table_prefix.'acl WHERE target_type='.intval($parms['target_type']).' AND target_id='.intval($parms['target_id']).' + ' . $page_where_clause_lite . ';'); + if(!$q) + return Array('mode'=>'error','error'=>$db->get_error()); + if ( sizeof ( $parms['perms'] ) < 1 ) + { + // As of 1.1.x, this returns success because the rule length is zero if the user selected "inherit" in all columns + return Array( + 'mode' => 'success', + 'target_type' => $parms['target_type'], + 'target_id' => $parms['target_id'], + 'target_name' => $parms['target_name'], + 'page_id' => $page_id, + 'namespace' => $namespace, + ); + } + $rules = $session->perm_to_string($parms['perms']); + $q = ($page_id && $namespace) ? 'INSERT INTO ' . table_prefix.'acl ( target_type, target_id, page_id, namespace, rules ) + VALUES( '.intval($parms['target_type']).', '.intval($parms['target_id']).', \'' . $db->escape($page_id) . '\', \'' . $db->escape($namespace) . '\', \'' . $db->escape($rules) . '\' )' : + 'INSERT INTO ' . table_prefix.'acl ( target_type, target_id, rules ) + VALUES( '.intval($parms['target_type']).', '.intval($parms['target_id']).', \'' . $db->escape($rules) . '\' )'; + if(!$db->sql_query($q)) return Array('mode'=>'error','error'=>$db->get_error()); + return Array( + 'mode' => 'success', + 'target_type' => $parms['target_type'], + 'target_id' => $parms['target_id'], + 'target_name' => $parms['target_name'], + 'page_id' => $page_id, + 'namespace' => $namespace, + ); + break; + case 'delete': + if ( defined('ENANO_DEMO_MODE') ) + { + return Array('mode'=>'error','error'=>$lang->get('acl_err_demo')); + } + $sql = 'DELETE FROM ' . table_prefix.'acl WHERE target_type='.intval($parms['target_type']).' AND target_id='.intval($parms['target_id']).' + ' . $page_where_clause_lite . ';'; + $q = $db->sql_query($sql); + if(!$q) + return Array('mode'=>'error','error'=>$db->get_error()); + return Array( + 'mode' => 'delete', + 'target_type' => $parms['target_type'], + 'target_id' => $parms['target_id'], + 'target_name' => $parms['target_name'], + 'page_id' => $page_id, + 'namespace' => $namespace, + ); + break; + case 'list_existing': + + $return = array( + 'mode' => 'list_existing', + 'key' => acl_list_draw_key(), + 'rules' => array() + ); + + $acl_columns = 'a.' . implode(', a.', $db->columns_in(table_prefix . 'acl')); + $users_columns = 'u.' . implode(', u.', $db->columns_in(table_prefix . 'users')); + $groups_columns = 'g.' . implode(', g.', $db->columns_in(table_prefix . 'groups')); + $pg_columns = 'p.' . implode(', p.', array('pg_id', 'pg_type', 'pg_name', 'pg_target')); + + $q = $db->sql_query("SELECT a.rule_id, u.username, g.group_name, a.target_type, a.target_id, a.page_id, a.namespace, a.rules, p.pg_name\n" + . " FROM " . table_prefix . "acl AS a\n" + . " LEFT JOIN " . table_prefix . "users AS u\n" + . " ON ( (a.target_type = " . ACL_TYPE_USER . " AND a.target_id = u.user_id) OR (u.user_id IS NULL) )\n" + . " LEFT JOIN " . table_prefix . "groups AS g\n" + . " ON ( (a.target_type = " . ACL_TYPE_GROUP . " AND a.target_id = g.group_id) OR (g.group_id IS NULL) )\n" + . " LEFT JOIN " . table_prefix . "page_groups as p\n" + . " ON ( (a.namespace = '__PageGroup' AND a.page_id = CAST(p.pg_id AS CHAR)) OR (p.pg_id IS NULL) )\n" + . " WHERE ( a.target_type = " . ACL_TYPE_USER . " OR a.target_type = " . ACL_TYPE_GROUP . " )\n" + . " GROUP BY a.rule_id, $acl_columns, $users_columns, $groups_columns, $pg_columns\n" + . " ORDER BY a.target_type ASC, a.rule_id ASC;" + ); + + if ( !$q ) + $db->_die(); + + while ( $row = $db->fetchrow($q) ) + { + if ( $row['target_type'] == ACL_TYPE_USER && empty($row['username']) ) + { + // This is only done if we have an ACL affecting a user that doesn't exist. + // Nice little bit of maintenance to have. + if ( !$db->sql_query("DELETE FROM " . table_prefix . "acl WHERE rule_id = {$row['rule_id']};") ) + $db->_die(); + continue; + } + $score = get_acl_rule_score($row['rules']); + $deep_limit = ACL_SCALE_MINIMAL_SHADE; + // Determine background color of cell by score + if ( $score > 5 ) + { + // high score, show in green + $color = 2.5 * $score; + if ( $color > 255 ) + $color = 255; + $color = round($color); + // blend with the colordepth limit + $color = $deep_limit + ( ( 0xFF - $deep_limit ) - ( ( $color / 0xFF ) * ( 0xFF - $deep_limit ) ) ); + $color = dechex($color); + $color = "{$color}ff{$color}"; + } + else if ( $score < -5 ) + { + // low score, show in red + $color = 0 - $score; + $color = 2.5 * $color; + if ( $color > 255 ) + $color = 255; + $color = round($color); + // blend with the colordepth limit + $color = $deep_limit + ( ( 0xFF - $deep_limit ) - ( ( $color / 0xFF ) * ( 0xFF - $deep_limit ) ) ); + $color = dechex($color); + $color = "ff{$color}{$color}"; + } + else + { + $color = 'efefef'; + } + + // Rate rule textually based on its score + if ( $score >= 70 ) + $desc = $lang->get('acl_msg_scale_allow'); + else if ( $score >= 50 ) + $desc = $lang->get('acl_msg_scale_mostly_allow'); + else if ( $score >= 25 ) + $desc = $lang->get('acl_msg_scale_some_allow'); + else if ( $score >= -25 ) + $desc = $lang->get('acl_msg_scale_mixed'); + else if ( $score <= -70 ) + $desc = $lang->get('acl_msg_scale_deny'); + else if ( $score <= -50 ) + $desc = $lang->get('acl_msg_scale_mostly_deny'); + else if ( $score <= -25 ) + $desc = $lang->get('acl_msg_scale_some_deny'); + + // group and user target info + $info = ''; + if ( $row['target_type'] == ACL_TYPE_USER ) + $info = $lang->get('acl_msg_list_user', array( 'username' => $row['username'] )); // "(User: {$row['username']})"; + else if ( $row['target_type'] == ACL_TYPE_GROUP ) + $info = $lang->get('acl_msg_list_group', array( 'group' => $row['group_name'] )); + + // affected pages info + if ( $row['page_id'] && $row['namespace'] && $row['namespace'] != '__PageGroup' ) + $info .= $lang->get('acl_msg_list_on_page', array( 'page_name' => "{$row['namespace']}:{$row['page_id']}" )); + else if ( $row['page_id'] && $row['namespace'] && $row['namespace'] == '__PageGroup' ) + $info .= $lang->get('acl_msg_list_on_page_group', array( 'page_group' => $row['pg_name'] )); + else + $info .= $lang->get('acl_msg_list_entire_site'); + + $score_string = $lang->get('acl_msg_list_score', array + ( + 'score' => $score, + 'desc' => $desc, + 'info' => $info + )); + $return['rules'][] = array( + 'score_string' => $score_string, + 'rule_id' => $row['rule_id'], + 'color' => $color + ); + } + + break; + case 'list_presets': + $presets = array(); + $q = $db->sql_query('SELECT page_id AS preset_name, rule_id, rules FROM ' . table_prefix . "acl WHERE target_type = " . ACL_TYPE_PRESET . ";"); + if ( !$q ) + $db->die_json(); + + while ( $row = $db->fetchrow() ) + { + $row['rules'] = $session->string_to_perm($row['rules']); + $presets[] = $row; + } + + return array( + 'mode' => 'list_existing', + 'presets' => $presets + ); + break; + case 'save_preset': + if ( empty($parms['preset_name']) ) + { + return array( + 'mode' => 'error', + 'error' => $lang->get('acl_err_preset_name_empty') + ); + } + $preset_name = $db->escape($parms['preset_name']); + $q = $db->sql_query('DELETE FROM ' . table_prefix . "acl WHERE target_type = " . ACL_TYPE_PRESET . " AND page_id = '$preset_name';"); + if ( !$q ) + $db->die_json(); + + $perms = $session->perm_to_string($parms['perms']); + if ( !$perms ) + { + return array( + 'mode' => 'error', + 'error' => $lang->get('acl_err_preset_is_blank') + ); + } + + $perms = $db->escape($perms); + $q = $db->sql_query('INSERT INTO ' . table_prefix . "acl(page_id, target_type, rules) VALUES\n" + . " ( '$preset_name', " . ACL_TYPE_PRESET . ", '$perms' );"); + if ( !$q ) + $db->die_json(); + + return array( + 'mode' => 'success' + ); + break; + case 'trace': + list($targetpid, $targetns) = RenderMan::strToPageID($parms['page']); + try + { + $perms = $session->fetch_page_acl_user($parms['user'], $targetpid, $targetns); + $perm_table = array( + AUTH_ALLOW => 'acl_lbl_field_allow', + AUTH_WIKIMODE => 'acl_lbl_field_wikimode', + AUTH_DISALLOW => 'acl_lbl_field_disallow', + AUTH_DENY => 'acl_lbl_field_deny' + ); + + $return = array( + 'mode' => 'trace', + 'perms' => array() + ); + + foreach ( $perms->perm_resolve_table as $perm_type => $lookup_data ) + { + if ( !$session->check_acl_scope($perm_type, $targetns) ) + continue; + + $src_l10n = $lang->get($session->acl_inherit_lang_table[$lookup_data['src']], $lookup_data); + $divclass = preg_replace('/^acl_inherit_/', '', $session->acl_inherit_lang_table[$lookup_data['src']]); + $perm_string = $lang->get($perm_table[$perms->perms[$perm_type]]); + $perm_name = $lang->get($session->acl_descs[$perm_type]); + + $return['perms'][$perm_type] = array( + 'divclass' => "acl_inherit acl_$divclass", + 'perm_type' => $perm_type, + 'perm_name' => $perm_name, + 'perm_value' => $perm_string, + 'perm_src' => $src_l10n, + 'rule_id' => intval($lookup_data['rule_id']), + 'bad_deps' => $perms->acl_check_deps($perm_type, true) + ); + } + + // group rules if possible + $return['groups'] = array(); + foreach ( $return['perms'] as $rule ) + { + if ( !isset($return['groups'][$rule['rule_id']]) ) + { + $return['groups'][$rule['rule_id']] = array(); + } + $return['groups'][$rule['rule_id']][] = $rule['perm_type']; + } + } + catch ( Exception $e ) + { + $return = array( + 'mode' => 'error', + 'error' => $e->getMessage() + ); + } + + break; + default: + return Array('mode'=>'error','error'=>'Hacking attempt'); + break; + } + } + return $return; + } + + /** + * Same as PageUtils::acl_editor(), but the parms are a JSON string instead of an array. This also returns a JSON string. + * @param string $parms Same as PageUtils::acl_editor/$parms, but should be a valid JSON string. + * @return string + */ + + public static function acl_json($parms = '{ }') + { + global $db, $session, $paths, $template, $plugins; // Common objects + try + { + $parms = enano_json_decode($parms); + } + catch ( Zend_Json_Exception $e ) + { + $parms = array(); + } + $ret = PageUtils::acl_editor($parms); + $ret = enano_json_encode($ret); + return $ret; + } + + /** + * A non-Javascript frontend for the ACL API. + * @param array The request data, if any, this should be in the format required by PageUtils::acl_editor() + */ + + public static function aclmanager($parms) + { + global $db, $session, $paths, $template, $plugins; // Common objects + global $lang; + ob_start(); + // Convenience + $formstart = ''; + $parms = PageUtils::acl_preprocess($parms); + $response = PageUtils::acl_editor($parms); + $response = PageUtils::acl_postprocess($response); + + //die('' . htmlspecialchars(print_r($response, true)) . ''); + + switch($response['mode']) + { + case 'debug': + echo '
' . htmlspecialchars($response['text']) . ''; + break; + case 'stage1': + echo '
' . $lang->get('acl_lbl_welcome_body') . '
'; + echo $formstart; + echo ' +' . $template->username_field('data[target_id_user]') . '
+' . $lang->get('acl_lbl_scope') . '
+ + ' . $groupsel . ' + +Error returned by permissions API:
' . htmlspecialchars($response['error']) . ''); + break; + } + $ret = ob_get_contents(); + ob_end_clean(); + echo + $template->getHeader() . + $ret . + $template->getFooter(); + } + + /** + * Preprocessor to turn the form-submitted data from the ACL editor into something the backend can handle + * @param array The posted data + * @return array + * @access private + */ + + public static function acl_preprocess($parms) + { + if ( !isset($parms['mode']) ) + // Nothing to do + return $parms; + switch ( $parms['mode'] ) + { + case 'seltarget': + + // Who's affected? + $parms['target_type'] = intval( $parms['target_type'] ); + $parms['target_id'] = ( $parms['target_type'] == ACL_TYPE_GROUP ) ? $parms['target_id_grp'] : $parms['target_id_user']; + + case 'save_edit': + case 'save_new': + if ( isset($parms['act_delete_rule']) ) + { + $parms['mode'] = 'delete'; + } + + // Scope (just this page or entire site?) + if ( $parms['scope'] == 'entire_site' || ( $parms['page_id'] == 'false' && $parms['namespace'] == 'false' ) ) + { + $parms['page_id'] = false; + $parms['namespace'] = false; + } + else if ( $parms['scope'] == 'page_group' ) + { + $parms['page_id'] = $parms['pg_id']; + $parms['namespace'] = '__PageGroup'; + } + + break; + } + + if ( isset($parms['act_go_stage1']) ) + { + $parms = array( + 'mode' => 'listgroups' + ); + } + + return $parms; + } + + public static function acl_postprocess($response) + { + if(!isset($response['mode'])) + { + if ( isset($response['groups']) ) + $response['mode'] = 'stage1'; + else + $response = Array( + 'mode' => 'error', + 'error' => 'Invalid action passed by API backend.', + ); + } + return $response; + } + } /** @@ -2462,32 +2462,32 @@ function acl_list_draw_key() { - $out = '