# HG changeset patch # User Dan # Date 1207701150 14400 # Node ID 3f2dfdb99be4cfb85e16199f569204401cf810dc # Parent 26287ae2449d3089a3e81274e7065f394382547b# Parent d264784355e5c1940b76944536ed3ecc2c846cdd Merging nighthawk and scribus branches diff -r d264784355e5 -r 3f2dfdb99be4 images/prompt-body.png Binary file images/prompt-body.png has changed diff -r d264784355e5 -r 3f2dfdb99be4 images/prompt-bottom.png Binary file images/prompt-bottom.png has changed diff -r d264784355e5 -r 3f2dfdb99be4 images/prompt-top.png Binary file images/prompt-top.png has changed diff -r d264784355e5 -r 3f2dfdb99be4 includes/clientside/css/enano-shared.css --- a/includes/clientside/css/enano-shared.css Tue Apr 08 20:30:05 2008 -0400 +++ b/includes/clientside/css/enano-shared.css Tue Apr 08 20:32:30 2008 -0400 @@ -745,3 +745,41 @@ background-image: url(../../../images/expander/open-prelight.gif); } +/* Flown-in mini prompts */ + +div.miniprompt { + position: absolute; +} + +div.miniprompt div.mp-top, div.miniprompt div.mp-bottom { + width: 388px; + height: 57px; + background-image: url(../../../images/prompt-top.png); + background-repeat: no-repeat; + background-position: center center; +} + +div.miniprompt div.mp-bottom { + height: 42px; + background-image: url(../../../images/prompt-bottom.png); +} + +div.miniprompt div.mp-body { + padding: 0 44px 10px 44px; + width: 300px; + background-image: url(../../../images/prompt-body.png); + background-repeat: repeat-y; + background-position: center center; +} + +div.miniprompt h3 { + /* fix padding issues on firefox */ + margin: 0 0 10px 0; +} + +/* for buttons */ +div.miniprompt div.mp-buttons { + text-align: right; + position: relative; + top: 10px; +} diff -r d264784355e5 -r 3f2dfdb99be4 includes/clientside/static/ajax.js --- a/includes/clientside/static/ajax.js Tue Apr 08 20:30:05 2008 -0400 +++ b/includes/clientside/static/ajax.js Tue Apr 08 20:32:30 2008 -0400 @@ -239,15 +239,138 @@ // IE <6 pseudo-compatibility if ( KILL_SWITCH ) return true; - r = prompt($lang.get('ajax_rename_prompt')); - if(!r || r=='') return; - setAjaxLoading(); - ajaxPost(stdAjaxPrefix+'&_mode=rename', 'newtitle='+ajaxEscape(r), function() { - if ( ajax.readyState == 4 && ajax.status == 200 ) { - unsetAjaxLoading(); - alert(ajax.responseText); - } - }, true); + + // updated - 1.1.4 to use miniPrompt + miniPrompt(ajaxRenameConstructDialog); +} + +var ajaxRenameConstructDialog = function(div) +{ + // title + var heading = document.createElement('h3'); + heading.appendChild(document.createTextNode($lang.get('ajax_rename_prompt_short'))); + div.appendChild(heading); + + // form + var form = document.createElement('form'); + form.action = 'javascript:void(0);'; + + // box + var box = document.createElement('input'); + box.size = '43'; + box.style.width = '100%'; + form.appendChild(box); + div.appendChild(form); + + // notice + var notice = document.createElement('small'); + notice.appendChild(document.createTextNode($lang.get('ajax_rename_notice'))); + div.appendChild(notice); + + // button area + var btndiv = document.createElement('div'); + btndiv.className = 'mp-buttons'; + + // buttons + var btn_submit = document.createElement('a'); + btn_submit.href = '#'; + btn_submit.appendChild(document.createTextNode($lang.get('etc_go'))); + btn_submit.className = 'fakebutton fakebutton-submit'; + + var btn_cancel = document.createElement('a'); + btn_cancel.href = '#'; + btn_cancel.appendChild(document.createTextNode($lang.get('etc_cancel'))); + btn_cancel.className = 'fakebutton'; + + btndiv.appendChild(btn_submit); + btndiv.appendChild(document.createTextNode(' | ')); + btndiv.appendChild(btn_cancel); + div.appendChild(btndiv); + + // events + btn_submit.onclick = function() + { + ajaxRenameSubmit(this); + return false; + } + btn_cancel.onclick = function() + { + miniPromptDestroy(this); + return false; + } + form.onsubmit = function() + { + ajaxRenameSubmit(this); + return false; + } + + setTimeout(function() + { + box.focus(); + }, 200); +} + +function ajaxRenameSubmit(obj) +{ + var box = miniPromptGetParent(obj); + if ( !box ) + return false; + + var newname = ( obj.getElementsByTagName('input')[0] ).value; + newname = trim(newname); + + if ( newname.length < 1 ) + { + alert($lang.get('ajax_rename_too_short')); + return false; + } + + if ( !newname ) + { + return false; + } + + var innerBox = getElementsByClassName(box, 'div', 'mp-body')[0]; + var whiteout = whiteOutElement(innerBox); + whiteout.style.width = ( $(whiteout).Width() - 78 ) + 'px'; + whiteout.style.left = ( $(whiteout).Left() + 44 ) + 'px'; + + ajaxPost(stdAjaxPrefix + '&_mode=rename', 'newtitle=' + ajaxEscape(newname), function() + { + if ( ajax.readyState == 4 && ajax.status == 200 ) + { + whiteout.parentNode.removeChild(whiteout); + var response = String(ajax.responseText); + if ( response.substr(0, 1) != '{' ) + { + handle_invalid_json(response); + return false; + } + response = parseJSON(response); + if ( response.success ) + { + miniPromptDestroy(box, true); + ajaxRenameDoClientTransform(newname); + new messagebox( MB_OK|MB_ICONINFORMATION, $lang.get('ajax_rename_success_title'), $lang.get('ajax_rename_success_body', { page_name_new: newname }) ); + mb_previously_had_darkener = false; + } + else + { + var errmsg = $lang.get('page_err_' + response.error); + alert(errmsg); + } + } + }, true); +} + +function ajaxRenameDoClientTransform(newname) +{ + var obj = document.getElementById('h2PageName'); + if ( obj ) + { + obj.firstChild.nodeValue = newname; + } + document.title = newname; } function ajaxMakePage() diff -r d264784355e5 -r 3f2dfdb99be4 includes/clientside/static/dropdown.js --- a/includes/clientside/static/dropdown.js Tue Apr 08 20:30:05 2008 -0400 +++ b/includes/clientside/static/dropdown.js Tue Apr 08 20:32:30 2008 -0400 @@ -92,6 +92,10 @@ obj.childNodes[i].onmouseover = function() { jBoxOverHandler(this); }; obj.childNodes[i].onmouseout = function(e) { jBoxOutHandler(this, e); }; obj.childNodes[i].nextSibling.onmouseout = function(e) { jBoxOutHandler(this, e); }; + if ( is_iPhone ) + { + obj.childNodes[i].onclick = function() { jBoxOverHandler(this); return false; }; + } } } } diff -r d264784355e5 -r 3f2dfdb99be4 includes/clientside/static/faders.js --- a/includes/clientside/static/faders.js Tue Apr 08 20:30:05 2008 -0400 +++ b/includes/clientside/static/faders.js Tue Apr 08 20:32:30 2008 -0400 @@ -1,20 +1,39 @@ -// Message box system +// Message box and visual effect system -function darken(nofade) +/** + * Darkens the browser screen. This will make the entire page un-clickable except for any floating divs created after this is called. Restore with enlighten(). + * @param bool Controls whether the fade should be disabled or not. aclDisableTransitionFX will override this if set to true, and fades are never fired on IE. + * @param int When specified, represents the numeric opacity value to set the fade layer to. 1-100. + */ + +function darken(nofade, opacVal) { if(IE) nofade = true; + if ( !opacVal ) + opacVal = 70; if(document.getElementById('specialLayer_darkener')) { if(nofade) { - changeOpac(70, 'specialLayer_darkener'); + changeOpac(opacVal, 'specialLayer_darkener'); document.getElementById('specialLayer_darkener').style.display = 'block'; + document.getElementById('specialLayer_darkener').myOpacVal = opacVal; } else { - document.getElementById('specialLayer_darkener').style.display = 'block'; - opacity('specialLayer_darkener', 0, 70, 1000); + if ( document.getElementById('specialLayer_darkener').style.display != 'none' ) + { + var currentOpac = document.getElementById('specialLayer_darkener').myOpacVal; + opacity('specialLayer_darkener', currentOpac, opacVal, 1000); + document.getElementById('specialLayer_darkener').myOpacVal = opacVal; + } + else + { + document.getElementById('specialLayer_darkener').style.display = 'block'; + document.getElementById('specialLayer_darkener').myOpacVal = opacVal; + opacity('specialLayer_darkener', 0, opacVal, 1000); + } } } else { w = getWidth(); @@ -41,10 +60,11 @@ thediv.style.height = '100%'; thediv.zIndex = getHighestZ() + 5; thediv.id = 'specialLayer_darkener'; + thediv.myOpacVal = opacVal; if(nofade) { - thediv.style.opacity = '0.7'; - thediv.style.filter = 'alpha(opacity=70)'; + thediv.style.opacity = ( parseFloat(opacVal) / 100 ); + thediv.style.filter = 'alpha(opacity=' + opacVal + ')'; body = document.getElementsByTagName('body'); body = body[0]; body.appendChild(thediv); @@ -52,11 +72,16 @@ body = document.getElementsByTagName('body'); body = body[0]; body.appendChild(thediv); - opacity('specialLayer_darkener', 0, 70, 1000); + opacity('specialLayer_darkener', 0, opacVal, 1000); } } } +/** + * Un-darkens the screen and re-enables clicking of on-screen controls. + * @param bool If true, disables the fade effect. Fades are always disabled if aclDisableTransitionFX is true and on IE. + */ + function enlighten(nofade) { if(IE) @@ -67,8 +92,13 @@ { document.getElementById('specialLayer_darkener').style.display = 'none'; } - opacity('specialLayer_darkener', 70, 0, 1000); - setTimeout("document.getElementById('specialLayer_darkener').style.display = 'none';", 1000); + else + { + var from = document.getElementById('specialLayer_darkener').myOpacVal; + // console.info('Fading from ' + from); + opacity('specialLayer_darkener', from, 0, 1000); + setTimeout("document.getElementById('specialLayer_darkener').style.display = 'none';", 1000); + } } } @@ -388,6 +418,102 @@ } } +/** + * The miniPrompt function, for creating small prompts and dialogs. The window will be flown in and the window darkened with opac=0.4. + * @param function Will be passed an HTMLElement that is the body of the prompt window; the function can do with this as it pleases + */ + +function miniPrompt(call_on_create) +{ + darken(false, 40); + + var wrapper = document.createElement('div'); + wrapper.className = 'miniprompt'; + var top = document.createElement('div'); + top.className = 'mp-top'; + var body = document.createElement('div'); + body.className = 'mp-body'; + var bottom = document.createElement('div'); + bottom.className = 'mp-bottom'; + if ( typeof(call_on_create) == 'function' ) + { + call_on_create(body); + } + wrapper.appendChild(top); + wrapper.appendChild(body); + wrapper.appendChild(bottom); + var left = ( getWidth() / 2 ) - ( 388 / 2 ); + wrapper.style.left = left + 'px'; + var top = getScrollOffset() - 27; + wrapper.style.top = top + 'px'; + domObjChangeOpac(0, wrapper); + var realbody = document.getElementsByTagName('body')[0]; + realbody.appendChild(wrapper); + + fly_in_top(wrapper, true, true); + + setTimeout(function() + { + domObjChangeOpac(100, wrapper); + }, 40); +} + +/** + * For a given element, loops through the element and all of its ancestors looking for a miniPrompt div, and returns it. Returns false on failure. + * @param object:HTMLElement Child node to scan + * @return object + */ + +function miniPromptGetParent(obj) +{ + while ( true ) + { + // prevent infinite loops + if ( !obj || obj.tagName == 'BODY' ) + return false; + + if ( $dynano(obj).hasClass('miniprompt') ) + { + return obj; + } + obj = obj.parentNode; + } + return false; +} + +/** + * Destroys the first miniPrompt div encountered by recursively checking all parent nodes. + * Usage: click + * @param object:HTMLElement a child of the div.miniprompt + * @param bool If true, does not call enlighten(). + */ + +function miniPromptDestroy(obj, nofade) +{ + obj = miniPromptGetParent(obj); + if ( !obj ) + return false; + + // found it + var parent = obj.parentNode; + if ( !nofade ) + enlighten(); + var timeout = fly_out_top(obj, true, true); + setTimeout(function() + { + parent.removeChild(obj); + }, timeout); +} + +/** + * Simple test case + */ + +function miniPromptTest() +{ + miniPrompt(function(div) { div.innerHTML = 'hello world! destroy me'; }); +} + // Function to fade classes info-box, warning-box, error-box, etc. function fadeInfoBoxes() diff -r d264784355e5 -r 3f2dfdb99be4 includes/functions.php --- a/includes/functions.php Tue Apr 08 20:30:05 2008 -0400 +++ b/includes/functions.php Tue Apr 08 20:32:30 2008 -0400 @@ -4078,6 +4078,17 @@ } /** + * Trims a snippet of text to the first and last curly braces. Useful for JSON. + * @param string Text to trim + * @return string + */ + +function enano_trim_json($json) +{ + return preg_replace('/^([^{]+)\{/', '{', preg_replace('/\}([^}]+)$/', '}', $json)); +} + +/** * Starts the profiler. */ diff -r d264784355e5 -r 3f2dfdb99be4 includes/lang.php --- a/includes/lang.php Tue Apr 08 20:30:05 2008 -0400 +++ b/includes/lang.php Tue Apr 08 20:32:30 2008 -0400 @@ -389,8 +389,7 @@ $contents =& $block[0]['value']; // Trim off all text before and after the starting and ending braces - $contents = preg_replace('/^([^{]+)\{/', '{', $contents); - $contents = preg_replace('/\}([^}]+)$/', '}', $contents); + $contents = enano_trim_json($contents); // Correct syntax to be nice to the json parser $contents = enano_clean_json($contents); diff -r d264784355e5 -r 3f2dfdb99be4 includes/plugins.php --- a/includes/plugins.php Tue Apr 08 20:30:05 2008 -0400 +++ b/includes/plugins.php Tue Apr 08 20:32:30 2008 -0400 @@ -224,9 +224,22 @@ if ( is_string($type) && $blocks[1][$i] !== $type ) continue; + $value =& $blocks[4][$i]; + // parse includes + preg_match_all('/^!include [\'"]?(.+?)[\'"]?$/m', $value, $includes); + foreach ( $includes[0] as $i => $replace ) + { + $filename = ENANO_ROOT . '/' . $includes[1][$i]; + if ( @file_exists( $filename ) && @is_readable( $filename ) ) + { + $contents = @file_get_contents($filename); + $value = str_replace_once($replace, $contents, $value); + } + } + $el = self::parse_vars($blocks[2][$i]); $el['block'] = $blocks[1][$i]; - $el['value'] = $blocks[4][$i]; + $el['value'] = $value; $return[] = $el; } diff -r d264784355e5 -r 3f2dfdb99be4 includes/rijndael.php --- a/includes/rijndael.php Tue Apr 08 20:30:05 2008 -0400 +++ b/includes/rijndael.php Tue Apr 08 20:32:30 2008 -0400 @@ -807,10 +807,11 @@ * @param string $text the encrypted text * @param string $key the raw binary key used to encrypt the text * @param int $input_encoding the encoding used for the encrypted string. Can be ENC_BINARY, ENC_HEX, or ENC_BASE64. + * @param bool $no_cache If true, will not cache the decrypted string on disk. * @return string */ - function decrypt($text, $key, $input_encoding = ENC_HEX) + function decrypt($text, $key, $input_encoding = ENC_HEX, $no_cache = false) { if ( $text == '' ) return ''; @@ -871,7 +872,8 @@ $this->decrypt_cache[$key_bin][$text_bin] = $dypt; - aes_decrypt_cache_store($text_bin, $dypt, $key_bin); + if ( !$no_cache ) + aes_decrypt_cache_store($text_bin, $dypt, $key_bin); return $dypt; } diff -r d264784355e5 -r 3f2dfdb99be4 includes/sessions.php --- a/includes/sessions.php Tue Apr 08 20:30:05 2008 -0400 +++ b/includes/sessions.php Tue Apr 08 20:32:30 2008 -0400 @@ -516,7 +516,7 @@ $super = $this->validate_session($key); } } - if(is_array($super)) + if(is_array(@$super)) { $this->auth_level = intval($super['auth_level']); $this->sid_super = $_REQUEST['auth']; @@ -3233,7 +3233,8 @@ // decrypt user info $aes_key = hexdecode($aes_key); $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE); - $userinfo_json = $aes->decrypt($userinfo_crypt, $aes_key, ENC_HEX); + // using "true" here disables caching of the decrypted login info (which includes the password) + $userinfo_json = $aes->decrypt($userinfo_crypt, $aes_key, ENC_HEX, true); if ( !$userinfo_json ) { return array( diff -r d264784355e5 -r 3f2dfdb99be4 install/schemas/mysql_stage2.sql --- a/install/schemas/mysql_stage2.sql Tue Apr 08 20:30:05 2008 -0400 +++ b/install/schemas/mysql_stage2.sql Tue Apr 08 20:32:30 2008 -0400 @@ -318,6 +318,17 @@ PRIMARY KEY ( key_id ) ) CHARACTER SET `utf8` COLLATE `utf8_bin`; +-- Added in 1.1.4 +-- This is really honestly a better way to handle plugins. + +CREATE TABLE {{TABLE_PREFIX}}plugins ( + plugin_id int(12) NOT NULL auto_increment, + plugin_filename varchar(63), + plugin_flags int(12), + plugin_version varchar(16), + PRIMARY KEY ( plugin_id ) +) ENGINE `MyISAM` CHARACTER SET `utf8` COLLATE `utf8_bin`; + INSERT INTO {{TABLE_PREFIX}}config(config_name, config_value) VALUES ('site_name', '{{SITE_NAME}}'), ('main_page', 'Main_Page'), @@ -335,10 +346,6 @@ ('w3c_vcss', '0'), ('approve_comments', '0'), ('enable_comments', '1'), - ('plugin_SpecialAdmin.php', '1'), - ('plugin_SpecialPageFuncs.php', '1'), - ('plugin_SpecialUserFuncs.php', '1'), - ('plugin_SpecialCSS.php', '1'), ('copyright_notice', '{{COPYRIGHT}}'), ('wiki_edit_notice_text', '== Why can I edit this page? ==\n\nEveryone can edit almost any page in this website. This concept is called a wiki. It gives everyone the opportunity to make a change for the best. While some spam and vandalism may occur, it is believed that most contributions will be legitimate and helpful.\n\nFor security purposes, a history of all page edits is kept, and administrators are able to restore vandalized or spammed pages with just a few clicks.'), ('cache_thumbs', '{{ENABLE_CACHE}}'), diff -r d264784355e5 -r 3f2dfdb99be4 install/schemas/postgresql_stage2.sql --- a/install/schemas/postgresql_stage2.sql Tue Apr 08 20:30:05 2008 -0400 +++ b/install/schemas/postgresql_stage2.sql Tue Apr 08 20:32:30 2008 -0400 @@ -315,6 +315,17 @@ PRIMARY KEY ( key_id ) ); +-- Added in 1.1.4 +-- This is really honestly a better way to handle plugins. + +CREATE TABLE {{TABLE_PREFIX}}plugins ( + plugin_id SERIAL, + plugin_filename varchar(63), + plugin_flags int, + plugin_version varchar(16), + PRIMARY KEY ( plugin_id ) +); + INSERT INTO {{TABLE_PREFIX}}config(config_name, config_value) VALUES ('site_name', '{{SITE_NAME}}'), ('main_page', 'Main_Page'), diff -r d264784355e5 -r 3f2dfdb99be4 install/schemas/upgrade/1.1.3-1.1.4-mysql.sql --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/install/schemas/upgrade/1.1.3-1.1.4-mysql.sql Tue Apr 08 20:32:30 2008 -0400 @@ -0,0 +1,10 @@ +-- This is really honestly a better way to handle plugins. + +CREATE TABLE {{TABLE_PREFIX}}plugins ( + plugin_id int(12) NOT NULL auto_increment, + plugin_filename varchar(63), + plugin_flags int(12), + plugin_version varchar(16), + PRIMARY KEY ( plugin_id ) +) ENGINE `MyISAM` CHARACTER SET `utf8` COLLATE `utf8_bin`; + diff -r d264784355e5 -r 3f2dfdb99be4 install/schemas/upgrade/1.1.3-1.1.4-postgresql.sql --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/install/schemas/upgrade/1.1.3-1.1.4-postgresql.sql Tue Apr 08 20:32:30 2008 -0400 @@ -0,0 +1,9 @@ +-- This is really honestly a better way to handle plugins. + +CREATE TABLE {{TABLE_PREFIX}}plugins ( + plugin_id SERIAL, + plugin_filename varchar(63), + plugin_flags int, + plugin_version varchar(16), + PRIMARY KEY ( plugin_id ) +); diff -r d264784355e5 -r 3f2dfdb99be4 language/english/admin.json --- a/language/english/admin.json Tue Apr 08 20:30:05 2008 -0400 +++ b/language/english/admin.json Tue Apr 08 20:32:30 2008 -0400 @@ -382,24 +382,7 @@ msg_demo_mode: 'Hmm, enabling executables, are we? Tsk tsk. I\'d love to know what\'s in that EXE file you want to upload. OK, maybe you didn\'t enable EXEs. But nevertheless, changing allowed filetypes is disabled in the demo.', }, acppl: { - err_heading: 'Error disabling plugin', - err_demo_plugin: 'The demo lockdown plugin cannot be disabled in demo mode.', - err_system_plugin: 'The plugin you selected cannot be disabled because it is a system plugin.', - err_open_dir: 'The plugins/ directory could not be opened.', - err_missing_dir: 'The plugins/ directory is missing from your Enano installation.', - col_filename: 'Plugin filename', - col_name: 'Plugin name', - col_description: 'Description', - col_author: 'Author', - col_version: 'Version', - btn_enable: 'Enable', - btn_disable: 'Disable', - btn_reimport: 'Re-import', - btn_reimport_tip: 'Reloads all the language and meta data from this plugin file.', - msg_reimport_success: 'All language strings from this plugin have been re-imported.', - btn_hide_system: 'Hide system plugins', - btn_show_system: 'Show system plugins', - lbl_system_plugin: '[System]', + }, acppm: { heading_main: 'Edit page properties', diff -r d264784355e5 -r 3f2dfdb99be4 language/english/core.json --- a/language/english/core.json Tue Apr 08 20:30:05 2008 -0400 +++ b/language/english/core.json Tue Apr 08 20:32:30 2008 -0400 @@ -430,7 +430,9 @@ ajax: { // Client-side messages protect_prompt_reason: 'Reason for (un)protecting:', - rename_prompt: 'What title should this page be renamed to?\nNote: This does not and will never change the URL of this page, that must be done from the admin panel.', + rename_prompt: 'What title should this page be renamed to?\n%this.ajax_rename_notice%', + rename_prompt_short: 'Enter a new name for this page', + rename_notice: 'This won\'t change the URL to this page. To change the URL to this page, use Page Manager in the administration panel.', delete_prompt_reason: 'Please enter your reason for deleting this page.', delete_confirm: 'You are about to REVERSIBLY delete this page. Do you REALLY want to do this?\n\n(Comments and categorization data, as well as any attached files, will be permanently lost)', delvote_confirm: 'Are you sure that you want to vote that this page be deleted?', @@ -449,7 +451,8 @@ // Server-side responses rename_too_short: 'The name you entered is too short. Please enter a longer name for this page.', - rename_success: 'The page "%page_name_old%" has been renamed to "%page_name_new%". You are encouraged to leave a comment explaining your action.\n\nYou will see the change take effect the next time you reload this page.', + rename_success_title: 'Page renamed', + rename_success_body: 'This page has been renamed to "%page_name_new%". You are encouraged to leave a comment explaining your action.

