# HG changeset patch # User Dan # Date 1237661734 14400 # Node ID ecc764c10138b2b0b6b24154fba4a6bd331eefb8 # Parent 0c3dd4c166c0e957ad5fa936d3bf1278050be28b# Parent e9ee4e246f966d06fc0326284014c0c9fbed13b7 Merging branches diff -r e9ee4e246f96 -r ecc764c10138 ajax.php --- a/ajax.php Sat Mar 21 14:54:53 2009 -0400 +++ b/ajax.php Sat Mar 21 14:55:34 2009 -0400 @@ -540,30 +540,78 @@ $parms = ( isset($_POST['acl_params']) ) ? rawurldecode($_POST['acl_params']) : false; echo PageUtils::acl_json($parms); break; + case 'theme_list': + header('Content-type: application/json'); + + $q = $db->sql_query('SELECT theme_name, theme_id FROM ' . table_prefix . "themes WHERE enabled = 1 ORDER BY theme_name ASC;"); + if ( !$q ) + $db->die_json(); + + $return = array(); + while ( $row = $db->fetchrow() ) + $return[] = $row; + + foreach ( $return as &$theme ) + { + $theme['have_thumb'] = file_exists(ENANO_ROOT . "/themes/{$theme['theme_id']}/preview.png"); + } + + echo enano_json_encode($return); + + break; + case "get_styles": + if ( !preg_match('/^[a-z0-9_-]+$/', $_GET['theme_id']) ) + die(enano_json_encode(array())); + + $theme_id = $_GET['theme_id']; + $return = array(); + + if ( $dr = @opendir(ENANO_ROOT . "/themes/$theme_id/css/") ) + { + while ( $dh = @readdir($dr) ) + { + if ( preg_match('/\.css$/', $dh) && $dh != '_printable.css' ) + { + $return[] = preg_replace('/\.css$/', '', $dh); + } + } + } + else + { + $return = array( + 'mode' => 'error', + 'error' => 'Could not open directory.' + ); + } + echo enano_json_encode($return); + break; case "change_theme": if ( !isset($_POST['theme_id']) || !isset($_POST['style_id']) ) { - die('Invalid input'); + die(enano_json_encode(array('mode' => 'error', 'error' => 'Invalid parameter'))); } if ( !preg_match('/^([a-z0-9_-]+)$/i', $_POST['theme_id']) || !preg_match('/^([a-z0-9_-]+)$/i', $_POST['style_id']) ) { - die('Invalid input'); + die(enano_json_encode(array('mode' => 'error', 'error' => 'Invalid parameter'))); } if ( !file_exists(ENANO_ROOT . '/themes/' . $_POST['theme_id'] . '/css/' . $_POST['style_id'] . '.css') ) { - die('Can\'t find theme file: ' . ENANO_ROOT . '/themes/' . $_POST['theme_id'] . '/css/' . $_POST['style_id'] . '.css'); + die(enano_json_encode(array('mode' => 'error', 'error' => 'Can\'t find theme file: ' . ENANO_ROOT . '/themes/' . $_POST['theme_id'] . '/css/' . $_POST['style_id'] . '.css')));; } if ( !$session->user_logged_in ) { - die('You must be logged in to change your theme'); + die(enano_json_encode(array('mode' => 'error', 'error' => 'You must be logged in to change your theme'))); } // Just in case something slipped through... $theme_id = $db->escape($_POST['theme_id']); $style_id = $db->escape($_POST['style_id']); - $e = $db->sql_query('UPDATE ' . table_prefix . "users SET theme='$theme_id', style='$style_id' WHERE user_id=$session->user_id;"); + $e = $db->sql_query('UPDATE ' . table_prefix . "users SET theme = '$theme_id', style = '$style_id' WHERE user_id = $session->user_id;"); if ( !$e ) die( $db->get_error() ); - die('GOOD'); + + echo enano_json_encode(array( + 'success' => true + )); break; case 'get_tags': diff -r e9ee4e246f96 -r ecc764c10138 images/check-large.png Binary file images/check-large.png has changed diff -r e9ee4e246f96 -r ecc764c10138 includes/clientside/css/enano-shared.css --- a/includes/clientside/css/enano-shared.css Sat Mar 21 14:54:53 2009 -0400 +++ b/includes/clientside/css/enano-shared.css Sat Mar 21 14:55:34 2009 -0400 @@ -777,6 +777,12 @@ background-color: #606060; } +.abutton.block { + display: block; + width: 60%; + margin: 0 auto 10px auto; +} + .abutton_green { color: #008800 !important; } .abutton_green:hover { background-color: #008800 !important; } .abutton_blue { color: #000088 !important; } @@ -911,3 +917,88 @@ span.acl_failed_deps span.title { color: #ff0000; } + +/** + * Theme selector + */ + +div#theme-selector-wrapper { + position: absolute; + width: 100%; + margin: 0; + padding: 0; + top: 0; + margin-top: 75px; +} + +div#theme-selector-body { + margin: 0 auto; + padding: 20px; + background-color: #ffffff; + text-align: center; + /* width: 708px; */ + width: 130px; + height: 130px; +} + +div#theme-selector-inner h3 { + font-size: x-large; +} + +div#theme-selector-inner ul { + list-style-type: none; + margin: 0; + padding: 0; +} + +div#theme-selector-inner ul li { + float: left; +} + +div#theme-selector-inner ul li a { + display: block; + border: 1px solid #d0d0d0; + padding: 4px; + width: 216px; + line-height: 150px; + text-align: center; + margin: 2px 5px; + text-decoration: none; + background-position: center center; + background-repeat: no-repeat; + background-image: url(../../../images/themepreview.png); +} + +div#theme-selector-inner ul li a span { + color: #456798; + background-color: #fff; + opacity: 0; + filter: alpha(opacity=0); + display: block; + width: 216px; + line-height: 150px; + font-size: x-large; +} + +div#theme-selector-inner ul li a:hover span { + opacity: 0.6; + filter: alpha(opacity=60); +} + +div#theme-selector-inner .abutton { + font-size: x-large; +} + +div#theme-selector-inner ul li a span.loading { + background-image: url(../../../images/loading-big.gif); + background-position: center center; + background-repeat: no-repeat; +} + +div.theme-selector-spinner { + height: 130px; + background-image: url(../../../images/loading-big.gif); + background-position: center center; + background-repeat: no-repeat; + margin: 0 auto; +} diff -r e9ee4e246f96 -r ecc764c10138 includes/clientside/jsres.php --- a/includes/clientside/jsres.php Sat Mar 21 14:54:53 2009 -0400 +++ b/includes/clientside/jsres.php Sat Mar 21 14:55:34 2009 -0400 @@ -54,10 +54,7 @@ // // We need to see if this is a specially marked Enano development server. You can create an Enano -// development server by cloning the Mercurial repository into a directory named repo, and then -// using symlinks to reference the original files so as to segregate unique files from non-unique -// and distribution-standard ones. Enano will pivot its root directory accordingly if the file -// .enanodev is found in the Enano root (not /repo/). +// development server using the script found on hg.enanocms.org. if ( strpos(__FILE__, '/repo/') && ( file_exists('../../.enanodev') || file_exists('../../../.enanodev') ) ) { // We have a development directory. Remove /repo/ from the picture. diff -r e9ee4e246f96 -r ecc764c10138 includes/clientside/static/ajax.js --- a/includes/clientside/static/ajax.js Sat Mar 21 14:54:53 2009 -0400 +++ b/includes/clientside/static/ajax.js Sat Mar 21 14:55:34 2009 -0400 @@ -155,7 +155,6 @@ return false; var input = obj.getElementsByTagName('input')[0]; - console.debug(obj, input); if ( !input ) return false; var newname = input.value; @@ -738,142 +737,170 @@ // IE <6 pseudo-compatibility if ( KILL_SWITCH ) return true; - load_component(['l10n', 'messagebox', 'flyin', 'fadefilter']); + load_component(['l10n', 'fadefilter', 'jquery', 'jquery-ui']); + + // force string fetch + $lang.get('etc_cancel'); + + // preload some images + var i1 = new Image(); + i1.src = cdnPath + '/images/loading-big.gif'; + var i2 = new Image(); + i2.src = cdnPath + '/images/check-large.png'; + + darken(true, 70, 'theme-selector-shade'); + + $('body').append('
'); + $('#theme-selector-wrapper') + .css('top', String(getScrollOffset()) + 'px') + .css('z-index', String( getHighestZ() + 20 )); - var inner_html = ''; - inner_html += '

