From: Robin Millette Date: Sun, 31 May 2009 21:12:04 +0000 (-0400) Subject: Merge branch '0.8.x' of git@gitorious.org:+laconica-developers/laconica/dev into... X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=ebeb5f744cbfd5bfea0da1b350a3757865ec4b3b;hp=3e0c291810d23bb54c337c0fe95c1ba441459c4c;p=quix0rs-gnu-social.git Merge branch '0.8.x' of git@gitorious.org:+laconica-developers/laconica/dev into upload Conflicts: js/util.js lib/attachmentlist.php --- diff --git a/README b/README index 9207f3e900..76b56a52e5 100644 --- a/README +++ b/README @@ -694,6 +694,13 @@ to users on a remote site. (Or not... it's not well tested.) The Upgrading ========= +IMPORTANT NOTE: Laconica 0.7.4 introduced a fix for some +incorrectly-stored international characters ("UTF-8"). For new +installations, it will now store non-ASCII characters correctly. +However, older installations will have the incorrect storage, and will +consequently show up "wrong" in browsers. See below for how to deal +with this situation. + If you've been using Laconica 0.6, 0.5 or lower, or if you've been tracking the "git" version of the software, you will probably want to upgrade and keep your existing data. There is no automated upgrade @@ -783,6 +790,29 @@ problem. 3. When fixup_inboxes is finished, you can set the enabled flag to 'true'. +UTF-8 Database +-------------- + +Laconica 0.7.4 introduced a fix for some incorrectly-stored +international characters ("UTF-8"). This fix is not +backwards-compatible; installations from before 0.7.4 will show +non-ASCII characters of old notices incorrectly. This section explains +what to do. + +0. You can disable the new behaviour by setting the 'db''utf8' config + option to "false". You should only do this until you're ready to + convert your DB to the new format. +1. When you're ready to convert, you can run the fixup_utf8.php script + in the scripts/ subdirectory. If you've had the "new behaviour" + enabled (probably a good idea), you can give the ID of the first + "new" notice as a parameter, and only notices before that one will + be converted. Notices are converted in reverse chronological order, + so the most recent (and visible) ones will be converted first. The + script should work whether or not you have the 'db''utf8' config + option enabled. +2. When you're ready, set $config['db']['utf8'] to true, so that + new notices will be stored correctly. + Configuration options ===================== @@ -910,6 +940,10 @@ mirror: you can set this to an array of DSNs, like the above and adding the slaves to this array. Note that if you want some requests to go to the 'database' (master) server, you'll need to include it in this array, too. +utf8: whether to talk to the database in UTF-8 mode. This is the default + with new installations, but older sites may want to turn it off + until they get their databases fixed up. See "UTF-8 database" + above for details. syslog ------ @@ -1162,6 +1196,21 @@ reporturl: URL to post statistics to. Defaults to Laconica developers' set 'run' to 'never' than to set this value to something nonsensical. + +attachments +----------- + +The software lets users upload files with their notices. You can configure +the types of accepted files by mime types and a trio of quota options: +per file, per user (total), per user per month. + +supported: an array of mime types you accept to store and distribute, + like 'image/gif', 'video/mpeg', 'audio/mpeg', etc. +file_quota: maximum size for a single file upload in bytes. +user_quota: total size in bytes a user can store. +monthly_quota: total size permitted in the current month. + + Troubleshooting =============== diff --git a/actions/api.php b/actions/api.php index 8762b4bcd3..b25ba99f30 100644 --- a/actions/api.php +++ b/actions/api.php @@ -67,6 +67,7 @@ class ApiAction extends Action $this->process_command(); } else { # basic authentication failed + common_log(LOG_WARNING, "Failed API auth attempt, nickname: $nickname."); $this->show_basic_auth_error(); } } diff --git a/actions/conversation.php b/actions/conversation.php index ef189016aa..0d7cb9a874 100644 --- a/actions/conversation.php +++ b/actions/conversation.php @@ -179,14 +179,14 @@ class ConversationTree extends NoticeList $this->out->elementStart('div', array('id' =>'notices_primary')); $this->out->element('h2', null, _('Notices')); - $this->out->elementStart('ul', array('class' => 'notices')); + $this->out->elementStart('ol', array('class' => 'notices xoxo')); if (array_key_exists('root', $this->tree)) { $rootid = $this->tree['root'][0]; $this->showNoticePlus($rootid); } - $this->out->elementEnd('ul'); + $this->out->elementEnd('ol'); $this->out->elementEnd('div'); return $cnt; @@ -215,13 +215,13 @@ class ConversationTree extends NoticeList if (array_key_exists($id, $this->tree)) { $children = $this->tree[$id]; - $this->out->elementStart('ul', array('class' => 'notices')); + $this->out->elementStart('ol', array('class' => 'notices')); foreach ($children as $child) { $this->showNoticePlus($child); } - $this->out->elementEnd('ul'); + $this->out->elementEnd('ol'); } $this->out->elementEnd('li'); @@ -295,4 +295,4 @@ class ConversationTreeItem extends NoticeListItem { return; } -} \ No newline at end of file +} diff --git a/actions/designsettings.php b/actions/designsettings.php index 315e5a199c..5774b85378 100644 --- a/actions/designsettings.php +++ b/actions/designsettings.php @@ -70,7 +70,7 @@ class DesignsettingsAction extends AccountSettingsAction function showContent() { $user = common_current_user(); - $this->elementStart('form', array('method' => 'POST', + $this->elementStart('form', array('method' => 'post', 'id' => 'form_settings_design', 'class' => 'form_settings', 'action' => @@ -82,7 +82,7 @@ class DesignsettingsAction extends AccountSettingsAction $this->element('legend', null, _('Change background image')); $this->elementStart('ul', 'form_data'); $this->elementStart('li'); - $this->element('label', array('for' => 'design_ background-image_file'), + $this->element('label', array('for' => 'design_background-image_file'), _('Upload file')); $this->element('input', array('name' => 'design_background-image_file', 'type' => 'file', diff --git a/actions/shownotice.php b/actions/shownotice.php index 2c469c9dee..34c8a8e946 100644 --- a/actions/shownotice.php +++ b/actions/shownotice.php @@ -208,10 +208,10 @@ class ShownoticeAction extends Action function showContent() { - $this->elementStart('ul', array('class' => 'notices')); + $this->elementStart('ol', array('class' => 'notices xoxo')); $nli = new NoticeListItem($this->notice, $this); $nli->show(); - $this->elementEnd('ul'); + $this->elementEnd('ol'); } /** diff --git a/classes/Foreign_link.php b/classes/Foreign_link.php index 6065609512..c0b356eced 100644 --- a/classes/Foreign_link.php +++ b/classes/Foreign_link.php @@ -11,7 +11,7 @@ class Foreign_link extends Memcached_DataObject public $__table = 'foreign_link'; // table name public $user_id; // int(4) primary_key not_null - public $foreign_id; // int(4) primary_key not_null + public $foreign_id; // bigint(8) primary_key not_null unsigned public $service; // int(4) primary_key not_null public $credentials; // varchar(255) public $noticesync; // tinyint(1) not_null default_1 diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php index 5f71f716b3..52ad4100fc 100644 --- a/classes/Memcached_DataObject.php +++ b/classes/Memcached_DataObject.php @@ -227,4 +227,22 @@ class Memcached_DataObject extends DB_DataObject $c->set($ckey, $cached, MEMCACHE_COMPRESSED, $expiry); return new ArrayWrapper($cached); } + + // We overload so that 'SET NAMES "utf8"' is called for + // each connection + + function _connect() + { + global $_DB_DATAOBJECT; + $exists = !empty($this->_database_dsn_md5) && + isset($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]); + $result = parent::_connect(); + if (!$exists) { + $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]; + if (common_config('db', 'utf8')) { + $DB->query('SET NAMES "utf8"'); + } + } + return $result; + } } diff --git a/db/laconica.sql b/db/laconica.sql index 0b20bc172c..a11e316925 100644 --- a/db/laconica.sql +++ b/db/laconica.sql @@ -285,7 +285,7 @@ create table foreign_user ( create table foreign_link ( user_id int comment 'link to user on this system, if exists' references user (id), - foreign_id int comment 'link ' references foreign_user(id), + foreign_id bigint unsigned comment 'link to user on foreign service, if exists' references foreign_user(id), service int not null comment 'foreign key to service' references foreign_service(id), credentials varchar(255) comment 'authc credentials, typically a password', noticesync tinyint not null default 1 comment 'notice synchronization, bit 1 = sync outgoing, bit 2 = sync incoming, bit 3 = filter local replies', diff --git a/db/notice_source.sql b/db/notice_source.sql index 1508af1ec8..d5a280b82c 100644 --- a/db/notice_source.sql +++ b/db/notice_source.sql @@ -2,6 +2,7 @@ INSERT INTO notice_source (code, name, url, created) VALUES ('adium', 'Adium', 'http://www.adiumx.com/', now()), + ('AgentSolo.com','AgentSolo.com','http://www.agentsolo.com/', now()), ('betwittered','BeTwittered','http://www.32hours.com/betwitteredinfo/', now()), ('bti','bti','http://gregkh.github.com/bti/', now()), ('cliqset', 'Cliqset', 'http://www.cliqset.com/', now()), @@ -29,6 +30,7 @@ VALUES ('pingvine','PingVine','http://pingvine.com/', now()), ('pocketwit','PockeTwit','http://code.google.com/p/pocketwit/', now()), ('posty','Posty','http://spreadingfunkyness.com/posty/', now()), + ('qtwitter','qTwitter','http://qtwitter.ayoy.net/', now()), ('royalewithcheese','Royale With Cheese','http://p.hellyeah.org/', now()), ('rssdent','rssdent','http://github.com/zcopley/rssdent/tree/master', now()), ('rygh.no','rygh.no','http://rygh.no/', now()), diff --git a/js/util.js b/js/util.js index 85ab48b4c0..acf44a17c7 100644 --- a/js/util.js +++ b/js/util.js @@ -250,6 +250,7 @@ $(document).ready(function(){ $("#form_notice").each(addAjaxHidden); NoticeHover(); NoticeReply(); + NoticeAttachments(); }); @@ -288,3 +289,53 @@ function NoticeReplySet(nick,id) { } return true; } + +function NoticeAttachments() { + $.fn.jOverlay.options = { + method : 'GET', + data : '', + url : '', + color : '#000', + opacity : '0.6', + zIndex : 99, + center : true, + imgLoading : $('address .url')[0].href+'theme/base/images/illustrations/illu_progress_loading-01.gif', + bgClickToClose : true, + success : function() { + $('#jOverlayContent').append(''); + $('#jOverlayContent button').click($.closeOverlay); + }, + timeout : 0 + }; + + $('a.attachment').click(function() { + $().jOverlay({url: $('address .url')[0].href+'/attachment/' + ($(this).attr('id').substring('attachment'.length + 1)) + '/ajax'}); + return false; + }); + + var t; + $("body:not(#shownotice) a.thumbnail").hover( + function() { + var anchor = $(this); + $("a.thumbnail").children('img').hide(); + anchor.closest(".entry-title").addClass('ov'); + + if (anchor.children('img').length == 0) { + t = setTimeout(function() { + $.get($('address .url')[0].href+'/attachment/' + (anchor.attr('id').substring('attachment'.length + 1)) + '/thumbnail', null, function(data) { + anchor.append(data); + }); + }, 500); + } + else { + anchor.children('img').show(); + } + }, + function() { + clearTimeout(t); + $("a.thumbnail").children('img').hide(); + $(this).closest(".entry-title").removeClass('ov'); + } + ); +} + diff --git a/lib/attachmentlist.php b/lib/attachmentlist.php index a58a50f6fe..61749dca57 100644 --- a/lib/attachmentlist.php +++ b/lib/attachmentlist.php @@ -83,11 +83,10 @@ class AttachmentList extends Widget $atts = new File; $att = $atts->getAttachments($this->notice->id); if (empty($att)) return 0; - - $this->out->elementStart('dl', array('id' =>'attachment')); + $this->out->elementStart('dl', array('id' =>'attachments')); $this->out->element('dt', null, _('Attachments')); $this->out->elementStart('dd'); - $this->out->elementStart('ul', array('class' => 'attachments')); + $this->out->elementStart('ol', array('class' => 'attachments')); foreach ($att as $n=>$attachment) { $item = $this->newListItem($attachment); @@ -95,7 +94,7 @@ class AttachmentList extends Widget } $this->out->elementEnd('dd'); - $this->out->elementEnd('ul'); + $this->out->elementEnd('ol'); $this->out->elementEnd('dl'); return count($att); diff --git a/lib/common.php b/lib/common.php index ede8d6277f..838f52f9d5 100644 --- a/lib/common.php +++ b/lib/common.php @@ -208,6 +208,7 @@ $config['db'] = 'require_prefix' => 'classes/', 'class_prefix' => '', 'mirror' => null, + 'utf8' => true, 'db_driver' => 'DB', # XXX: JanRain libs only work with DB 'quote_identifiers' => false, 'type' => 'mysql' ); diff --git a/lib/noticelist.php b/lib/noticelist.php index 420272515e..5513e317e0 100644 --- a/lib/noticelist.php +++ b/lib/noticelist.php @@ -86,7 +86,7 @@ class NoticeList extends Widget { $this->out->elementStart('div', array('id' =>'notices_primary')); $this->out->element('h2', null, _('Notices')); - $this->out->elementStart('ul', array('class' => 'notices')); + $this->out->elementStart('ol', array('class' => 'notices xoxo')); $cnt = 0; @@ -101,7 +101,7 @@ class NoticeList extends Widget $item->show(); } - $this->out->elementEnd('ul'); + $this->out->elementEnd('ol'); $this->out->elementEnd('div'); return $cnt; diff --git a/lib/noticesection.php b/lib/noticesection.php index 37aafdaf6c..ca14326861 100644 --- a/lib/noticesection.php +++ b/lib/noticesection.php @@ -52,12 +52,12 @@ class NoticeSection extends Section { $notices = $this->getNotices(); $cnt = 0; - $this->out->elementStart('ul', 'notices'); + $this->out->elementStart('ol', 'notices xoxo'); while ($notices->fetch() && ++$cnt <= NOTICES_PER_SECTION) { $this->showNotice($notices); } - $this->out->elementEnd('ul'); + $this->out->elementEnd('ol'); return ($cnt > NOTICES_PER_SECTION); } diff --git a/plugins/FBConnect/FBConnectAuth.php b/plugins/FBConnect/FBConnectAuth.php index 233eb83ab3..4699ce636c 100644 --- a/plugins/FBConnect/FBConnectAuth.php +++ b/plugins/FBConnect/FBConnectAuth.php @@ -62,7 +62,28 @@ class FBConnectauthAction extends Action parent::handle($args); if (common_is_real_login()) { - $this->clientError(_('Already logged in.')); + + // User is already logged in. Does she already have a linked Facebook acct? + $flink = Foreign_link::getByForeignID($this->fbuid, FACEBOOK_CONNECT_SERVICE); + + if ($flink) { + + // User already has a linked Facebook account and shouldn't be here + common_debug('There is already a local user (' . $flink->user_id . + ') linked with this Facebook (' . $this->fbuid . ').'); + + // We don't want these cookies + getFacebook()->clear_cookie_state(); + + $this->clientError(_('There is already a local user linked with this Facebook.')); + + } else { + + // User came from the Facebook connect settings tab, and + // probably just wants to link/relink their Facebook account + $this->connectUser(); + } + } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { $token = $this->trimmed('token'); @@ -78,7 +99,7 @@ class FBConnectauthAction extends Action } $this->createNewUser(); } else if ($this->arg('connect')) { - $this->connectUser(); + $this->connectNewUser(); } else { common_debug(print_r($this->args, true), __FILE__); $this->showForm(_('Something weird happened.'), @@ -259,7 +280,7 @@ class FBConnectauthAction extends Action 303); } - function connectUser() + function connectNewUser() { $nickname = $this->trimmed('nickname'); $password = $this->trimmed('password'); @@ -290,6 +311,23 @@ class FBConnectauthAction extends Action $this->goHome($user->nickname); } + function connectUser() + { + $user = common_current_user(); + + $result = $this->flinkUser($user->id, $this->fbuid); + + if (!$result) { + $this->serverError(_('Error connecting user to Facebook.')); + return; + } + + common_debug("Connected Facebook user $this->fbuid to local user $user->id"); + + // Return to Facebook connection settings tab + common_redirect(common_local_url('FBConnectSettings'), 303); + } + function tryLogin() { common_debug("Trying Facebook Login..."); diff --git a/plugins/FBConnect/FBConnectPlugin.php b/plugins/FBConnect/FBConnectPlugin.php index ad5e47e478..c85ef432d4 100644 --- a/plugins/FBConnect/FBConnectPlugin.php +++ b/plugins/FBConnect/FBConnectPlugin.php @@ -143,23 +143,6 @@ class FBConnectPlugin extends Plugin if ($user) { - $action->menuItem(common_local_url('all', array('nickname' => $user->nickname)), - _('Home'), _('Personal profile and friends timeline'), false, 'nav_home'); - $action->menuItem(common_local_url('profilesettings'), - _('Account'), _('Change your email, avatar, password, profile'), false, 'nav_account'); - if (common_config('xmpp', 'enabled')) { - $action->menuItem(common_local_url('imsettings'), - _('Connect'), _('Connect to IM, SMS, Twitter'), false, 'nav_connect'); - } else { - $action->menuItem(common_local_url('smssettings'), - _('Connect'), _('Connect to SMS, Twitter'), false, 'nav_connect'); - } - $action->menuItem(common_local_url('invite'), - _('Invite'), - sprintf(_('Invite friends and colleagues to join you on %s'), - common_config('site', 'name')), - false, 'nav_invitecontact'); - $flink = Foreign_link::getByUserId($user->id, FACEBOOK_CONNECT_SERVICE); $fbuid = 0; @@ -195,9 +178,25 @@ class FBConnectPlugin extends Plugin } } - // Need to override the Logout link to make it do FB stuff + $action->menuItem(common_local_url('all', array('nickname' => $user->nickname)), + _('Home'), _('Personal profile and friends timeline'), false, 'nav_home'); + $action->menuItem(common_local_url('profilesettings'), + _('Account'), _('Change your email, avatar, password, profile'), false, 'nav_account'); + if (common_config('xmpp', 'enabled')) { + $action->menuItem(common_local_url('imsettings'), + _('Connect'), _('Connect to IM, SMS, Twitter'), false, 'nav_connect'); + } else { + $action->menuItem(common_local_url('smssettings'), + _('Connect'), _('Connect to SMS, Twitter'), false, 'nav_connect'); + } + $action->menuItem(common_local_url('invite'), + _('Invite'), + sprintf(_('Invite friends and colleagues to join you on %s'), + common_config('site', 'name')), + false, 'nav_invitecontact'); - if ($fbuid > 0) { + // Need to override the Logout link to make it do FB stuff + if ($flink && $fbuid > 0) { $logout_url = common_local_url('logout'); $title = _('Logout from the site'); @@ -258,21 +257,32 @@ class FBConnectPlugin extends Plugin return true; } - function onEndLogout($action) + function onStartLogout($action) { - try { + $user = common_current_user(); + + $flink = Foreign_link::getByUserId($user->id, FACEBOOK_CONNECT_SERVICE); + + $action->logout(); + + if ($flink) { $facebook = getFacebook(); - $fbuid = $facebook->get_loggedin_user(); - if ($fbuid > 0) { - $facebook->logout(common_local_url('public')); - } + try { + $fbuid = $facebook->get_loggedin_user(); - } catch (Exception $e) { - common_log(LOG_WARNING, 'Could\'t logout of Facebook: ' . - $e->getMessage()); + if ($fbuid > 0) { + $facebook->logout(common_local_url('public')); + } + + } catch (Exception $e) { + common_log(LOG_WARNING, 'Could\'t logout of Facebook: ' . + $e->getMessage()); + } } + + return true; } } diff --git a/plugins/FBConnect/FBConnectSettings.php b/plugins/FBConnect/FBConnectSettings.php index 7e255f43a6..034ecebae2 100644 --- a/plugins/FBConnect/FBConnectSettings.php +++ b/plugins/FBConnect/FBConnectSettings.php @@ -78,63 +78,73 @@ class FBConnectSettingsAction extends ConnectSettingsAction function showContent() { $user = common_current_user(); - $flink = Foreign_link::getByUserID($user->id, FACEBOOK_CONNECT_SERVICE); + $this->elementStart('form', array('method' => 'post', + 'id' => 'form_settings_facebook', + 'class' => 'form_settings', + 'action' => + common_local_url('FBConnectSettings'))); + if (!$flink) { - $this->element('p', 'form_note', + $this->element('p', 'instructions', _('There is no Facebook user connected to this account.')); $this->element('fb:login-button', array('onlogin' => 'goto_login()', 'length' => 'long')); - return; - } + } else { - $this->element('p', 'form_note', - _('Connected Facebook user:')); + $this->element('p', 'form_note', + _('Connected Facebook user')); + + $this->elementStart('p', array('class' => 'facebook-user-display')); + $this->elementStart('fb:profile-pic', + array('uid' => $flink->foreign_id, + 'size' => 'small', + 'linked' => 'true', + 'facebook-logo' => 'true')); + $this->elementEnd('fb:profile-pic'); + + $this->elementStart('fb:name', array('uid' => $flink->foreign_id, + 'useyou' => 'false')); + $this->elementEnd('fb:name'); + $this->elementEnd('p'); - $this->elementStart('p', array('class' => 'facebook-user-display')); - $this->elementStart('fb:profile-pic', - array('uid' => $flink->foreign_id, - 'size' => 'square', - 'linked' => 'true', - 'facebook-logo' => 'true')); - $this->elementEnd('fb:profile-pic'); + $this->hidden('token', common_session_token()); - $this->elementStart('fb:name', array('uid' => $flink->foreign_id)); - $this->elementEnd('fb:name'); - $this->elementEnd('p'); + $this->elementStart('fieldset'); - $this->elementStart('form', array('method' => 'post', - 'id' => 'form_settings_facebook', - 'class' => 'form_settings', - 'action' => - common_local_url('FBConnectSettings'))); + $this->element('legend', null, _('Disconnect my account from Facebook')); - $this->hidden('token', common_session_token()); + if (!$user->password) { - $this->elementStart('fieldset'); + $this->elementStart('p', array('class' => 'form_guide')); + $this->text(_('Disconnecting your Faceboook ' . + 'would make it impossible to log in! Please ')); + $this->element('a', + array('href' => common_local_url('passwordsettings')), + _('set a password')); - $this->element('legend', null, _('Disconnect my account from Facebook')); + $this->text(_(' first.')); + $this->elementEnd('p'); + } else { - if (!$user->password) { + $note = 'Keep your %s account but disconnect from Facebook. ' . + 'You\'ll use your %s password to log in.'; - $this->elementStart('p', array('class' => 'form_guide')); - $this->text(_('Disconnecting your Faceboook ' . - 'would make it impossible to log in! Please ')); - $this->element('a', - array('href' => common_local_url('passwordsettings')), - _('set a password')); + $site = common_config('site', 'name'); - $this->text(_(' first.')); - $this->elementEnd('p'); - } else { - $this->submit('disconnect', _('Disconnect')); - } + $this->element('p', 'instructions', + sprintf($note, $site, $site)); + + $this->submit('disconnect', _('Disconnect')); + } + + $this->elementEnd('fieldset'); + } - $this->elementEnd('fieldset'); $this->elementEnd('form'); } @@ -171,8 +181,7 @@ class FBConnectSettingsAction extends ConnectSettingsAction try { - // XXX: not sure what exactly to do here - + // Clear FB Connect cookies out $facebook = getFacebook(); $facebook->clear_cookie_state(); @@ -182,7 +191,7 @@ class FBConnectSettingsAction extends ConnectSettingsAction $e->getMessage()); } - $this->showForm(_('Facebook user disconnected.'), true); + $this->showForm(_('You have disconnected from Facebook.'), true); } else { $this->showForm(_('Not sure what you\'re trying to do.')); diff --git a/scripts/fixup_utf8.php b/scripts/fixup_utf8.php new file mode 100644 index 0000000000..e5021ff343 --- /dev/null +++ b/scripts/fixup_utf8.php @@ -0,0 +1,141 @@ +#!/usr/bin/env php +. + */ + +# Abort if called from a web server +if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) { + print "This script must be run from the command line\n"; + exit(1); +} + +ini_set("max_execution_time", "0"); +ini_set("max_input_time", "0"); +set_time_limit(0); +mb_internal_encoding('UTF-8'); + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); +define('LACONICA', true); + +require_once(INSTALLDIR . '/lib/common.php'); +require_once('DB.php'); + +function fixup_utf8($id) { + + $dbl = doConnect('latin1'); + + if (empty($dbl)) { + return; + } + + $dbu = doConnect('utf8'); + + if (empty($dbu)) { + return; + } + + // Do a separate DB connection + + $sth = $dbu->prepare("UPDATE notice SET content = UNHEX(?), rendered = UNHEX(?) WHERE id = ?"); + + if (PEAR::isError($sth)) { + echo "ERROR: " . $sth->getMessage() . "\n"; + return; + } + + $sql = 'SELECT id, content, rendered FROM notice ' . + 'WHERE LENGTH(content) != CHAR_LENGTH(content)'; + + if (!empty($id)) { + $sql .= ' AND id < ' . $id; + } + + $sql .= ' ORDER BY id DESC'; + + $rn = $dbl->query($sql); + + if (PEAR::isError($rn)) { + echo "ERROR: " . $rn->getMessage() . "\n"; + return; + } + + echo "Number of rows: " . $rn->numRows() . "\n"; + + $notice = array(); + + while (DB_OK == $rn->fetchInto($notice)) { + + $id = ($notice[0])+0; + $content = bin2hex($notice[1]); + $rendered = bin2hex($notice[2]); + + echo "$id..."; + + $result =& $dbu->execute($sth, array($content, $rendered, $id)); + + if (PEAR::isError($result)) { + echo "ERROR: " . $result->getMessage() . "\n"; + continue; + } + + $cnt = $dbu->affectedRows(); + + if ($cnt != 1) { + echo "ERROR: 0 rows affected\n"; + continue; + } + + $notice = Notice::staticGet('id', $id); + $notice->decache(); + + echo "OK\n"; + } +} + +function doConnect($charset) +{ + $db = DB::connect(common_config('db', 'database'), + array('persistent' => false)); + + if (PEAR::isError($db)) { + echo "ERROR: " . $db->getMessage() . "\n"; + return NULL; + } + + $result = $db->query("SET NAMES $charset"); + + if (PEAR::isError($result)) { + echo "ERROR: " . $result->getMessage() . "\n"; + $db->disconnect(); + return NULL; + } + + $result = $db->autoCommit(true); + + if (PEAR::isError($result)) { + echo "ERROR: " . $result->getMessage() . "\n"; + $db->disconnect(); + return NULL; + } + + return $db; +} + +$id = ($argc > 1) ? $argv[1] : null; + +fixup_utf8($id); diff --git a/theme/base/css/display.css b/theme/base/css/display.css index 9bc1417b17..7d495c5395 100644 --- a/theme/base/css/display.css +++ b/theme/base/css/display.css @@ -855,20 +855,6 @@ display:inline-block; text-transform:lowercase; } -.notice .attachment { -position:relative; -} -.notice .attachment img { -position:absolute; -top:18px; -left:0; -z-index:99; -} -#shownotice .notice .attachment img { -position:static; -} - - .notice-options { position:relative; font-size:0.95em; @@ -936,6 +922,75 @@ padding:0; } +.notice .attachment { +position:relative; +padding-left:16px; +} +#attachments .attachment { +padding-left:0; +} +.notice .attachment img { +position:absolute; +top:18px; +left:0; +z-index:99; +} +#shownotice .notice .attachment img { +position:static; +} + +#attachments { +clear:both; +float:left; +width:100%; +margin-top:18px; +} +#attachments dt { +font-weight:bold; +font-size:1.3em; +margin-bottom:4px; +} + +#attachments ol li { +margin-bottom:18px; +list-style-type:decimal; +float:left; +clear:both; +} + +#jOverlayContent, +#jOverlayContent #content, +#jOverlayContent #content_inner { +width: auto !important; +margin-bottom:0; +} +#jOverlayContent #content { +padding:11px; +min-height:auto; +} +#jOverlayContent .external span { +display:block; +margin-bottom:11px; +} +#jOverlayContent button { +position:absolute; +top:0; +right:0; +width:29px; +height:29px; +text-align:center; +font-weight:bold; +padding:0; +} +#jOverlayContent h1 { +max-width:475px; +} +#jOverlayContent #content { +border-radius:7px; +-moz-border-radius:7px; +-webkit-border-radius:7px; +} + #usergroups #new_group { float: left; margin-right: 2em; @@ -1182,6 +1237,7 @@ width:33%; } #settings_design_color .form_data label { float:none; +display:block; } #settings_design_color .form_data .swatch { padding:11px; diff --git a/theme/base/css/ie.css b/theme/base/css/ie.css index 5d8bea8ae3..df0388a5ad 100644 --- a/theme/base/css/ie.css +++ b/theme/base/css/ie.css @@ -30,3 +30,9 @@ margin-right:4px; .entity_profile { width:64%; } +#jOverlayContent .notice * { +z-index:1; +} +#jOverlayContent .notice .attachment img { +z-index:9999; +} diff --git a/theme/base/images/icons/twotone/green/clip-02.gif b/theme/base/images/icons/twotone/green/clip-02.gif new file mode 100644 index 0000000000..77a729799b Binary files /dev/null and b/theme/base/images/icons/twotone/green/clip-02.gif differ diff --git a/theme/default/css/display.css b/theme/default/css/display.css index 737db7ce96..e08a4783bd 100644 --- a/theme/default/css/display.css +++ b/theme/default/css/display.css @@ -175,6 +175,12 @@ background-image:url(../../base/images/icons/twotone/green/shield.gif); } /* NOTICES */ +.notice .attachment { +background:transparent url(../../base/images/icons/twotone/green/clip-02.gif) no-repeat 0 45%; +} +#attachments .attachment { +background:none; +} .notice-options .notice_reply a, .notice-options form input.submit { background-color:transparent; diff --git a/theme/identica/css/display.css b/theme/identica/css/display.css index f7abac4823..1f1298737c 100644 --- a/theme/identica/css/display.css +++ b/theme/identica/css/display.css @@ -175,6 +175,12 @@ background-image:url(../../base/images/icons/twotone/green/shield.gif); } /* NOTICES */ +.notice .attachment { +background:transparent url(../../base/images/icons/twotone/green/clip-02.gif) no-repeat 0 45%; +} +#attachments .attachment { +background:none; +} .notice-options .notice_reply a, .notice-options form input.submit { background-color:transparent;