Please note that you might not see this change take effect until you reload the page.', clearlogs_success: 'The logs for this page have been cleared. A backup of this page has been added to the logs table so that this page can be restored in case of vandalism or spam later.', delete_need_reason: 'Invalid reason for deletion passed. Please enter a reason for deleting this page.', delete_success: 'This page has been deleted. Note that there is still a log of edits and actions in the database, and anyone with admin rights can raise this page from the dead unless the log is cleared. If the deleted file is an image, there may still be cached thumbnails of it in the cache/ directory, which is inaccessible to users.', @@ -458,7 +461,6 @@ delvote_reset_success: 'The number of votes for having this page deleted has been reset to zero.', password_success: 'The password for this page has been set.', password_disable_success: 'The password for this page has been disabled.', - }, sidebar: { title_navigation: 'Navigation', @@ -691,6 +693,7 @@ cancel: 'Cancel', yes: 'Yes', no: 'No', + go: 'Go', unit_bytes: 'bytes', unit_kilobytes: 'kilobytes', unit_megabytes: 'megabytes', diff -r d264784355e5 -r 3f2dfdb99be4 plugins/PrivateMessages.php --- a/plugins/PrivateMessages.php Tue Apr 08 20:30:05 2008 -0400 +++ b/plugins/PrivateMessages.php Tue Apr 08 20:32:30 2008 -0400 @@ -1,12 +1,14 @@ '.$showhide_link.''; echo ''; } +*/ function page_Admin_DBBackup() { diff -r d264784355e5 -r 3f2dfdb99be4 plugins/SpecialCSS.php --- a/plugins/SpecialCSS.php Tue Apr 08 20:30:05 2008 -0400 +++ b/plugins/SpecialCSS.php Tue Apr 08 20:32:30 2008 -0400 @@ -1,12 +1,14 @@ + /**!blocktype( param1 = "value1" [ param2 = "value2" ... ] )** + + ... block content ... + + **!* / (remove that last space) + + * The format inside blocks varies. Metadata and language strings will be in JSON; installation and upgrade schemas will be in + * SQL. You can include an external file into a block using the following syntax inside of a block: + + !include "path/to/file" + + * The file will always be relative to the Enano root. So if your plugin has a language file in ENANO_ROOT/plugins/fooplugin/, + * you would use "plugins/fooplugin/language.json". + * + * The format for plugin metadata is as follows: + + /**!info** + { + "Plugin Name" : "Foo plugin", + "Plugin URI" : "http://fooplugin.enanocms.org/", + "Description" : "Some short descriptive text", + "Author" : "John Doe", + "Version" : "0.1", + "Author URI" : "http://yourdomain.com/", + "Version list" : [ "0.1-alpha1", "0.1-alpha2", "0.1-beta1", "0.1" ] + } + **!* / + + * This is the format for language data: + + /**!language** + { + eng: { + categories: [ 'meta', 'foo', 'bar' ], + strings: { + meta: { + foo: "Foo strings", + bar: "Bar strings" + }, + foo: { + string_name: "string value", + string_name_2: "string value 2" + } + } + } + } + **!* / (once more, remove the space in there) + + * Here is the format for installation schemas: + + /**!install** + + CREATE TABLE {{TABLE_PREFIX}}foo_table( + ... + ) + + **!* / + + * And finally, the format for upgrade schemas: + + /**!upgrade from = "0.1-alpha1" to = "0.1-alpha2" ** + + **!* / + + * Remember that upgrades will always be done incrementally, so if the user is upgrading 0.1-alpha2 to 0.1, Enano's plugin + * engine will run the 0.1-alpha2 to 0.1-beta1 upgrader, then the 0.1-beta1 to 0.1 upgrader, going by the versions listed in + * the example metadata block above. + * + * All of this information is effective as of Enano 1.1.4. + */ + +// Plugin manager "2.0" + +function page_Admin_PluginManager() +{ + global $db, $session, $paths, $template, $plugins; // Common objects + global $lang; + if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN ) + { + $login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true); + echo '

' . $lang->get('adm_err_not_auth_title') . '

'; + echo '

' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '

'; + return; + } + + // Are we processing an AJAX request from the smartform? + if ( $paths->getParam(0) == 'action.json' ) + { + // Set to application/json to discourage advertisement scripts + header('Content-Type: application/json'); + + // Init return data + $return = array('mode' => 'error', 'error' => 'undefined'); + + // Start parsing process + try + { + // Is the request properly sent on POST? + if ( isset($_POST['r']) ) + { + // Try to decode the request + $request = enano_json_decode($_POST['r']); + // Is the action to perform specified? + if ( isset($request['mode']) ) + { + switch ( $request['mode'] ) + { + default: + // The requested action isn't something this script knows how to do + $return = array( + 'mode' => 'error', + 'error' => 'Unknown mode "' . $request['mode'] . '" sent in request' + ); + break; + } + } + else + { + // Didn't specify action + $return = array( + 'mode' => 'error', + 'error' => 'Missing key "mode" in request' + ); + } + } + else + { + // Didn't send a request + $return = array( + 'mode' => 'error', + 'error' => 'No request specified' + ); + } + } + catch ( Exception $e ) + { + // Sent a request but it's not valid JSON + $return = array( + 'mode' => 'error', + 'error' => 'Invalid request - JSON parsing failed' + ); + } + + echo enano_json_encode($return); + + return true; + } + + // + // Not a JSON request, output normal HTML interface + // + + // Scan all plugins + $plugin_list = array(); + + if ( $dirh = @opendir( ENANO_ROOT . '/plugins' ) ) + { + while ( $dh = @readdir($dirh) ) + { + if ( !preg_match('/\.php$/i', $dh) ) + continue; + $fullpath = ENANO_ROOT . "/plugins/$dh"; + // it's a PHP file, attempt to read metadata + // pass 1: try to read a !info block + $blockdata = $plugins->parse_plugin_blocks($fullpath, 'info'); + if ( empty($blockdata) ) + { + // no !info block, check for old header + $fh = @fopen($fullpath, 'r'); + if ( !$fh ) + // can't read, bail out + continue; + $plugin_data = array(); + for ( $i = 0; $i < 8; $i++ ) + { + $plugin_data[] = @fgets($fh, 8096); + } + // close our file handle + fclose($fh); + // is the header correct? + if ( trim($plugin_data[0]) != ' $value ) + { + $plugin_meta[ strtolower($key) ] = $value; + } + } + if ( !isset($plugin_meta) || !is_array(@$plugin_meta) ) + { + // parsing didn't work. + continue; + } + // check for required keys + $required_keys = array('plugin name', 'plugin uri', 'description', 'author', 'version', 'author uri'); + foreach ( $required_keys as $key ) + { + if ( !isset($plugin_meta[$key]) ) + // not set, skip this plugin + continue 2; + } + // decide if it's a system plugin + $plugin_meta['system plugin'] = in_array($dh, $plugins->system_plugins); + // all checks passed + $plugin_list[$dh] = $plugin_meta; + } + } + echo '
' . print_r($plugin_list, true) . '
'; +}