'; - var chtheme_mb = new MessageBox(MB_OKCANCEL|MB_ICONQUESTION, $lang.get('ajax_changestyle_title'), inner_html); - chtheme_mb.onbeforeclick['OK'] = ajaxChangeStyleComplete; + $.get(stdAjaxPrefix + '&_mode=theme_list', {}, function(data, status) + { + $('#theme-selector-inner .theme-selector-spinner').fadeOut(650); + $('#theme-selector-body').animate({ width: 708 }, 600, function() + { + // avoiding jQuery's fade functions because they insist on toggling display as well + changeOpac(0, 'theme-selector-inner'); + $('#theme-selector-inner').html('

'); + $('#theme-selector-inner > h3').text($lang.get('ajax_thmsel_lbl_choosetheme')); + $('#theme-selector-inner').append(''); + for ( var i = 0; i < data.length; i++ ) + { + var bgi = data[i].have_thumb ? cdnPath + '/themes/' + data[i].theme_id + '/preview.png' : cdnPath + '/images/themepreview.png'; + $('#theme-selector-inner > ul').append('
  • '); + $('#theme-selector-inner li#theme_' + i + ' > a') + .css('background-image', 'url(' + bgi + ')') + .attr('enano:theme_id', data[i].theme_id); + $('#theme-selector-inner li#theme_' + i + ' > a > span') + .text(data[i].theme_name); + } + $('#theme-selector-inner').append(''); + $('#theme-selector-inner').append('
    ' + $lang.get('etc_cancel') + '
    '); + + $('#theme-selector-body').animate({ height: $('#theme-selector-inner').height() + 30 }, 600, function() + { + opacity('theme-selector-inner', 0, 100, 750); + }); + + $('#theme-selector-inner li a').click(function() + { + var theme_id = $(this).attr('enano:theme_id'); + $('span', this).html(' ').addClass('loading').fadeTo('fast', 0.6) + $.get(stdAjaxPrefix + '&_mode=get_styles', { theme_id: theme_id }, function(data, status) + { + if ( data.length > 1 ) + { + $('#theme-selector-inner').css('height', $('#theme-selector-inner').height()).fadeOut(600, function() + { + var div = document.createElement('div'); + domObjChangeOpac(0, div); + + $(div).attr('id', 'theme-selector-style-list').append('

    '); + $('h3', div).text($lang.get('ajax_thmsel_lbl_choosestyle')); + + for ( var i = 0; i < data.length; i++ ) + { + $(div).append('' + themeid_to_title(data[i]) + ''); + } + + $(div).append('
    ' + $lang.get('etc_cancel') + '
    '); + + changeOpac(0, 'theme-selector-style-list'); + $(this).html(div).show(); + + $('#theme-selector-body').animate({width: 300, height: $('#theme-selector-style-list').height() + 30}, 300, function() + { + opacity('theme-selector-style-list', 0, 100, 500); + }); + + $('.stylebtn').click(function() + { + ajaxChangeThemeSetLoading(); + $.post(stdAjaxPrefix + '&_mode=change_theme', { theme_id: theme_id, style_id: $(this).attr('enano:style_id') }, function(data, status) + { + if ( data.error ) + { + alert(data.error); + ajaxChangeStyleClose(); + return false; + } + ajaxChangeThemeShowSuccess(); + }, 2000); + + return false; + }); + }); + } + else + { + if ( !data[0] ) + { + alert('Didn\'t find any CSS files. :-/'); + ajaxChangeStyleClose(); + } + + $.post(stdAjaxPrefix + '&_mode=change_theme', { theme_id: theme_id, style_id: data[0] }, function(data, status) + { + if ( data.error ) + { + alert(data.error); + ajaxChangeStyleClose(); + return false; + } + ajaxChangeThemeShowSuccess(); + }, 'json'); + } + }, 'json'); + return false; + }); // click function + }); // animate + }, 'json'); // get } -window.ajaxGetStyles = function(id) +window.ajaxChangeThemeSetLoading = function() { - // IE <6 pseudo-compatibility - if ( KILL_SWITCH ) - return true; - var thediv = document.getElementById('chtheme_sel_style_parent'); - if ( thediv ) - { - thediv.parentNode.removeChild(thediv); - } - if ( id == '_blank' ) - { - return null; - } - ajaxGet(stdAjaxPrefix + '&_mode=getstyles&id=' + id, function(ajax) { - if ( ajax.readyState == 4 && ajax.status == 200 ) + $('#theme-selector-body').animate({width: 130, height: 130}); + $('#theme-selector-inner').empty().html('
    '); +} + +window.ajaxChangeThemeShowSuccess = function() +{ + $('#theme-selector-body').animate({width: 400, height: 300 }, 600, function() { - // IE doesn't like substr() on ajax.responseText - var response = String(ajax.responseText + ''); - if ( response.substr(0,1) != '[' ) - { - alert('Invalid or unexpected JSON response from server:\n' + response); - return null; - } - - // Build a selector and matching label - var data = parseJSON(response); - var options = new Array(); - for( var i in data ) - { - var item = data[i]; - var title = themeid_to_title(item); - var option = document.createElement('option'); - option.value = item; - option.appendChild(document.createTextNode(title)); - options.push(option); - } - var p_parent = document.createElement('p'); - var label = document.createElement('label'); - p_parent.id = 'chtheme_sel_style_parent'; - label.appendChild(document.createTextNode($lang.get('ajax_changestyle_lbl_style') + ' ')); - var select = document.createElement('select'); - select.id = 'chtheme_sel_style'; - for ( var i in options ) - { - select.appendChild(options[i]); - } - label.appendChild(select); - p_parent.appendChild(label); - - // Stick it onto the messagebox - var div = document.getElementById('messageBox'); - var kid = div.firstChild.nextSibling; - - kid.appendChild(p_parent); - - } - }, true); + $('#theme-selector-inner').append(' '); + $('#theme-selector-inner').append('

    ' + $lang.get('ajax_thmsel_msg_success') + '

    '); + $('#theme-selector-inner').append('
    ' + $lang.get('ajax_thmsel_btn_reload') + '
    '); + $('#theme-selector-inner').append('
    ' + $lang.get('ajax_thmsel_btn_close') + '
    ' + $lang.get('ajax_thmsel_btn_close_hint') + '
    '); + $('#theme-selector-inner').fadeIn(); + }); + setTimeout(function() + { + $('#theme-selector-inner').empty(); + }, 10); } -window.ajaxChangeStyleComplete = function() +window.ajaxChangeStyleClose = function() { - // IE <6 pseudo-compatibility - if ( KILL_SWITCH ) - return true; - var theme = $dynano('chtheme_sel_theme'); - var style = $dynano('chtheme_sel_style'); - if ( !theme.object || !style.object ) - { - alert($lang.get('ajax_changestyle_pleaseselect_theme')); - return true; - } - var theme_id = theme.object.value; - var style_id = style.object.value; - - if ( typeof(theme_id) != 'string' || typeof(style_id) != 'string' ) - { - alert('Couldn\'t get theme or style ID'); - return true; - } - - if ( theme_id.length < 1 || style_id.length < 1 ) - { - alert('Theme or style ID is zero length'); - return true; - } - - ajaxPost(stdAjaxPrefix + '&_mode=change_theme', 'theme_id=' + ajaxEscape(theme_id) + '&style_id=' + ajaxEscape(style_id), function(ajax) + setTimeout(function() { - if ( ajax.readyState == 4 && ajax.status == 200 ) - { - if ( ajax.responseText == 'GOOD' ) + enlighten(false, 'theme-selector-shade'); + $('#theme-selector-wrapper').fadeOut(500, function() { - var c = confirm($lang.get('ajax_changestyle_success')); - if ( c ) - window.location.reload(); - } - else - { - alert('Error occurred during attempt to change theme:\n' + ajax.responseText); - } - } - }, true); - - return false; - + $(this).remove(); + }); + }, 300); + opacity('theme-selector-inner', 100, 0, 250); } -window.ajaxSwapCSS = function() +function themeid_to_title(id) { - // IE <6 pseudo-compatibility - if ( KILL_SWITCH ) - return true; - setAjaxLoading(); - if(_css) { - document.getElementById('mdgCss').href = main_css; - _css = false; - } else { - document.getElementById('mdgCss').href = print_css; - _css = true; - } - unsetAjaxLoading(); - menuOff(); + if ( typeof(id) != 'string' ) + return false; + id = id.substr(0, 1).toUpperCase() + id.substr(1); + id = id.replace(/_/g, ' '); + id = id.replace(/-/g, ' '); + return id; } window.ajaxSetPassword = function() @@ -1379,7 +1406,7 @@ }); } -window.ajaxPluginAction = function(action, plugin_filename, btnobj) +window.ajaxPluginAction = function(action, plugin_filename, btnobj, send_confirm) { // if installing, uninstalling, or re-importing, confirm if ( action == 'install' || action == 'uninstall' || action == 'reimport' ) @@ -1420,12 +1447,12 @@ ajaxPluginAction(this._action + '_confirm', this._filename, this._button); miniPromptDestroy(this); return false; - } + }; btn_cancel.onclick = function() { miniPromptDestroy(this); return false; - } + }; }); return true; } @@ -1436,10 +1463,15 @@ var td = btnobj.parentNode.parentNode.parentNode.parentNode; var blackbox = whiteOutElement(td); } - var request = toJSONString({ + var request = { mode: action, plugin: plugin_filename - }); + }; + if ( send_confirm ) + { + request.install_confirmed = true; + } + request = toJSONString(request); ajaxPost(makeUrlNS('Admin', 'PluginManager/action.json'), 'r=' + ajaxEscape(request), function(ajax) { if ( ajax.readyState == 4 && ajax.status == 200 ) @@ -1451,15 +1483,43 @@ return false; } response = parseJSON(response); + if ( blackbox ) + { + blackbox.parentNode.removeChild(blackbox); + } if ( response.success ) { - if ( blackbox ) - { - blackbox.parentNode.removeChild(blackbox); - } ajaxPage( namespace_list['Admin'] + 'PluginManager' ); return true; - } + } + if ( response.need_confirm ) + { + miniPromptMessage({ + title: $lang.get(response.confirm_title), + message: $lang.get(response.confirm_body), + buttons: [ + { + text: $lang.get('acppl_btn_install'), + color: 'red', + style: { + fontWeight: 'bold', + }, + onclick: function() { + ajaxPluginAction(action + '_confirm', plugin_filename, btnobj, true); + miniPromptDestroy(this); + } + }, + { + text: $lang.get('etc_cancel'), + color: 'blue', + onclick: function() { + miniPromptDestroy(this); + } + } + ] + }); + return true; + } // wait for fade effect to finish its run setTimeout(function() { @@ -1532,12 +1592,3 @@ }); } -function themeid_to_title(id) -{ - if ( typeof(id) != 'string' ) - return false; - id = id.substr(0, 1).toUpperCase() + id.substr(1); - id = id.replace(/_/g, ' '); - id = id.replace(/-/g, ' '); - return id; -} diff -r e9ee4e246f96 -r ecc764c10138 includes/clientside/static/dropdown.js --- a/includes/clientside/static/dropdown.js Sat Mar 21 14:54:53 2009 -0400 +++ b/includes/clientside/static/dropdown.js Sat Mar 21 14:55:34 2009 -0400 @@ -456,6 +456,8 @@ if(!type) type = '*'; ret = new Array(); + if ( !parent ) + return ret; el = parent.getElementsByTagName(type); for ( var i = 0; i < el.length; i++ ) { diff -r e9ee4e246f96 -r ecc764c10138 includes/clientside/static/fadefilter.js --- a/includes/clientside/static/fadefilter.js Sat Mar 21 14:54:53 2009 -0400 +++ b/includes/clientside/static/fadefilter.js Sat Mar 21 14:55:34 2009 -0400 @@ -14,7 +14,7 @@ if ( !opacVal ) opacVal = 70; darkener_index[layerid] = ( typeof(darkener_index[layerid]) == 'number' ) ? darkener_index[layerid] + 1 : 1; - if(document.getElementById(layerid)) + if(document.getElementById(layerid) && !document.getElementById(layerid).destroying) { document.getElementById(layerid).style.zIndex = getHighestZ() + 1; if(nofade) @@ -38,7 +38,18 @@ opacity(layerid, 0, opacVal, 1000); } } - } else { + } + else if(document.getElementById(layerid) && document.getElementById(layerid).destroying) + { + // fade in progress - abort + console.warn('Aborting fade'); + abortFades(); + changeOpac(opacVal, layerid); + document.getElementById(layerid).destroying = false; + return document.getElementById(layerid); + } + else + { w = getWidth(); h = getHeight(); var thediv = document.createElement('div'); @@ -103,9 +114,10 @@ } else { + document.getElementById(layerid).destroying = true; var from = document.getElementById(layerid).myOpacVal; opacity(layerid, from, 0, 1000); - setTimeout("document.getElementById('" + layerid + "').style.display = 'none';", 1000); + setTimeout("if ( document.getElementById('" + layerid + "').destroying ) { document.getElementById('" + layerid + "').destroying = false; document.getElementById('" + layerid + "').style.display = 'none'; }", 1000); } } return document.getElementById(layerid); diff -r e9ee4e246f96 -r ecc764c10138 includes/clientside/static/functions.js --- a/includes/clientside/static/functions.js Sat Mar 21 14:54:53 2009 -0400 +++ b/includes/clientside/static/functions.js Sat Mar 21 14:55:34 2009 -0400 @@ -598,7 +598,7 @@ domOpacity(object, opacStart, opacEnd, millisec); } -var opacityDOMCache = new Object(); +var opacityDOMCache = {}; function domOpacity(obj, opacStart, opacEnd, millisec) { //speed for each frame var speed = Math.round(millisec / 100); @@ -611,19 +611,24 @@ //determine the direction for the blending, if start and end are the same nothing happens if(opacStart > opacEnd) { for(i = opacStart; i >= opacEnd; i--) { - setTimeout("var obj = opacityDOMCache["+uniqid+"]; domObjChangeOpac(" + i + ",obj)",(timer * speed)); + setTimeout("if ( opacityDOMCache["+uniqid+"] ) { var obj = opacityDOMCache["+uniqid+"]; domObjChangeOpac(" + i + ",obj) }",(timer * speed)); timer++; } } else if(opacStart < opacEnd) { for(i = opacStart; i <= opacEnd; i++) { - setTimeout("var obj = opacityDOMCache["+uniqid+"]; domObjChangeOpac(" + i + ",obj)",(timer * speed)); + setTimeout("if ( opacityDOMCache["+uniqid+"] ) { var obj = opacityDOMCache["+uniqid+"]; domObjChangeOpac(" + i + ",obj); }",(timer * speed)); timer++; } } setTimeout("delete(opacityDOMCache["+uniqid+"]);",(timer * speed)); } +function abortFades() +{ + opacityDOMCache = {}; +} + // change the opacity for different browsers function changeOpac(opacity, id) { diff -r e9ee4e246f96 -r ecc764c10138 includes/clientside/static/json.js --- a/includes/clientside/static/json.js Sat Mar 21 14:54:53 2009 -0400 +++ b/includes/clientside/static/json.js Sat Mar 21 14:55:34 2009 -0400 @@ -11,6 +11,10 @@ function toJSONString(input) { + if ( window.JSON ) + { + return window.JSON.stringify(input); + } var m = { '\b': '\\b', '\t': '\\t', @@ -142,6 +146,11 @@ function parseJSON(string, filter) { + if ( window.JSON ) + { + return window.JSON.parse(string); + } + try { if (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/. test(string)) diff -r e9ee4e246f96 -r ecc764c10138 includes/clientside/static/paginate.js --- a/includes/clientside/static/paginate.js Sat Mar 21 14:54:53 2009 -0400 +++ b/includes/clientside/static/paginate.js Sat Mar 21 14:55:34 2009 -0400 @@ -322,6 +322,7 @@ var offset = ( userinput - 1 ) * perpage; if ( userinput > max || isNaN(userinput) || userinput < 1 ) { + load_component(['messagebox', 'fadefilter', 'flyin']); new MessageBox(MB_OK|MB_ICONSTOP, $lang.get('paginate_err_bad_page_title'), $lang.get('paginate_err_bad_page_body', { max: max })); return false; } diff -r e9ee4e246f96 -r ecc764c10138 includes/functions.php --- a/includes/functions.php Sat Mar 21 14:54:53 2009 -0400 +++ b/includes/functions.php Sat Mar 21 14:55:34 2009 -0400 @@ -2195,6 +2195,127 @@ } /** + * Generates the HTML of a pagination control. + * @param int Current page + * @param int Number of pages + * @param string sprintf()-style formatting URL for pages + * @param int Multiplier for start offset, defaults to 1 + * @param int Add to each $i for addition to result urls, usually either 0 or 1 (depends on whether you want your ?page= to start with 0 ro 1) + * @return string HTML + */ + +function generate_paginator($current_page, $num_pages, $result_url, $start_mult = 1, $start_add = 1) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + global $lang; + + $out = ''; + $i = 0; + + // Build paginator + $pg_css = ( strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') ) ? + // IE-specific hack + 'display: block; width: 1px;': + // Other browsers + 'display: table; margin: 10px 0 0 auto;'; + + $begin = '
    + + '; + $block = ''; + $end = '
    ' . $lang->get('paginate_lbl_page') . '{LINK}
    '; + $blk = $template->makeParserText($block); + $inner = ''; + $cls = 'row2'; + if ( $num_pages < 5 ) + { + for ( $i = 0; $i < $num_pages; $i++ ) + { + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $offset = strval(($i * $start_mult) + $start_add); + $url = htmlspecialchars(sprintf($result_url, $offset)); + $j = $i + 1; + $link = ( $i == $current_page ) ? "$j" : "$j"; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + } + } + else + { + if ( $current_page + 5 > $num_pages ) + { + $list = Array(); + $tp = $current_page; + if ( $current_page + 0 == $num_pages ) $tp = $tp - 3; + if ( $current_page + 1 == $num_pages ) $tp = $tp - 2; + if ( $current_page + 2 == $num_pages ) $tp = $tp - 1; + for ( $i = $tp - 1; $i <= $tp + 1; $i++ ) + { + $list[] = $i; + } + } + else + { + $list = Array(); + $current = $current_page; + $lower = ( $current < 3 ) ? 1 : $current - 1; + for ( $i = 0; $i < 3; $i++ ) + { + $list[] = $lower + $i; + } + } + $url = sprintf($result_url, '0'); + $link = ( 0 == $current_page ) ? "" . $lang->get('paginate_btn_first') . "" : "« " . $lang->get('paginate_btn_first') . ""; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + + foreach ( $list as $i ) + { + if ( $i == $num_pages ) + break; + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $offset = strval(($i * $start_mult) + $start_add); + $url = sprintf($result_url, $offset); + $j = $i + 1; + $link = ( $i == $current_page ) ? "$j" : "$j"; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + } + + // "Last" button + $total = (($num_pages - 1) * $start_mult) + $start_add; + + if ( $current_page < $num_pages ) + { + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $offset = strval($total); + $url = sprintf($result_url, $offset); + $link = ( $num_pages - 1 == $current_page ) ? "" . $lang->get('paginate_btn_last') . "" : "" . $lang->get('paginate_btn_last') . " »"; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + } + + } + + $inner .= '↓'; + + $paginator = "\n$begin$inner$end\n"; + return $paginator; +} + +/** * Paginates (breaks into multiple pages) a MySQL result resource, which is treated as unbuffered. * @param resource The MySQL result resource. This should preferably be an unbuffered query. * @param string A template, with variables being named after the column name @@ -2211,124 +2332,15 @@ function paginate($q, $tpl_text, $num_results, $result_url, $start = 0, $perpage = 10, $callers = Array(), $header = '', $footer = '') { global $db, $session, $paths, $template, $plugins; // Common objects - global $lang; $parser = $template->makeParserText($tpl_text); + $num_pages = ceil ( $num_results / $perpage ); $out = ''; + $this_page = ceil ( $start / $perpage ); $i = 0; - $this_page = ceil ( $start / $perpage ); - - // Build paginator - $pg_css = ( strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') ) ? - // IE-specific hack - 'display: block; width: 1px;': - // Other browsers - 'display: table; margin: 10px 0 0 auto;'; - $begin = '
    - - '; - $block = ''; - $end = '
    ' . $lang->get('paginate_lbl_page') . '{LINK}
    '; - $blk = $template->makeParserText($block); - $inner = ''; - $cls = 'row2'; - if ( $num_pages < 5 ) - { - for ( $i = 0; $i < $num_pages; $i++ ) - { - $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; - $offset = strval($i * $perpage); - $url = htmlspecialchars(sprintf($result_url, $offset)); - $j = $i + 1; - $link = ( $offset == strval($start) ) ? "$j" : "$j"; - $blk->assign_vars(array( - 'CLASS'=>$cls, - 'LINK'=>$link - )); - $inner .= $blk->run(); - } - } - else - { - if ( $this_page + 5 > $num_pages ) - { - $list = Array(); - $tp = $this_page; - if ( $this_page + 0 == $num_pages ) $tp = $tp - 3; - if ( $this_page + 1 == $num_pages ) $tp = $tp - 2; - if ( $this_page + 2 == $num_pages ) $tp = $tp - 1; - for ( $i = $tp - 1; $i <= $tp + 1; $i++ ) - { - $list[] = $i; - } - } - else - { - $list = Array(); - $current = $this_page; - $lower = ( $current < 3 ) ? 1 : $current - 1; - for ( $i = 0; $i < 3; $i++ ) - { - $list[] = $lower + $i; - } - } - $url = sprintf($result_url, '0'); - $link = ( 0 == $start ) ? "" . $lang->get('paginate_btn_first') . "" : "« " . $lang->get('paginate_btn_first') . ""; - $blk->assign_vars(array( - 'CLASS'=>$cls, - 'LINK'=>$link - )); - $inner .= $blk->run(); - - // if ( !in_array(1, $list) ) - // { - // $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; - // $blk->assign_vars(array('CLASS'=>$cls,'LINK'=>'...')); - // $inner .= $blk->run(); - // } - - foreach ( $list as $i ) - { - if ( $i == $num_pages ) - break; - $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; - $offset = strval($i * $perpage); - $url = sprintf($result_url, $offset); - $j = $i + 1; - $link = ( $offset == strval($start) ) ? "$j" : "$j"; - $blk->assign_vars(array( - 'CLASS'=>$cls, - 'LINK'=>$link - )); - $inner .= $blk->run(); - } - - $total = $num_pages * $perpage - $perpage; - - if ( $this_page < $num_pages ) - { - // $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; - // $blk->assign_vars(array('CLASS'=>$cls,'LINK'=>'...')); - // $inner .= $blk->run(); - - $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; - $offset = strval($total); - $url = sprintf($result_url, $offset); - $j = $i + 1; - $link = ( $offset == strval($start) ) ? "" . $lang->get('paginate_btn_last') . "" : "" . $lang->get('paginate_btn_last') . " »"; - $blk->assign_vars(array( - 'CLASS'=>$cls, - 'LINK'=>$link - )); - $inner .= $blk->run(); - } - - } - - $inner .= '↓'; - - $paginator = "\n$begin$inner$end\n"; + + $paginator = generate_paginator($this_page, $num_pages, $result_url, $perpage, 0); $out .= $paginator; $cls = 'row2'; @@ -2394,138 +2406,9 @@ $i = 0; $this_page = ceil ( $start / $perpage ); - // Build paginator - $begin = '
    - - '; - $block = ''; - $end = '
    ' . $lang->get('paginate_lbl_page') . '{LINK}
    '; - $blk = $template->makeParserText($block); - $inner = ''; - $cls = 'row2'; - $total = $num_pages * $perpage - $perpage; - /* - if ( $start > 0 ) - { - $url = sprintf($result_url, abs($start - $perpage)); - $link = "« " . $lang->get('paginate_btn_prev') . ""; - $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; - $blk->assign_vars(array( - 'CLASS'=>$cls, - 'LINK'=>$link - )); - $inner .= $blk->run(); - } - */ - if ( $num_pages < 5 ) - { - for ( $i = 0; $i < $num_pages; $i++ ) - { - $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; - $offset = strval($i * $perpage); - $url = htmlspecialchars(sprintf($result_url, $offset)); - $j = $i + 1; - $link = ( $offset == strval($start) ) ? "$j" : "$j"; - $blk->assign_vars(array( - 'CLASS'=>$cls, - 'LINK'=>$link - )); - $inner .= $blk->run(); - } - } - else - { - if ( $this_page + 5 > $num_pages ) - { - $list = Array(); - $tp = $this_page; - if ( $this_page + 0 == $num_pages ) $tp = $tp - 3; - if ( $this_page + 1 == $num_pages ) $tp = $tp - 2; - if ( $this_page + 2 == $num_pages ) $tp = $tp - 1; - for ( $i = $tp - 1; $i <= $tp + 1; $i++ ) - { - $list[] = $i; - } - } - else - { - $list = Array(); - $current = $this_page; - $lower = ( $current < 3 ) ? 1 : $current - 1; - for ( $i = 0; $i < 3; $i++ ) - { - $list[] = $lower + $i; - } - } - $url = sprintf($result_url, '0'); - $link = ( 0 == $start ) ? "" . $lang->get('paginate_btn_first') . "" : "« " . $lang->get('paginate_btn_first') . ""; - $blk->assign_vars(array( - 'CLASS'=>$cls, - 'LINK'=>$link - )); - $inner .= $blk->run(); - - // if ( !in_array(1, $list) ) - // { - // $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; - // $blk->assign_vars(array('CLASS'=>$cls,'LINK'=>'...')); - // $inner .= $blk->run(); - // } - - foreach ( $list as $i ) - { - if ( $i == $num_pages ) - break; - $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; - $offset = strval($i * $perpage); - $url = sprintf($result_url, $offset); - $j = $i + 1; - $link = ( $offset == strval($start) ) ? "$j" : "$j"; - $blk->assign_vars(array( - 'CLASS'=>$cls, - 'LINK'=>$link - )); - $inner .= $blk->run(); - } - - if ( $this_page < $num_pages ) - { - // $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; - // $blk->assign_vars(array('CLASS'=>$cls,'LINK'=>'...')); - // $inner .= $blk->run(); - - $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; - $offset = strval($total); - $url = sprintf($result_url, $offset); - $j = $i + 1; - $link = ( $offset == strval($start) ) ? "" . $lang->get('paginate_btn_last') . "" : "" . $lang->get('paginate_btn_last') . " »"; - $blk->assign_vars(array( - 'CLASS'=>$cls, - 'LINK'=>$link - )); - $inner .= $blk->run(); - } - - } - - /* - if ( $start < $total ) - { - $link_offset = abs($start + $perpage); - $url = htmlspecialchars(sprintf($result_url, strval($link_offset))); - $link = "" . $lang->get('paginate_btn_next') . " »"; - $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; - $blk->assign_vars(array( - 'CLASS'=>$cls, - 'LINK'=>$link - )); - $inner .= $blk->run(); - } - */ - - $inner .= '↓'; - - $paginator = "\n$begin$inner$end\n"; + $paginator = generate_paginator($this_page, $num_pages, $result_url, $perpage, 0); + $out .= $paginator; + if ( $total > 1 ) { $out .= $paginator; diff -r e9ee4e246f96 -r ecc764c10138 includes/namespaces/default.php --- a/includes/namespaces/default.php Sat Mar 21 14:54:53 2009 -0400 +++ b/includes/namespaces/default.php Sat Mar 21 14:55:34 2009 -0400 @@ -362,7 +362,7 @@ } else { - $pathskey = $paths->nslist[ $this->namespace ] . $paths->page_id; + $pathskey = $paths->nslist[ $this->namespace ] . $this->page_id; $page_format = $paths->pages[$pathskey]['page_format']; } diff -r e9ee4e246f96 -r ecc764c10138 includes/output.php --- a/includes/output.php Sat Mar 21 14:54:53 2009 -0400 +++ b/includes/output.php Sat Mar 21 14:55:34 2009 -0400 @@ -163,6 +163,24 @@ echo $template->getFooter(); echo $this->after_footer; + global $aggressive_optimize_html; + if ( $aggressive_optimize_html ) + { + $content = ob_get_contents(); + ob_end_clean(); + + ob_start(); + echo aggressive_optimize_html($content); + } + else + { + $content = ob_get_contents(); + ob_end_clean(); + + ob_start(); + echo preg_replace('~~', '', $content); + } + } public function set_title($title) @@ -175,6 +193,51 @@ } /** + * Same as HTML, except uses simple-header and simple-footer. + */ + +class Output_HTML_Simple extends Output_HTML +{ + public function footer() + { + global $template; + if ( !$this->headers_sent ) + return; + + $this->headers_sent = false; + $content = ob_get_contents(); + ob_end_clean(); + + ob_start(); + echo $this->before_header; + echo $template->getHeader(true); + echo $this->after_header; + echo $content; + echo $this->before_footer; + echo $template->getFooter(true); + echo $this->after_footer; + + global $aggressive_optimize_html; + if ( $aggressive_optimize_html ) + { + $content = ob_get_contents(); + ob_end_clean(); + + ob_start(); + echo aggressive_optimize_html($content); + } + else + { + $content = ob_get_contents(); + ob_end_clean(); + + ob_start(); + echo preg_replace('~~', '', $content); + } + } +} + +/** * Outputter that bypasses $template->header() and $template->footer(), but still shows HTML added via {before,after}_{header,footer}. */ diff -r e9ee4e246f96 -r ecc764c10138 includes/plugins.php --- a/includes/plugins.php Sat Mar 21 14:54:53 2009 -0400 +++ b/includes/plugins.php Sat Mar 21 14:55:34 2009 -0400 @@ -513,6 +513,29 @@ } /** + * Determines if a file is an authentication extension by looking at the file contents. + * @param string Plugin filename + * @return bool + */ + + function is_file_auth_plugin($filename) + { + $filename = ENANO_ROOT . '/plugins/' . $filename; + if ( !file_exists($filename) ) + return false; + + $info = $this->get_plugin_info($filename); + if ( isset($info['auth plugin']) ) + return true; + + $contents = @file_get_contents($filename); + if ( strstr($contents, 'login_process_userdata_json') ) + return true; + + return false; + } + + /** * Installs a plugin. * @param string Filename of plugin. * @param array The list of plugins as output by pluginLoader::get_plugin_list(). If not passed, the function is called, possibly wasting time. diff -r e9ee4e246f96 -r ecc764c10138 includes/template.php --- a/includes/template.php Sat Mar 21 14:54:53 2009 -0400 +++ b/includes/template.php Sat Mar 21 14:55:34 2009 -0400 @@ -1014,7 +1014,7 @@ $js_foot = << - + //]]> JSEOF; } @@ -1195,6 +1195,7 @@ 'ADMIN_SID_AMP_HTML' => $ash, 'ADMIN_SID_AUTO' => $as2, 'ADMIN_SID_RAW' => ( is_string($session->sid_super) ? $session->sid_super : '' ), + 'CSRF_TOKEN' => $session->csrf_token, 'COPYRIGHT' => RenderMan::parse_internal_links(getConfig('copyright_notice')), 'TOOLBAR_EXTRAS' => $this->toolbar_menu, 'REQUEST_URI' => ( defined('ENANO_CLI') ? '' : $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'] ), @@ -1271,9 +1272,20 @@ global $db, $session, $paths, $template, $plugins; // Common objects global $lang; - ob_start(); + echo $this->getHeader($simple); + } + + function footer($simple = false) + { + echo $this->getFooter($simple); + } + + function getHeader($simple = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + global $lang; - if(!$this->theme_loaded) + if ( !$this->theme_loaded ) { $this->load_theme($session->theme, $session->style); } @@ -1296,36 +1308,23 @@ } if ( !$simple && $session->user_logged_in && $session->unread_pms > 0 ) { - echo $this->notify_unread_pms(); + $header .= $this->notify_unread_pms(); } if ( !$simple && $session->sw_timed_out ) { $login_link = makeUrlNS('Special', 'Login/' . $paths->fullpage, 'level=' . $session->user_level, true); - echo '
    '; - echo $lang->get('user_msg_elev_timed_out', array( 'login_link' => $login_link )); - echo '
    '; + $header .= '
    '; + $header .= $lang->get('user_msg_elev_timed_out', array( 'login_link' => $login_link )); + $header .= '
    '; } if ( $this->site_disabled && $session->user_level >= USER_LEVEL_ADMIN && ( $paths->page != $paths->nslist['Special'] . 'Administration' ) ) { $admin_link = makeUrlNS('Special', 'Administration', 'module=' . $paths->nslist['Admin'] . 'GeneralConfig', true); - echo '
    ' . $lang->get('page_sitedisabled_admin_msg_title') . '
    + $header .= '
    ' . $lang->get('page_sitedisabled_admin_msg_title') . '
    ' . $lang->get('page_sitedisabled_admin_msg_body', array('admin_link' => $admin_link)) . '
    '; } } - - function footer($simple = false) - { - echo $this->getFooter($simple); - } - - function getHeader() - { - $headers_sent = true; - if(!defined('ENANO_HEADERS_SENT')) - define('ENANO_HEADERS_SENT', ''); - if(!$this->no_headers) return $this->process_template('header.tpl'); - } function getFooter($simple = false) { global $db, $session, $paths, $template, $plugins; // Common objects @@ -1644,21 +1643,30 @@ /** * Post-processor for template code. Basically what this does is it localizes {lang:foo} blocks. * @param string Mostly-processed TPL code + * @param bool Post-eval switch. If true, does not escape code. * @return string */ - function compile_template_text_post($text) + function compile_template_text_post($text, $post_eval = false) { + global $db, $session, $paths, $template, $plugins; // Common objects global $lang; + + // Language strings preg_match_all('/\{lang:([a-z0-9]+_[a-z0-9_]+)\}/', $text, $matches); foreach ( $matches[1] as $i => $string_id ) { $string = $lang->get($string_id); - $string = str_replace('\\', '\\\\', $string); - $string = str_replace('\'', '\\\'', $string); + if ( !$post_eval ) + { + $string = str_replace('\\', '\\\\', $string); + $string = str_replace('\'', '\\\'', $string); + } $text = str_replace_once($matches[0][$i], $string, $text); } - preg_match_all('/\{url:([A-z0-9]+):([\w\.\/:;\(\)@\[\]_=-]+)(?::([^\s\}]+))?(?:\|(escape))?\}/', $text, $matches); + + // URLs + preg_match_all('/\{url:([A-z0-9]+):([^\s\}]+?)(?:;([^\s\}]+?))?(?:\|(escape))?\}/i', $text, $matches); foreach ( $matches[1] as $i => $string_id ) { $namespace =& $matches[1][$i]; @@ -1672,11 +1680,47 @@ $result = makeUrlNS($namespace, $page_id, $params, $escape); + if ( !$post_eval ) + { + $result = str_replace('\\', '\\\\', $result); + $result = str_replace('\'', '\\\'', $result); + } + $text = str_replace_once($matches[0][$i], $result, $text); } + + $code = $plugins->setHook('compie_template_text_post'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + return $text; } + /** + * Returns the output of a theme hook + * @param string Hook name + * @return string + */ + + function get_theme_hook($hook) + { + global $db, $session, $paths, $template, $plugins; // Common objects + global $lang; + + ob_start(); + $code = $plugins->setHook($hook); + foreach ( $code as $cmd ) + { + eval($cmd); + } + $out = ob_get_contents(); + ob_end_clean(); + + return $out; + } + // n00bish comments removed from here. 2008-03-13 @ 12:02AM when I had nothing else to do. /** @@ -2601,6 +2645,9 @@ // System messages $text = preg_replace('//is', '\' . $template->tplWikiFormat($paths->sysMsg(\'\\1\')) . \'', $text); + // Hooks + $text = preg_replace('//', '\' . $this->get_theme_hook(\'\\1\') . \'', $text); + // only do this if the plugins API is loaded if ( is_object(@$plugins) ) { diff -r e9ee4e246f96 -r ecc764c10138 index.php --- a/index.php Sat Mar 21 14:54:53 2009 -0400 +++ b/index.php Sat Mar 21 14:55:34 2009 -0400 @@ -575,40 +575,24 @@ break; } - // - // Optimize HTML by replacing newlines with spaces (excludes
    ,