# HG changeset patch # User Dan # Date 1185660538 14400 # Node ID 608dee512bf0ecf42108af30a7e2272918b77938 # Parent 1f85c1c609fdf2ef8218bbecfe6cb0e33fa07975 Work started on page tags, still aways to go, but syncing to Nighthawk diff -r 1f85c1c609fd -r 608dee512bf0 ajax.php --- a/ajax.php Wed Jul 25 18:09:21 2007 -0400 +++ b/ajax.php Sat Jul 28 18:08:58 2007 -0400 @@ -104,11 +104,6 @@ case "setpass": echo PageUtils::setpass($paths->cpage['urlname_nons'], $paths->namespace, $_POST['password']); break; - case "wikihelp": - $html = file_get_contents('http://enanocms.org/ajax.php?title=Help:Wiki_formatting&_mode=getpage&nofooters'); - $html = str_replace('src="/Special', 'src="http://enanocms.org/Special', $html); - echo '

Wiki formatting guide

'.$html.'
'; - break; case "fillusername": $name = (isset($_GET['name'])) ? $db->escape($_GET['name']) : false; if ( !$name ) @@ -225,6 +220,34 @@ die( $db->get_error() ); die('GOOD'); break; + case 'get_tags': + $json = new Services_JSON(SERVICES_JSON_LOOSE_TYPE); + + $ret = array('tags' => array(), 'user_level' => $session->user_level, 'can_add' => $session->get_permissions('tag_create')); + $q = $db->sql_query('SELECT t.tag_id, t.tag_name, pg.pg_target IS NULL AS used_in_acl, t.user FROM '.table_prefix.'tags AS t + LEFT JOIN '.table_prefix.'page_groups AS pg + ON ( ( pg.pg_type = ' . PAGE_GRP_TAGGED . ' AND pg.pg_target=t.tag_name ) OR ( pg.pg_type IS NULL AND pg.pg_target IS NULL ) ) + WHERE t.page_id=\'' . $db->escape($paths->cpage['urlname_nons']) . '\' AND t.namespace=\'' . $db->escape($paths->namespace) . '\';'); + if ( !$q ) + $db->_die(); + + while ( $row = $db->fetchrow() ) + { + $can_del = ( + ( $session->get_permissions('tag_delete_own') && $row['user'] == $session->user_id && $session->user_logged_in ) || // User created the tag and can remove own tags + ( $session->get_permissions('tag_delete_other') && $row['used_in_acl'] != 1 ) || // User can remove tags and the tag isn't used in an ACL (page group) + ( $row['used_in_acl'] == 1 && $session->get_permissions('tag_delete_own') && $session->get_permissions('tag_delete_other') && ( $session->get_permissions('edit_acl') || $session->user_level >= USER_LEVEL_ADMIN ) ) + ); + $ret['tags'][] = array( + 'id' => $row['tag_id'], + 'name' => $row['tag_name'], + 'can_del' => $can_del + ); + } + + echo $json->encode($ret); + + break; default: die('Hacking attempt'); break; diff -r 1f85c1c609fd -r 608dee512bf0 includes/clientside/static/acl.js --- a/includes/clientside/static/acl.js Wed Jul 25 18:09:21 2007 -0400 +++ b/includes/clientside/static/acl.js Sat Jul 28 18:08:58 2007 -0400 @@ -148,19 +148,19 @@ scopeRadioPage.value = 'page'; scopeRadioPage.checked = 'checked'; scopeRadioPage.className = '1048576'; - scopeRadioPage.onclick = function() { var id = 'enACL_pgsel_' + this.className; document.getElementById(id).style.display = 'none'; }; + if ( groups.page_groups.length > 0 ) scopeRadioPage.onclick = function() { var id = 'enACL_pgsel_' + this.className; document.getElementById(id).style.display = 'none'; }; scopeRadioGlobal = document.createElement('input'); scopeRadioGlobal.type = 'radio'; scopeRadioGlobal.name = 'scope'; scopeRadioGlobal.value = 'global'; scopeRadioGlobal.className = '1048576'; - scopeRadioGlobal.onclick = function() { var id = 'enACL_pgsel_' + this.className; document.getElementById(id).style.display = 'none'; }; + if ( groups.page_groups.length > 0 ) scopeRadioGlobal.onclick = function() { var id = 'enACL_pgsel_' + this.className; document.getElementById(id).style.display = 'none'; }; scopeRadioGroup = document.createElement('input'); scopeRadioGroup.type = 'radio'; scopeRadioGroup.name = 'scope'; scopeRadioGroup.value = 'group'; scopeRadioGroup.className = '1048576'; - scopeRadioGroup.onclick = function() { var id = 'enACL_pgsel_' + this.className; document.getElementById(id).style.display = 'block'; }; + if ( groups.page_groups.length > 0 ) scopeRadioGroup.onclick = function() { var id = 'enACL_pgsel_' + this.className; document.getElementById(id).style.display = 'block'; }; lblPage = document.createElement('label'); lblPage.style.display = 'block'; lblPage.appendChild(scopeRadioPage); @@ -229,7 +229,10 @@ { container.appendChild(scopedesc); container.appendChild(scopediv1); - container.appendChild(scopediv2); + if ( groups.page_groups.length > 0 ) + { + container.appendChild(scopediv2); + } container.appendChild(scopediv3); } diff -r 1f85c1c609fd -r 608dee512bf0 includes/clientside/static/ajax.js --- a/includes/clientside/static/ajax.js Wed Jul 25 18:09:21 2007 -0400 +++ b/includes/clientside/static/ajax.js Sat Jul 28 18:08:58 2007 -0400 @@ -83,7 +83,7 @@
\ Edit summary:
\
\ - save changes | preview changes | revert changes | discard changes | formatting help\ + save changes | preview changes | revert changes | discard changes\
\ '+editNotice+'\ '; @@ -760,22 +760,6 @@ ); } -function ajaxWikiEditHelp() -{ - // IE <6 pseudo-compatibility - if ( KILL_SWITCH ) - return true; - jws.openWin('root3', 640, 480); - setAjaxLoading(); - ajaxGet(stdAjaxPrefix+'&_mode=wikihelp', function() { - if(ajax.readyState==4) - { - unsetAjaxLoading(); - document.getElementById('cn3').innerHTML = ajax.responseText; - } - }); -} - function ajaxStartLogin() { // IE <6 pseudo-compatibility @@ -867,3 +851,98 @@ }); } +var catHTMLBuf = false; + +function ajaxCatToTag() +{ + if ( KILL_SWITCH ) + return false; + setAjaxLoading(); + ajaxGet(stdAjaxPrefix + '&_mode=get_tags', function() + { + if ( ajax.readyState == 4 ) + { + unsetAjaxLoading(); + var resptext = String(ajax.responseText + ' '); + resptext = resptext.substr(0, resptext.length-1); + if ( resptext.substr(0, 1) != '{' ) + { + alert('Invalid JSON response from server:\n' + resptext); + return false; + } + var json = parseJSON(resptext); + var catbox = document.getElementById('mdgCatBox'); + if ( !catbox ) + return false; + var linkbox = catbox.parentNode.firstChild.firstChild.nextSibling; + linkbox.firstChild.nodeValue = 'show page categorization'; + linkbox.onclick = function() { ajaxTagToCat(); return false; }; + catHTMLBuf = catbox.innerHTML; + catbox.innerHTML = ''; + catbox.appendChild(document.createTextNode('Page tags: ')); + if ( json.tags.length < 1 ) + { + catbox.appendChild(document.createTextNode('No tags on this page')); + } + for ( var i = 0; i < json.tags.length; i++ ) + { + catbox.appendChild(document.createTextNode(json.tags[i].name)); + if ( json.tags[i].can_del ) + { + catbox.appendChild(document.createTextNode(' ')); + var a = document.createElement('a'); + a.appendChild(document.createTextNode('[X]')); + a.href = '#'; + a.onclick = function() { return false; } + catbox.appendChild(a); + } + if ( ( i + 1 ) < json.tags.length ) + catbox.appendChild(document.createTextNode(', ')); + } + if ( json.can_add ) + { + catbox.appendChild(document.createTextNode(' ')); + var addlink = document.createElement('a'); + addlink.href = '#'; + addlink.onclick = function() { try { ajaxAddTagStage1(); } catch(e) { }; return false; }; + addlink.appendChild(document.createTextNode('(add a tag)')); + catbox.appendChild(addlink); + } + } + }); +} + +function ajaxAddTagStage1() +{ + var catbox = document.getElementById('mdgCatBox'); + var adddiv = document.createElement('div'); + var text = document.createElement('input'); + var addlink = document.createElement('a'); + addlink.href = '#'; + addlink.onclick = function() { return false; }; + addlink.appendChild(document.createTextNode('+ Add')); + text.type = 'text'; + text.size = '15'; + + adddiv.style.margin = '5px 0 0 0'; + adddiv.appendChild(document.createTextNode('Add a tag: ')); + adddiv.appendChild(text); + adddiv.appendChild(document.createTextNode(' ')); + adddiv.appendChild(addlink); + catbox.appendChild(adddiv); +} + +function ajaxTagToCat() +{ + if ( !catHTMLBuf ) + return false; + var catbox = document.getElementById('mdgCatBox'); + if ( !catbox ) + return false; + var linkbox = catbox.parentNode.firstChild.firstChild.nextSibling; + linkbox.firstChild.nodeValue = 'show page tags'; + linkbox.onclick = function() { ajaxCatToTag(); return false; }; + catbox.innerHTML = catHTMLBuf; + catHTMLBuf = false; +} + diff -r 1f85c1c609fd -r 608dee512bf0 includes/clientside/static/windows.js --- a/includes/clientside/static/windows.js Wed Jul 25 18:09:21 2007 -0400 +++ b/includes/clientside/static/windows.js Sat Jul 28 18:08:58 2007 -0400 @@ -215,7 +215,7 @@ myHeight = document.body.clientHeight; } return myHeight; -} +} function getWidth() { var myWidth = 0; diff -r 1f85c1c609fd -r 608dee512bf0 includes/common.php --- a/includes/common.php Wed Jul 25 18:09:21 2007 -0400 +++ b/includes/common.php Sat Jul 28 18:08:58 2007 -0400 @@ -170,7 +170,9 @@ table_prefix.'groups', table_prefix.'group_members', table_prefix.'acl', - table_prefix.'search_cache' + table_prefix.'search_cache', + table_prefix.'page_groups', + table_prefix.'page_group_members' ); dc_here('common: initializing base classes'); diff -r 1f85c1c609fd -r 608dee512bf0 includes/functions.php --- a/includes/functions.php Wed Jul 25 18:09:21 2007 -0400 +++ b/includes/functions.php Sat Jul 28 18:08:58 2007 -0400 @@ -40,17 +40,17 @@ function setConfig($n, $v) { - + global $enano_config, $db; $enano_config[$n] = $v; $v = $db->escape($v); - + $e = $db->sql_query('DELETE FROM '.table_prefix.'config WHERE config_name=\''.$n.'\';'); if ( !$e ) { $db->_die('Error during generic setConfig() call row deletion.'); } - + $e = $db->sql_query('INSERT INTO '.table_prefix.'config(config_name, config_value) VALUES(\''.$n.'\', \''.$v.'\')'); if ( !$e ) { @@ -82,17 +82,17 @@ $sep = '&'; } if ( isset($_GET['style'] ) ) { - $flags .= $sep . 'style='.$session->style; + $flags .= $sep . 'style='.$session->style; $sep = '&'; } - + $url = $session->append_sid(contentPath.$t.$flags); if($query) { $sep = strstr($url, '?') ? '&' : '?'; $url = $url . $sep . $query; } - + return ($escape) ? htmlspecialchars($url) : $url; } @@ -109,7 +109,7 @@ { global $db, $session, $paths, $template, $plugins; // Common objects $flags = ''; - + if(defined('ENANO_BASE_CLASSES_INITIALIZED')) { $sep = urlSeparator; @@ -122,7 +122,7 @@ $flags .= $sep . 'printable'; $sep = '&'; } - if ( isset( $_GET['theme'] ) ) + if ( isset( $_GET['theme'] ) ) { $flags .= $sep . 'theme='.$session->theme; $sep = '&'; @@ -132,7 +132,7 @@ $flags .= $sep . 'style='.$session->style; $sep = '&'; } - + if(defined('ENANO_BASE_CLASSES_INITIALIZED')) { $url = contentPath . $paths->nslist[$n] . $t . $flags; @@ -142,10 +142,10 @@ // If the path manager hasn't been initted yet, take an educated guess at what the URI should be $url = contentPath . $n . ':' . $t . $flags; } - + if($query) { - if(strstr($url, '?')) + if(strstr($url, '?')) { $sep = '&'; } @@ -155,12 +155,12 @@ } $url = $url . $sep . $query . $flags; } - + if(defined('ENANO_BASE_CLASSES_INITIALIZED')) { $url = $session->append_sid($url); } - + return ($escape) ? htmlspecialchars($url) : $url; } @@ -177,7 +177,7 @@ { global $db, $session, $paths, $template, $plugins; // Common objects $flags = ''; - + if(defined('ENANO_BASE_CLASSES_INITIALIZED')) { $sep = urlSeparator; @@ -190,7 +190,7 @@ $flags .= $sep . 'printable'; $sep = '&'; } - if ( isset( $_GET['theme'] ) ) + if ( isset( $_GET['theme'] ) ) { $flags .= $sep . 'theme='.$session->theme; $sep = '&'; @@ -200,7 +200,7 @@ $flags .= $sep . 'style='.$session->style; $sep = '&'; } - + if(defined('ENANO_BASE_CLASSES_INITIALIZED')) { $url = $session->append_sid(contentPath . $paths->nslist[$n] . $t . $flags); @@ -216,10 +216,10 @@ else $sep = '?'; $url = $url . $sep . $query . $flags; } - + $baseprot = 'http' . ( isset($_SERVER['HTTPS']) ? 's' : '' ) . '://' . $_SERVER['HTTP_HOST']; $url = $baseprot . $url; - + return ($escape) ? htmlspecialchars($url) : $url; } @@ -232,11 +232,11 @@ function get_page_title($page_id) { global $db, $session, $paths, $template, $plugins; // Common objects - + $idata = RenderMan::strToPageID($page_id); $page_id_key = $paths->nslist[ $idata[1] ] . $idata[0]; $page_data = $paths->pages[$page_id_key]; - $title = ( isset($page_data['name']) ) ? $page_data['name'] : $paths->nslist[$idata[1]] . str_replace('_', ' ', dirtify_page_id( $idata[0] ) ); + $title = ( isset($page_data['name']) ) ? ( $page_data['namespace'] == 'Article' ? '' : $paths->nslist[ $idata[1] ] ) . $page_data['name'] : $paths->nslist[$idata[1]] . str_replace('_', ' ', dirtify_page_id( $idata[0] ) ); return $title; } @@ -250,7 +250,7 @@ function get_page_title_ns($page_id, $namespace) { global $db, $session, $paths, $template, $plugins; // Common objects - + $page_id_key = $paths->nslist[ $namespace ] . $page_id; $page_data = $paths->pages[$page_id_key]; $title = ( isset($page_data['name']) ) ? $page_data['name'] : $paths->nslist[$namespace] . str_replace('_', ' ', dirtify_page_id( $page_id ) ); @@ -264,17 +264,17 @@ * @param string $message A short message to show to the user * @param string $timeout Timeout, in seconds, to delay the redirect. Defaults to 3. */ - + function redirect($url, $title = 'Redirecting...', $message = 'Please wait while you are redirected.', $timeout = 3) { global $db, $session, $paths, $template, $plugins; // Common objects - + if ( $timeout == 0 ) { header('Location: ' . $url); header('HTTP/1.1 307 Temporary Redirect'); } - + $template->add_header(''); $template->add_header(' '); - + $template->tpl_strings['PAGE_NAME'] = $title; $template->header(true); echo '

' . $message . '

If you are not redirected within ' . ( $timeout + 1 ) . ' seconds, please click here.

'; $template->footer(true); - + $db->close(); exit(0); - + } // Removed wikiFormat() from here, replaced with RenderMan::render @@ -305,21 +305,21 @@ function isPage($p) { global $db, $session, $paths, $template, $plugins; // Common objects - + // Try the easy way first ;-) if ( isset( $paths->pages[ $p ] ) ) { return true; } - + // Special case for Special, Template, and Admin pages that can't have slashes in their URIs $ns_test = RenderMan::strToPageID( $p ); - + if($ns_test[1] != 'Special' && $ns_test[1] != 'Template' && $ns_test[1] != 'Admin') { return false; } - + $particles = explode('/', $p); if ( isset ( $paths->pages[ $particles[ 0 ] ] ) ) { @@ -331,6 +331,10 @@ } } +/** + * These are some old functions that were used with the Midget codebase. They are deprecated and should not be used any more. + */ + function arrayItemUp($arr, $keyname) { $keylist = array_keys($arr); $keyflop = array_flip($keylist); @@ -436,16 +440,24 @@ // Function strip_php moved to RenderMan class +/** + * Immediately brings the site to a halt with an error message. Unlike grinding_halt() this can only be called after the config has been + * fetched (plugin developers don't even need to worry since plugins are always loaded after the config) and shows the site name and + * description. + * @param string The title of the error message + * @param string The body of the message, this can be HTML, and should be separated into paragraphs using the

tag + */ + function die_semicritical($t, $p) { global $db, $session, $paths, $template, $plugins; // Common objects $db->close(); - + if ( ob_get_status() ) ob_end_clean(); - + dc_here('functions: calling die_semicritical'); - + $tpl = new template_nodb(); $tpl->load_theme('oxygen', 'bleu'); $tpl->tpl_strings['SITE_NAME'] = getConfig('site_name'); @@ -455,17 +467,23 @@ $tpl->header(); echo $p; $tpl->footer(); - + exit; } +/** + * Halts Enano execution with a message. This doesn't have to be an error message, it's sometimes used to indicate success at an operation. + * @param string The title of the message + * @param string The body of the message, this can be HTML, and should be separated into paragraphs using the

tag + */ + function die_friendly($t, $p) { global $db, $session, $paths, $template, $plugins; // Common objects - + if ( ob_get_status() ) ob_end_clean(); - + dc_here('functions: calling die_friendly'); $paths->cpage['name'] = $t; $template->tpl_strings['PAGE_NAME'] = $t; @@ -473,19 +491,25 @@ echo $p; $template->footer(); $db->close(); - + exit; } +/** + * Immediately brings the site to a halt with an error message, and focuses on immediately closing the database connection and shutting down Enano in the event that an attack may happen. This should only be used very early on to indicate very severe errors, or if the site may be under attack (like if the DBAL detects a malicious query). In the vast majority of cases, die_semicritical() is more appropriate. + * @param string The title of the error message + * @param string The body of the message, this can be HTML, and should be separated into paragraphs using the

tag + */ + function grinding_halt($t, $p) { global $db, $session, $paths, $template, $plugins; // Common objects - + $db->close(); - + if ( ob_get_status() ) ob_end_clean(); - + dc_here('functions: calling grinding_halt'); $tpl = new template_nodb(); $tpl->load_theme('oxygen', 'bleu'); @@ -499,11 +523,17 @@ exit; } -function show_category_info() { +/** + * Prints out the categorization box found on most regular pages. Doesn't take or return anything, but assumes that the page information is already set in $paths. + */ + + /* +function show_category_info() +{ global $db, $session, $paths, $template, $plugins; // Common objects dc_here('functions: showing category info'); - if($template->no_headers && !strpos($_SERVER['REQUEST_URI'], 'ajax.php')) return ''; - if($paths->namespace=='Category') + // if($template->no_headers && !strpos($_SERVER['REQUEST_URI'], 'ajax.php')) return ''; + if ( $paths->namespace == 'Category' ) { $q = $db->sql_query('SELECT page_id,namespace FROM '.table_prefix.'categories WHERE category_id=\''.$paths->cpage['urlname_nons'].'\' AND namespace=\'Category\' ORDER BY page_id;'); if(!$q) $db->_die('The category information could not be selected.'); @@ -513,27 +543,49 @@ echo ''; while($row = $db->fetchrow()) { - $ticker++;if($ticker==3) $ticker=0; - if($ticker==0) echo ''; - echo ''; - if($ticker==2) echo ''; + $ticker++; + if ( $ticker == 3 ) + { + $ticker = 0; + } + if ( $ticker == 0 ) + { + echo ''; + } + echo ''; + if ( $ticker == 2 ) + { + echo ''; + } } $db->free_result(); if($ticker) echo ''; echo '
'.$paths->pages[$paths->nslist[$row['namespace']].$row['page_id']]['name'].'
' . htmlspecialchars($paths->pages[$paths->nslist[$row['namespace']].$row['page_id']]['name']) . '
'; - + $q = $db->sql_query('SELECT page_id,namespace FROM '.table_prefix.'categories WHERE category_id=\''.$paths->cpage['urlname_nons'].'\' AND namespace!=\'Category\' ORDER BY page_id;'); - if(!$q) $db->_die('The category information could not be selected.'); + if ( !$q ) + { + $db->_die('The category information could not be selected.'); + } $ticker = -1; echo '

Pages

'; - if($db->numrows() < 1) echo '

There are no pages in this category.

'; + if ( $db->numrows() < 1 ) + { + echo '

There are no pages in this category.

'; + } echo ''; while($row = $db->fetchrow()) { - $ticker++;if($ticker==3) $ticker=0; - if($ticker==0) echo ''; - echo ''; - if($ticker==2) echo ''; + $ticker += ( $ticker == 3 ) ? -3 : 1; + if ( $ticker == 0 ) + { + echo ''; + } + echo ''; + if ( $ticker == 2 ) + { + echo ''; + } } $db->free_result(); if($ticker) echo ''; @@ -551,7 +603,9 @@ echo ''.$paths->pages[$paths->nslist['Category'].$r['category_id']]['name'].''; } if( ( $paths->wiki_mode && !$paths->page_protected ) || ( $session->get_permissions('edit_cat') && $session->get_permissions('even_when_protected') ) ) echo ' [ edit categorization ]'; - } else { + } + else + { echo '
Categories: '; echo '(Uncategorized)'; if( ( $paths->wiki_mode && !$paths->page_protected ) || ( $session->get_permissions('edit_cat') && $session->get_permissions('even_when_protected') ) ) echo ' [ edit categorization ]
'; @@ -559,6 +613,180 @@ } $db->free_result(); } +*/ + +function show_category_info() +{ + global $db, $session, $paths, $template, $plugins; // Common objects + + if ( $paths->namespace == 'Category' ) + { + // Show member pages and subcategories + $q = $db->sql_query('SELECT p.urlname, p.namespace, p.name, p.namespace=\'Category\' AS is_category FROM '.table_prefix.'categories AS c + LEFT JOIN '.table_prefix.'pages AS p + ON ( p.urlname = c.page_id AND p.namespace = c.namespace ) + WHERE c.category_id=\'' . $db->escape($paths->cpage['urlname_nons']) . '\' + ORDER BY is_category DESC, p.name ASC;'); + if ( !$q ) + { + $db->_die(); + } + echo '

Subcategories

'; + echo '
'; + echo '
'.$paths->pages[$paths->nslist[$row['namespace']].$row['page_id']]['name'].'
'.htmlspecialchars($paths->pages[$paths->nslist[$row['namespace']].$row['page_id']]['name']).'
'; + echo ''; + $ticker = 0; + $counter = 0; + $switched = false; + $class = 'row1'; + while ( $row = $db->fetchrow() ) + { + if ( $row['is_category'] == 0 && !$switched ) + { + if ( $counter > 0 ) + { + // Fill-in + while ( $ticker < 3 ) + { + $ticker++; + echo ''; + } + } + else + { + echo ''; + } + echo '
No subcategories.
' . "\n\n"; + echo '

Pages

'; + echo '
'; + echo ''; + echo ''; + $counter = 0; + $ticker = 0; + $switched = true; + } + $counter++; + $ticker++; + if ( $ticker == 3 ) + { + echo ''; + $ticker = 0; + $class = ( $class == 'row3' ) ? 'row1' : 'row3'; + } + echo ""; + } + if ( !$switched ) + { + if ( $counter > 0 ) + { + // Fill-in + while ( $ticker < 3 ) + { + $ticker++; + echo ''; + } + } + else + { + echo ''; + } + echo '
"; // " to workaround stupid jEdit bug + + $link = makeUrlNS($row['namespace'], sanitize_page_id($row['urlname'])); + echo 'nslist[$row['namespace']] . sanitize_page_id($row['urlname']); + if ( !isPage( $key ) ) + { + echo ' class="wikilink-nonexistent"'; + } + echo '>'; + $title = get_page_title_ns($row['urlname'], $row['namespace']); + echo htmlspecialchars($title); + echo ''; + + echo "No subcategories.
' . "\n\n"; + echo '

Pages

'; + echo '
'; + echo ''; + echo ''; + $counter = 0; + $ticker = 0; + $switched = true; + } + if ( $counter > 0 ) + { + // Fill-in + while ( $ticker < 3 ) + { + $ticker++; + echo ''; + } + } + else + { + echo ''; + } + echo '
No pages in this category.
' . "\n\n"; + } + + if ( $paths->namespace != 'Special' && $paths->namespace != 'Admin' ) + { + echo '
'; + if ( $session->user_level >= USER_LEVEL_ADMIN ) + { + echo '
'; + echo '(show page tags)'; + echo '
'; + } + echo '
Categories: '; + + $where = '( c.page_id=\'' . $db->escape($paths->cpage['urlname_nons']) . '\' AND c.namespace=\'' . $db->escape($paths->namespace) . '\' )'; + $prefix = table_prefix; + $sql = <<sql_query($sql); + if ( !$q ) + $db->_die(); + + if ( $row = $db->fetchrow() ) + { + $list = array(); + do + { + $cid = sanitize_page_id($row['category_id']); + $title = get_page_title_ns($cid, 'Category'); + $link = makeUrlNS('Category', $cid); + $list[] = '' . htmlspecialchars($title) . ''; + } + while ( $row = $db->fetchrow() ); + echo implode(', ', $list); + } + else + { + echo '(Uncategorized)'; + } + + $can_edit = ( $session->get_permissions('edit_cat') && ( !$paths->page_protected || $session->get_permissions('even_when_protected') ) ); + if ( $can_edit ) + { + $edit_link = 'edit categorization'; + echo ' [ ' . $edit_link . ' ]'; + } + + echo '
'; + + } + +} + +/** + * Prints out the file information box seen on File: pages. Doesn't take or return anything, but assumes that the page information is already set in $paths, and expects $paths->namespace to be File. + */ function show_file_info() { @@ -630,6 +858,10 @@ echo '
'; } +/** + * Shows header information on the current page. Currently this is only the delete-vote feature. Doesn't take or return anything, but assumes that the page information is already set in $paths. + */ + function display_page_headers() { global $db, $session, $paths, $template, $plugins; // Common objects @@ -653,6 +885,10 @@ } } +/** + * Displays page footer information including file and category info. This also has the send_page_footers hook. Doesn't take or return anything, but assumes that the page information is already set in $paths. + */ + function display_page_footers() { global $db, $session, $paths, $template, $plugins; // Common objects @@ -666,6 +902,10 @@ show_category_info(); } +/** + * Deprecated, do not use. + */ + function password_prompt($id = false) { global $db, $session, $paths, $template, $plugins; // Common objects @@ -679,6 +919,12 @@ } } +/** + * Some sort of primitive hex converter from back in the day. Deprecated, do not use. + * @param string Text to encode + * @return string + */ + function str_hex($string){ $hex=''; for ($i=0; $i < strlen($string); $i++){ @@ -687,92 +933,111 @@ return substr($hex, 1, strlen($hex)); } -// Function pulled from phpBB's smtp.php -function smtp_get_response($socket, $response, $line = __LINE__) +/** + * Essentially an return code reader for a socket. Don't use this unless you're writing mail code and smtp_send_email doesn't cut it. Ported from phpBB's smtp.php. + * @param socket A socket resource + * @param string The expected response from the server, this needs to be exactly three characters. + */ + +function smtp_get_response($socket, $response, $line = __LINE__) { - $server_response = ''; - while (substr($server_response, 3, 1) != ' ') - { - if (!($server_response = fgets($socket, 256))) - { + $server_response = ''; + while (substr($server_response, 3, 1) != ' ') + { + if (!($server_response = fgets($socket, 256))) + { die_friendly('SMTP Error', "

Couldn't get mail server response codes

"); - } - } + } + } - if (!(substr($server_response, 0, 3) == $response)) - { + if (!(substr($server_response, 0, 3) == $response)) + { die_friendly('SMTP Error', "

Ran into problems sending mail. Response: $server_response

"); - } + } } +/** + * Wrapper for smtp_send_email_core that takes the sender as the fourth parameter instead of additional headers. + * @param string E-mail address to send to + * @param string Subject line + * @param string The body of the message + * @param string Address of the sender + */ + function smtp_send_email($to, $subject, $message, $from) { return smtp_send_email_core($to, $subject, $message, "From: <$from>\n"); } -// Replacement or substitute for PHP's mail command -// Ported from phpBB - copyright (C) phpBB group, GPL. +/** + * Replacement or substitute for PHP's mail() builtin function. + * @param string E-mail address to send to + * @param string Subject line + * @param string The body of the message + * @param string Message headers, separated by a single newline ("\n") + * @copyright (C) phpBB Group + * @license GPL + */ + function smtp_send_email_core($mail_to, $subject, $message, $headers = '') { - global $board_config; - - // Fix any bare linefeeds in the message to make it RFC821 Compliant. - $message = preg_replace("#(? 1) - { - $headers = join("\n", $headers); - } - else - { - $headers = $headers[0]; - } - } - $headers = chop($headers); + if ($headers != '') + { + if (is_array($headers)) + { + if (sizeof($headers) > 1) + { + $headers = join("\n", $headers); + } + else + { + $headers = $headers[0]; + } + } + $headers = chop($headers); - // Make sure there are no bare linefeeds in the headers - $headers = preg_replace('#(?\r\n"); - smtp_get_response($socket, "250", __LINE__); + // From this point onward most server response codes should be 250 + // Specify who the mail is from.... + enano_fputs($socket, "MAIL FROM: <" . getConfig('contact_email') . ">\r\n"); + smtp_get_response($socket, "250", __LINE__); - // Specify each user to send to and build to header. - $to_header = ''; + // Specify each user to send to and build to header. + $to_header = ''; - // Add an additional bit of error checking to the To field. - $mail_to = (trim($mail_to) == '') ? 'Undisclosed-recipients:;' : trim($mail_to); - if (preg_match('#[^ ]+\@[^ ]+#', $mail_to)) - { - enano_fputs($socket, "RCPT TO: <$mail_to>\r\n"); - smtp_get_response($socket, "250", __LINE__); - } + // Add an additional bit of error checking to the To field. + $mail_to = (trim($mail_to) == '') ? 'Undisclosed-recipients:;' : trim($mail_to); + if (preg_match('#[^ ]+\@[^ ]+#', $mail_to)) + { + enano_fputs($socket, "RCPT TO: <$mail_to>\r\n"); + smtp_get_response($socket, "250", __LINE__); + } - // Ok now do the CC and BCC fields... - @reset($bcc); - while(list(, $bcc_address) = each($bcc)) - { - // Add an additional bit of error checking to bcc header... - $bcc_address = trim($bcc_address); - if (preg_match('#[^ ]+\@[^ ]+#', $bcc_address)) - { - enano_fputs($socket, "RCPT TO: <$bcc_address>\r\n"); - smtp_get_response($socket, "250", __LINE__); - } - } + // Ok now do the CC and BCC fields... + @reset($bcc); + while(list(, $bcc_address) = each($bcc)) + { + // Add an additional bit of error checking to bcc header... + $bcc_address = trim($bcc_address); + if (preg_match('#[^ ]+\@[^ ]+#', $bcc_address)) + { + enano_fputs($socket, "RCPT TO: <$bcc_address>\r\n"); + smtp_get_response($socket, "250", __LINE__); + } + } - @reset($cc); - while(list(, $cc_address) = each($cc)) - { - // Add an additional bit of error checking to cc header - $cc_address = trim($cc_address); - if (preg_match('#[^ ]+\@[^ ]+#', $cc_address)) - { - enano_fputs($socket, "RCPT TO: <$cc_address>\r\n"); - smtp_get_response($socket, "250", __LINE__); - } - } + @reset($cc); + while(list(, $cc_address) = each($cc)) + { + // Add an additional bit of error checking to cc header + $cc_address = trim($cc_address); + if (preg_match('#[^ ]+\@[^ ]+#', $cc_address)) + { + enano_fputs($socket, "RCPT TO: <$cc_address>\r\n"); + smtp_get_response($socket, "250", __LINE__); + } + } - // Ok now we tell the server we are ready to start sending data - enano_fputs($socket, "DATA\r\n"); + // Ok now we tell the server we are ready to start sending data + enano_fputs($socket, "DATA\r\n"); - // This is the last response code we look for until the end of the message. - smtp_get_response($socket, "354", __LINE__); + // This is the last response code we look for until the end of the message. + smtp_get_response($socket, "354", __LINE__); - // Send the Subject Line... - enano_fputs($socket, "Subject: $subject\r\n"); + // Send the Subject Line... + enano_fputs($socket, "Subject: $subject\r\n"); - // Now the To Header. - enano_fputs($socket, "To: $mail_to\r\n"); + // Now the To Header. + enano_fputs($socket, "To: $mail_to\r\n"); - // Now any custom headers.... - enano_fputs($socket, "$headers\r\n\r\n"); + // Now any custom headers.... + enano_fputs($socket, "$headers\r\n\r\n"); - // Ok now we are ready for the message... - enano_fputs($socket, "$message\r\n"); + // Ok now we are ready for the message... + enano_fputs($socket, "$message\r\n"); - // Ok the all the ingredients are mixed in let's cook this puppy... - enano_fputs($socket, ".\r\n"); - smtp_get_response($socket, "250", __LINE__); + // Ok the all the ingredients are mixed in let's cook this puppy... + enano_fputs($socket, ".\r\n"); + smtp_get_response($socket, "250", __LINE__); - // Now tell the server we are done and close the socket... - enano_fputs($socket, "QUIT\r\n"); - fclose($socket); + // Now tell the server we are done and close the socket... + enano_fputs($socket, "QUIT\r\n"); + fclose($socket); - return TRUE; + return TRUE; } /** @@ -918,85 +1183,41 @@ return $r; } +/** + * What kinda sh** was I thinking when I wrote this. Deprecated. + */ + function _dualurlenc($t) { return rawurlencode(rawurlencode($t)); } - + +/** + * Badly named function to send back eval'able Javascript code with an error message. Deprecated, use JSON instead. + * @param string Message to send + */ + function _die($t) { $_ob = 'document.getElementById("ajaxEditContainer").innerHTML = unescape(\'' . rawurlencode('' . $t . '') . '\')'; die($_ob); } +/** + * Same as _die(), but sends an SQL backtrace with the error message, and doesn't halt execution. + * @param string Message to send + */ + function jsdie($text) { global $db, $session, $paths, $template, $plugins; // Common objects $text = rawurlencode($text . "\n\nSQL Backtrace:\n" . $db->sql_backtrace()); echo 'document.getElementById("ajaxEditContainer").innerHTML = unescape(\''.$text.'\');'; } -// HTML sanitizing function - written by Kallahar -// Original function at: http://quickwired.com/kallahar/smallprojects/php_xss_filter_function.php - -// UNUSED - todo: remove this in gold or put it to use - -function RemoveXSS($val) { - // remove all non-printable characters. CR(0a) and LF(0b) and TAB(9) are allowed - // this prevents some character re-spacing such as - // note that you have to handle splits with \n, \r, and \t later since they *are* allowed in some inputs - $val = preg_replace('/([\x00-\x08][\x0b-\x0c][\x0e-\x20])/', '', $val); - - // straight replacements, the user should never need these since they're normal characters - // this prevents like - $search = 'abcdefghijklmnopqrstuvwxyz'; - $search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; - $search .= '1234567890!@#$%^&*()'; - $search .= '~`";:?+/={}[]-_|\'\\'; - for ($i = 0; $i < strlen($search); $i++) { - // ;? matches the ;, which is optional - // 0{0,7} matches any padded zeros, which are optional and go up to 8 chars - - // @ @ search for the hex values - $val = preg_replace('/(&#[x|X]0{0,8}'.dechex(ord($search[$i])).';?)/i', $search[$i], $val); // with a ; - // @ @ 0{0,7} matches '0' zero to seven times - $val = preg_replace('/(�{0,8}'.ord($search[$i]).';?)/', $search[$i], $val); // with a ; - } - - // now the only remaining whitespace attacks are \t, \n, and \r - $ra1 = Array('javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base'); - $ra2 = Array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload'); - $ra = array_merge($ra1, $ra2); - - $found = true; // keep replacing as long as the previous round replaced something - while ($found == true) { - $val_before = $val; - for ($i = 0; $i < sizeof($ra); $i++) { - $pattern = '/'; - for ($j = 0; $j < strlen($ra[$i]); $j++) { - if ($j > 0) { - $pattern .= '('; - $pattern .= '(&#[x|X]0{0,8}([9][a][b]);?)?'; - $pattern .= '|(�{0,8}([9][10][13]);?)?'; - $pattern .= ')?'; - } - $pattern .= $ra[$i][$j]; - } - $pattern .= '/i'; - $replacement = substr($ra[$i], 0, 2).''.substr($ra[$i], 2); // add in <> to nerf the tag - $val = preg_replace($pattern, $replacement, $val); // filter out the hex tags - if ($val_before == $val) { - // no replacements were made, so exit the loop - $found = false; - } - } - } - return $val; -} - /** * Capitalizes the first letter of a string * @param $text string the text to be transformed * @return string */ - + function capitalize_first_letter($text) { return strtoupper(substr($text, 0, 1)) . substr($text, 1); @@ -1008,7 +1229,7 @@ * @param $value int the value to switch off * @return bool */ - + function is_bit($bitfield, $value) { return ( $bitfield & $value ) ? true : false; @@ -1019,7 +1240,7 @@ * @param $text the text to process * @return string */ - + function trim_spaces($text) { $d = true; @@ -1040,10 +1261,10 @@ * @param $inc int size of each block * @return array */ - + function enano_str_split($text, $inc = 1) { - if($inc < 1) + if($inc < 1) { return false; } @@ -1078,10 +1299,10 @@ /** * Generates and/or prints a human-readable backtrace - * @param bool $return - if true, this function returns a string, otherwise returns null + * @param bool $return - if true, this function returns a string, otherwise returns null and prints the backtrace * @return mixed */ - + function enano_debug_print_backtrace($return = false) { ob_start(); @@ -1109,7 +1330,7 @@ * @param optional string $suffix text after each hex character * @return string */ - + function hexencode($text, $prefix = '%', $suffix = '') { $arr = enano_str_split($text); @@ -1127,7 +1348,7 @@ * Enano-ese equivalent of get_magic_quotes_gpc() * @return bool */ - + function enano_get_magic_quotes_gpc() { if(function_exists('get_magic_quotes_gpc')) @@ -1145,7 +1366,7 @@ * @param array * @return array */ - + function stripslashes_recurse($arr) { foreach($arr as $k => $xxxx) @@ -1164,7 +1385,7 @@ * @param array * @return array */ - + function strip_nul_chars($arr) { foreach($arr as $k => $xxxx_unused) @@ -1179,7 +1400,7 @@ } /** - * If magic_quotes_gpc is on, calls stripslashes() on everything in $_GET/$_POST/$_COOKIE + * If magic_quotes_gpc is on, calls stripslashes() on everything in $_GET/$_POST/$_COOKIE. Also strips any NUL characters from incoming requests, as these are typically malicious. * @ignore - this doesn't work too well in my tests * @todo port version from the PHP manual * @return void @@ -1201,10 +1422,10 @@ /** * A very basic single-character compression algorithm for binary strings/bitfields - * @param string $bits the text to compress + * @param string $bits the text to compress, should be only 1s and 0s * @return string */ - + function compress_bitfield($bits) { $crc32 = crc32($bits); @@ -1272,7 +1493,7 @@ * @param string $bits the compressed bitfield * @return string the uncompressed, original (we hope) bitfield OR bool false on error */ - + function uncompress_bitfield($bits) { if(substr($bits, 0, 4) != 'cbf:') @@ -1352,7 +1573,7 @@ ); $collist[] = $field; } - + if ( $structure ) { $db->sql_query('SET SQL_QUOTE_SHOW_CREATE = 0;'); @@ -1382,7 +1603,7 @@ $struct = implode("", $struct_arr); } } - + // Structuring complete if($data) { @@ -1441,7 +1662,7 @@ * Encodes a string value for use in an INSERT statement for given column type $type. * @access private */ - + function mysql_encode_column($input, $type) { global $db, $session, $paths, $template, $plugins; // Common objects @@ -1529,16 +1750,16 @@ function sanitize_html($html, $filter_php = true) { - + $html = preg_replace('#<([a-z]+)([\s]+)([^>]+?)'.htmlalternatives('javascript:').'(.+?)>(.*?)#is', '<\\1\\2\\3javascript:\\59>\\60</\\1>', $html); $html = preg_replace('#<([a-z]+)([\s]+)([^>]+?)'.htmlalternatives('javascript:').'(.+?)>#is', '<\\1\\2\\3javascript:\\59>', $html); - + if($filter_php) $html = str_replace( Array('', '%>'), Array('<?php', '<?', '<%', '?>', '%>'), $html); - + $tag_whitelist = array_keys ( setupAttributeWhitelist() ); if ( !$filter_php ) $tag_whitelist[] = '?php'; @@ -1565,7 +1786,7 @@ $quote_char = $chr; } if ( $chr == '<' && !$in_tag && $next != '/' ) - { + { // start of a tag $tag_start = $i; $in_tag = true; @@ -1576,26 +1797,26 @@ $full_tag = substr($html, $tag_start, ( $i - $tag_start ) + 1 ); $l = strlen($tag_name) + 2; $attribs_only = trim( substr($full_tag, $l, ( strlen($full_tag) - $l - 1 ) ) ); - + // Debugging message // echo htmlspecialchars($full_tag) . '
'; - + if ( !in_array($tag_name, $tag_whitelist) ) { // Illegal tag //echo $tag_name . ' '; - + $s = ( empty($attribs_only) ) ? '' : ' '; - + $sanitized = '<' . $tag_name . $s . $attribs_only . '>'; - + $html = substr($html, 0, $tag_start) . $sanitized . substr($html, $i + 1); $html = str_replace('', '</' . $tag_name . '>', $html); $new_i = $tag_start + strlen($sanitized); - + $len = strlen($html); $i = $new_i; - + $in_tag = false; $tag_name = ''; continue; @@ -1606,14 +1827,14 @@ continue; $f = fixTagAttributes( $attribs_only, $tag_name ); $s = ( empty($f) ) ? '' : ' '; - + $sanitized = '<' . $tag_name . $f . '>'; $new_i = $tag_start + strlen($sanitized); - + $html = substr($html, 0, $tag_start) . $sanitized . substr($html, $i + 1); $len = strlen($html); $i = $new_i; - + $in_tag = false; $tag_name = ''; continue; @@ -1629,20 +1850,20 @@ $trk_name = false; } } - + } - + // Vulnerability from ha.ckers.org/xss.html: //