# HG changeset patch # User Dan # Date 1231727922 18000 # Node ID db23957ad1c7685831626803f0c1010da4c2ea4b First commit; requires latest Enano from Hg. It works! diff -r 000000000000 -r db23957ad1c7 plugins/AjIM2.0.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/AjIM2.0.php Sun Jan 11 21:38:42 2009 -0500 @@ -0,0 +1,54 @@ +attachHook('compile_template', 'ajim_compile_sidebar();'); + +function ajim_compile_sidebar() +{ + global $db, $session, $paths, $template, $plugins; // Common objects + global $lang; + + $template->add_header(''); + $template->add_header(''); + $can_mod = $session->get_permissions('ajim_mod') ? 'true' : 'false'; + $template->add_header(''); + + $msg_loading = $lang->get('ajim_msg_loading'); + $html = '
'; + $html .= <<<__EOF +
+
+
+ + $msg_loading + +
+
+__EOF; + if ( $session->get_permissions('ajim_post') ) + { + if ( $session->user_logged_in ) + { + $html .= ''; + } + else + { + $l_name = $lang->get('ajim_lbl_name'); + $l_site = $lang->get('ajim_lbl_website'); + $html .= <<<______EOF + + + + + +
+ $l_name + + +
+______EOF; + } + $b_submit = $lang->get('ajim_btn_submit'); + $html .= ''; + $html .= <<<____EOF +
+ +
+____EOF; + if ( $session->get_permissions('ajim_mod') ) + { + $html .= '
'; + if ( $session->auth_level < USER_LEVEL_CHPREF ) + { + $html .= '' . $lang->get('ajim_btn_mod') . ''; + } + $html .= '
'; + } + } + else + { + $msg_nopost = $lang->get('ajim_msg_no_post'); + $html .= <<<____EOF + $msg_nopost +____EOF; + } + + $html .= '
+
'; + $template->sidebar_widget('AjIM Shoutbox', $html); +} diff -r 000000000000 -r db23957ad1c7 plugins/ajim/enanosetup.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/ajim/enanosetup.php Sun Jan 11 21:38:42 2009 -0500 @@ -0,0 +1,10 @@ +attachHook('acl_rule_init', 'ajim_permissions_setup($session, $this);'); + +function ajim_permissions_setup(&$session, &$paths) +{ + $session->register_acl_type('ajim_post', AUTH_ALLOW, 'ajim_perm_post', Array(), 'All'); + $session->register_acl_type('ajim_edit', AUTH_ALLOW, 'ajim_perm_edit', Array(), 'All'); + $session->register_acl_type('ajim_mod', AUTH_DISALLOW, 'ajim_perm_mod', Array('ajim_post'), 'All'); +} diff -r 000000000000 -r db23957ad1c7 plugins/ajim/language.json --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/ajim/language.json Sun Jan 11 21:38:42 2009 -0500 @@ -0,0 +1,33 @@ +{ + // english + eng: { + categories: [ 'meta', 'ajim' ], + strings: { + meta: { + ajim: 'AjIM plugin' + }, + ajim: { + msg_loading: 'Loading...', + lbl_name: 'Name:', + lbl_website: 'URL:', + btn_submit: 'Post', + msg_no_post: 'Posting is disabled.', + btn_edit: 'edit', + btn_delete: 'delete', + btn_save: 'save', + msg_no_posts: 'No posts.', + btn_mod: 'Moderation', + + err_generic_title: 'Shoutbox error', + err_bad_json: 'Invalid JSON response from server:', + err_post_denied: 'Access denied. Your post was not submitted.', + err_access_denied: 'You\'re not allowed to do that.', + err_no_post: 'Please enter a post.', + + perm_post: 'Post AjIM messages', + perm_edit: 'Edit own AjIM messages', + perm_mod: 'Moderate AjIM messages' + } + } + } +} diff -r 000000000000 -r db23957ad1c7 plugins/ajim/server.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/ajim/server.php Sun Jan 11 21:38:42 2009 -0500 @@ -0,0 +1,264 @@ +attachHook('session_started', 'ajim_page_init();'); + +function ajim_page_init() +{ + global $db, $session, $paths, $template, $plugins; // Common objects + + $paths->add_page(array( + 'name' => 'AjIM JSON handler', + 'urlname' => 'AjimJson', + 'namespace' => 'Special', + 'visible' => 0, + 'special' => 1, + 'comments_on' => 0, + 'protected' => 0 + )); +} + +function page_Special_AjimJson() +{ + global $db, $session, $paths, $template, $plugins; // Common objects + global $lang; + + header('Content-type: text/javascript'); + if ( !isset($_GET['r']) && !isset($_POST['r']) ) + { + return print enano_json_encode(array( + 'mode' => 'error', + 'error' => 'No request specified.' + )); + } + $request = enano_json_decode($_REQUEST['r']); + if ( !isset($request['mode']) ) + { + return print enano_json_encode(array( + 'mode' => 'error', + 'error' => 'No mode specified.' + )); + } + switch($request['mode']) + { + case 'watch': + @set_time_limit(0); + $time = ( !empty($request['lastrefresh']) ) ? intval($request['lastrefresh']) : 0; + $end = microtime_float() + 59; + // run cron-ish stuff + if ( intval(getConfig('ajim_last_cleanout', 0)) + 86400 < time() ) + { + $q = $db->sql_query('SELECT COUNT(message_id) FROM ' . table_prefix . "ajim2;"); + if ( !$q ) + $db->die_json(); + + list($count) = $db->fetchrow_num(); + $db->free_result(); + if ( intval($count) > 50 ) + { + // if there are more than 50 messages in the database, clean it out + $limit = $count - 15; + $q = $db->sql_query('DELETE FROM ' . table_prefix . "ajim2 ORDER BY message_time ASC LIMIT $limit;"); + if ( !$q ) + $db->die_json(); + } + + setConfig('ajim_last_cleanout', time()); + } + + while ( microtime_float() < $end ) + { + $q = $db->sql_query('SELECT * FROM ' . table_prefix . "ajim2 WHERE message_time >= $time OR message_update_time >= $time ORDER BY message_time DESC LIMIT 30;"); + if ( !$q ) + $db->die_json(); + if ( $db->numrows() > 0 || $time == 0 ) + break; + $db->free_result(); + usleep(500000); // 0.5s + } + if ( $q ) + { + $messages = array(); + while ( $row = $db->fetchrow() ) + { + $row['rank_info'] = $session->get_user_rank($row['user_id']); + $row['message_html'] = RenderMan::render($row['message']); + $row['human_time'] = enano_date('n/j, g:ia', $row['message_time']); + $messages[] = $row; + } + $response = array( + 'mode' => 'messages', + 'now' => time(), + 'messages' => $messages + ); + return print enano_json_encode($response); + } + else + { + return print enano_json_encode(array( + 'mode' => 'messages', + 'now' => time(), + 'messages' => array() + )); + } + break; + case 'submit': + if ( !$session->get_permissions('ajim_post') ) + { + return print enano_json_encode(array( + 'mode' => 'error', + 'error' => $lang->get('ajim_err_post_denied') + )); + } + $name = $session->user_logged_in ? $session->username : $request['user']; + $content = trim($request['message']); + if ( empty($content) ) + { + return print enano_json_encode(array( + 'mode' => 'error', + 'error' => $lang->get('ajim_err_no_post') + )); + } + + $now = time(); + $content_db = $db->escape($content); + $name_db = $db->escape($name); + + $sql = 'INSERT INTO ' . table_prefix . "ajim2(user_id, username, message, message_time, message_update_time) VALUES\n" + . " ({$session->user_id}, '$name_db', '$content_db', $now, $now);"; + if ( !$db->sql_query($sql) ) + $db->die_json(); + + // workaround for no insert_id() on postgresql + $q = $db->sql_query('SELECT message_id FROM ' . table_prefix . "ajim2 WHERE username = '$name_db' AND message = '$content_db' AND message_time = $now ORDER BY message_id DESC LIMIT 1;"); + if ( !$q ) + $db->die_json(); + + list($message_id) = $db->fetchrow_num(); + $db->free_result(); + + return print enano_json_encode(array( + 'mode' => 'messages', + 'messages' => array(array( + 'rank_info' => $session->get_user_rank($session->user_id), + 'human_time' => enano_date('n/j, g:ia'), + 'message' => $content, + 'username' => $name, + 'user_id' => $session->user_id, + 'message_time' => time(), + 'message_update_time' => time(), + 'message_id' => $message_id, + 'message_html' => RenderMan::render($content) + )) + )); + break; + case 'delete': + if ( empty($request['message_id']) ) + { + return print enano_json_encode(array( + 'mode' => 'error', + 'error' => 'No message_id specified.' + )); + } + + $message_id = intval($request['message_id']); + + if ( ( !$session->get_permissions('ajim_mod') || $session->auth_level < USER_LEVEL_CHPREF ) ) + { + // we don't have permission according to ACLs, but try to see if we can edit our + // own posts. if so, we can allow this to continue. + $perm_override = false; + if ( $session->get_permissions('ajim_edit') && $session->user_logged_in ) + { + $q = $db->sql_query('SELECT user_id FROM ' . table_prefix . "ajim2 WHERE message_id = $message_id;"); + if ( !$q ) + $db->die_json(); + + list($user_id) = $db->fetchrow_num(); + $db->free_result(); + if ( $user_id === $session->user_id ) + { + $perm_override = true; + } + } + if ( !$perm_override ) + { + return print enano_json_encode(array( + 'mode' => 'error', + 'error' => $lang->get('ajim_err_access_denied') + )); + } + } + + $now = time(); + $q = $db->sql_query('UPDATE ' . table_prefix . "ajim2 SET message = '', message_update_time = $now WHERE message_id = $message_id;"); + if ( !$q ) + $db->die_json(); + + return print enano_json_encode(array( + 'mode' => 'delete', + 'message_id' => $message_id + )); + break; + case 'update': + if ( empty($request['message_id']) ) + { + return print enano_json_encode(array( + 'mode' => 'error', + 'error' => 'No message_id specified.' + )); + } + + $message_id = intval($request['message_id']); + + if ( ( !$session->get_permissions('ajim_mod') || $session->auth_level < USER_LEVEL_CHPREF ) ) + { + // we don't have permission according to ACLs, but try to see if we can edit our + // own posts. if so, we can allow this to continue. + $perm_override = false; + if ( $session->get_permissions('ajim_edit') && $session->user_logged_in ) + { + $q = $db->sql_query('SELECT user_id FROM ' . table_prefix . "ajim2 WHERE message_id = $message_id;"); + if ( !$q ) + $db->die_json(); + + list($user_id) = $db->fetchrow_num(); + $db->free_result(); + if ( $user_id === $session->user_id ) + { + $perm_override = true; + } + } + if ( !$perm_override ) + { + return print enano_json_encode(array( + 'mode' => 'error', + 'error' => $lang->get('ajim_err_access_denied') + )); + } + } + + $message = trim(@$request['message']); + if ( empty($message) ) + { + return print enano_json_encode(array( + 'mode' => 'error', + 'error' => $lang->get('ajim_err_no_post') + )); + } + + $message_db = $db->escape($message); + $now = time(); + $q = $db->sql_query('UPDATE ' . table_prefix . "ajim2 SET message = '{$message_db}', message_update_time = $now WHERE message_id = $message_id;"); + if ( !$q ) + $db->die_json(); + + return print enano_json_encode(array( + 'mode' => 'update', + 'message_id' => $message_id, + 'message' => $message, + 'message_html' => RenderMan::render($message) + )); + break; + } +} + diff -r 000000000000 -r db23957ad1c7 plugins/ajim/shoutbox.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/ajim/shoutbox.css Sun Jan 11 21:38:42 2009 -0500 @@ -0,0 +1,88 @@ +div.ajim_wrapper { + padding: 2px; + color: #000; +} + +div.ajim_wrapper > div { + border-color: #000; + border-style: solid; + font-family: arial, helvetica, sans-serif; + font-size: 75%; + padding: 3px; +} + +div.ajim_messages { + border-width: 1px 1px 0 1px; + background-color: #fff; + height: 200px; + clip: rect(0px, auto, auto, 0px); + overflow: auto; +} + +div.ajim_form { + border-width: 0 1px 1px 1px; +} + +div.ajim_form input.ajim_field { + font-size: 100%; + width: 80%; +} + +div.ajim_form textarea { + width: 93%; + font-size: 100%; +} + +div.ajim_submit_wrap { + padding-top: 2px; + text-align: center; +} + +div.ajim_submit_wrap input { + font-size: 100%; +} + +span.ajim_noposts { + color: #a0a0a0; +} + +div#ajim_error { + background-color: #ffd0d0; + color: #500000; +} + +div.ajim_modbuttons { + float: right; +} + +div.ajim_modbuttons a.ajim_btn_edit { + color: #30aa00; +} + +div.ajim_modbuttons a.ajim_btn_delete { + color: #aa3000; +} + +div.ajim_messagehead { + clear: both; + border-bottom: 1px solid #c8c8c8; + margin: 3px 0; +} + +div.ajim_timestamp { + color: #aaaaaa; + font-style: italic; +} + +div.ajim_message_inner p { + margin: 2px 0; +} + +div#ajim_mod { + text-align: center; +} + +a.ajim_modlink { + color: blue; + font-size: 87.5%; +} diff -r 000000000000 -r db23957ad1c7 plugins/ajim/shoutbox.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/ajim/shoutbox.js Sun Jan 11 21:38:42 2009 -0500 @@ -0,0 +1,402 @@ +var ajim_last_refresh = 0; + +window.ajim_init = function() +{ + window.ajim_authed = ( auth_level >= USER_LEVEL_CHPREF && ajim_can_mod ); + // have it immediately pull in whatever comments the server wants us to pull + ajim_watch(true); + ajim_setup_textarea(); +} + +addOnloadHook(ajim_init); + +window.ajim_watch = function(immediate) +{ + ajim_send_request({ + mode: 'watch', + lastrefresh: immediate ? 0 : ajim_last_refresh + }); +} + +window.ajim_send_request = function(request) +{ + request = ajaxEscape(toJSONString(request)); + ajaxPost(makeUrlNS('Special', 'AjimJson'), 'r=' + request, function() + { + if ( ajax.readyState == 4 && ajax.status == 200 ) + { + var response = String(ajax.responseText + ''); + if ( !check_json_response(response) ) + { + ajim_handle_invalid_json(response); + return false; + } + response = parseJSON(response); + ajim_handle_response(response); + } + }); +} + +window.ajim_handle_invalid_json = function(response) +{ + ajim_fail_message_list(); + load_component('l10n'); + var messages = document.getElementById('ajim_messages'); + messages.innerHTML = $lang.get('ajim_err_bad_json') + '
'; + messages.appendChild(document.createTextNode(response)); +} + +window.ajim_handle_response = function(response) +{ + switch(response.mode) + { + case 'messages': + ajim_show_message_list(); + + var noposts = document.getElementById('ajim_noposts'); + if ( noposts ) + noposts.parentNode.removeChild(noposts); + for ( var i = response.messages.length - 1; i >= 0; i-- ) + { + ajim_inject_message(response.messages[i]); + } + if ( response.now ) + { + window.ajim_last_refresh = response.now; + ajim_watch(); + } + break; + case 'delete': + var message = document.getElementById('ajim_message:' + response.message_id); + if ( message ) + { + message.parentNode.removeChild(message); + } + ajim_show_message_list(); + break; + case 'update': + var message = document.getElementById('ajim_message:' + response.message_id); + if ( message ) + { + var inner = getElementsByClassName(message, 'div', 'ajim_message_inner')[0]; + inner.innerHTML = response.message_html; + message.meta.message_src = response.message; + } + ajim_show_message_list(); + break; + case 'error': + ajim_fail_message_list(); + ajim_show_error(response.error); + break; + } +} + +window.ajim_draw_message = function(message) +{ + var div = document.createElement('div'); + div.id = 'ajim_message:' + message.message_id; + div.className = 'ajim_message'; + div.meta = { + message_id: message.message_id, + message_src: message.message + }; + + // deleted message? + if ( message.message == '' ) + return div; + + // *sigh* actually draw a message. + + // mod buttons + var modbuttons = document.createElement('div'); + modbuttons.className = 'ajim_modbuttons'; + if ( !window.ajim_authed && ( ( message.user_id != 1 && message.user_id != ajim_user_id ) || message.user_id == 1 ) ) + modbuttons.style.display = 'none'; + + var edbtn = document.createElement('a'); + edbtn.href = '#'; + edbtn.className = 'ajim_btn_edit'; + edbtn.appendChild(document.createTextNode(ajim_str_edit)); + edbtn.onclick = ajim_handle_click_edit; + modbuttons.appendChild(edbtn); + + modbuttons.appendChild(document.createTextNode(' | ')); + + var delbtn = document.createElement('a'); + delbtn.href = '#'; + delbtn.className = 'ajim_btn_delete'; + delbtn.appendChild(document.createTextNode(ajim_str_delete)); + delbtn.onclick = ajim_handle_click_delete; + modbuttons.appendChild(delbtn); + + div.appendChild(modbuttons); + + // username + var el = message.user_id == 1 ? 'span' : 'a'; + var username = document.createElement(el); + username.appendChild(document.createTextNode(message.username)); + username.title = message.rank_info.rank_title; + username.setAttribute('style', message.rank_info.rank_style); + if ( message.user_id != 1 ) + username.href = makeUrlNS('User', message.username); + div.appendChild(username); + + // message header (does a clear: both) + var messagehead = document.createElement('div'); + messagehead.className = 'ajim_messagehead'; + div.appendChild(messagehead); + + // date + var date = document.createElement('div'); + date.className = 'ajim_timestamp'; + date.appendChild(document.createTextNode(message.human_time)); + div.appendChild(date); + + // *finally* message + var msgdata = document.createElement('div'); + msgdata.innerHTML = message.message_html; + msgdata.className = 'ajim_message_inner'; + div.appendChild(msgdata); + + // message footer + var messagefoot = document.createElement('div'); + messagefoot.className = 'ajim_messagefoot'; + div.appendChild(messagefoot); + + return div; +} + +window.ajim_inject_message = function(message) +{ + var drawn = ajim_draw_message(message); + var existing = document.getElementById('ajim_message:' + message.message_id); + if ( existing ) + { + insertAfter(existing.parentNode, drawn, existing); + existing.parentNode.removeChild(existing); + } + else + { + ajim_inject_object(drawn); + } +} + +window.ajim_inject_object = function(element) +{ + var error = document.getElementById('ajim_error'); + insertAfter(error.parentNode, element, error); +} + +window.ajim_show_error = function(message) +{ + if ( window.ajim_error_timeout ) + window.clearTimeout(ajim_error_timeout); + var error = document.getElementById('ajim_error'); + error.innerHTML = ''; + error.appendChild(document.createTextNode(message)); + window.ajim_error_timeout = window.setTimeout(ajim_clear_error, 5000); +} + +window.ajim_clear_error = function() +{ + var error = document.getElementById('ajim_error'); + error.innerHTML = ''; +} + +window.ajim_handle_click_edit = function() +{ + if ( this.editing ) + { + this.editing = false; + + this.firstChild.nodeValue = $lang.get('ajim_btn_edit'); + this.nextSibling.nextSibling.firstChild.nodeValue = $lang.get('ajim_btn_delete'); + + var newsrc = this.ta.value; + this.ta.parentNode.removeChild(this.ta); + this.inner.style.display = 'block'; + + if ( newsrc == '' ) + ajim_show_error($lang.get('ajim_err_no_post')); + + ajim_hide_message_list(); + + ajim_send_request({ + mode: 'update', + message_id: this.parentNode.parentNode.meta.message_id, + message: newsrc + }); + } + else + { + load_component('l10n'); + this.firstChild.nodeValue = $lang.get('ajim_btn_save'); + this.nextSibling.nextSibling.firstChild.nodeValue = $lang.get('etc_cancel').toLowerCase(); + this.editing = true; + + var src = this.parentNode.parentNode.meta.message_src; + var inner = this.parentNode.nextSibling.nextSibling.nextSibling.nextSibling; + + var ta = document.createElement('textarea'); + ta.value = src; + ta.style.fontSize = '100%'; + ta.style.width = '93%'; + inner.style.display = 'none'; + this.parentNode.parentNode.appendChild(ta); + this.ta = ta; + this.inner = inner; + } + return false; +} + +window.ajim_handle_click_delete = function() +{ + var editlink = this.previousSibling.previousSibling; + if ( editlink.editing ) + { + editlink.editing = false; + + editlink.firstChild.nodeValue = $lang.get('ajim_btn_edit'); + this.firstChild.nodeValue = $lang.get('ajim_btn_delete'); + + editlink.ta.parentNode.removeChild(editlink.ta); + editlink.inner.style.display = 'block'; + } + else + { + ajim_hide_message_list(); + var message_id = this.parentNode.parentNode.meta.message_id; + ajim_send_request({ + mode: 'delete', + message_id: message_id + }); + } + return false; +} + +window.ajim_handle_click_mod = function() +{ + if ( auth_level >= USER_LEVEL_CHPREF ) + return false; + load_component('login'); + + ajaxLogonInit(function(k) + { + ajaxLoginReplaceSIDInline(k, false, USER_LEVEL_CHPREF); + window.setTimeout(function() + { + mb_current_obj.destroy(); + ajim_enable_mod_tools(); + }, 500); + }, USER_LEVEL_CHPREF); + + return false; +} + +window.ajim_enable_mod_tools = function() +{ + var mods = document.getElementsByClassName('div', 'ajim_modbuttons'); + for ( var i = 0; i < mods.length; i++ ) + { + mods[i].style.display = 'block'; + } + document.getElementById('ajim_mod').innerHTML = ''; +} + +window.ajim_setup_textarea = function() +{ + var ta = document.getElementById('ajim_message'); + if ( !ta ) + return false; + + ta.shift = false; + ta.onkeypress = function(e) + { + if ( !e ) + e = window.event; + if ( !e ) + return true; + if ( typeof(e.keyCode) == undefined ) + return true; + if ( e.keyCode == 13 ) + { + if ( !this.shift ) + { + ajim_submit_message(); + e.preventDefault(); + return false; + } + } + } + ta.onkeydown = function(e) + { + if ( !e ) + e = window.event; + if ( !e ) + return true; + if ( !e.keyCode ) + return true; + if ( e.keyCode == 16 ) + { + this.shift = true; + } + } + ta.onkeyup = function(e) + { + if ( !e ) + e = window.event; + if ( !e ) + return true; + if ( !e.keyCode ) + return true; + if ( e.keyCode == 16 ) + { + this.shift = false; + } + } +} + +window.ajim_submit_message = function() +{ + var ta = document.getElementById('ajim_message'); + if ( ta.value == '' ) + { + load_component('l10n'); + ajim_show_error($lang.get('ajim_err_no_post')); + return false; + } + + var user = document.getElementById('ajim_nickname'); + if ( user.value == '' ) + user.value = 'Guest'; + + ajim_hide_message_list(); + + ajim_send_request({ + mode: 'submit', + user: user.value, + message: ta.value + }); + + ta.value = ''; +} + +window.ajim_hide_message_list = function() +{ + var messages = document.getElementById('ajim_messages'); + window.ajim_submit_whitey = whiteOutElement(messages); +} + +window.ajim_show_message_list = function() +{ + if ( window.ajim_submit_whitey ) + whiteOutReportSuccess(window.ajim_submit_whitey); + window.ajim_submit_whitey = false; +} + +window.ajim_fail_message_list = function() +{ + if ( window.ajim_submit_whitey ) + whiteOutReportFailure(window.ajim_submit_whitey); + window.ajim_submit_whitey = false; +}