Merge branch '0.8.x' of git@gitorious.org:+laconica-developers/laconica/dev into upload

Conflicts:

	js/util.js
	lib/attachmentlist.php
This commit is contained in:
Robin Millette 2009-05-31 17:12:04 -04:00
commit ebeb5f744c
23 changed files with 504 additions and 111 deletions

49
README
View File

@ -694,6 +694,13 @@ to users on a remote site. (Or not... it's not well tested.) The
Upgrading 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 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 tracking the "git" version of the software, you will probably want
to upgrade and keep your existing data. There is no automated upgrade 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 3. When fixup_inboxes is finished, you can set the enabled flag to
'true'. '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 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 and adding the slaves to this array. Note that if you want some
requests to go to the 'database' (master) server, you'll need requests to go to the 'database' (master) server, you'll need
to include it in this array, too. 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 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 set 'run' to 'never' than to set this value to something
nonsensical. 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 Troubleshooting
=============== ===============

View File

@ -67,6 +67,7 @@ class ApiAction extends Action
$this->process_command(); $this->process_command();
} else { } else {
# basic authentication failed # basic authentication failed
common_log(LOG_WARNING, "Failed API auth attempt, nickname: $nickname.");
$this->show_basic_auth_error(); $this->show_basic_auth_error();
} }
} }

View File

@ -179,14 +179,14 @@ class ConversationTree extends NoticeList
$this->out->elementStart('div', array('id' =>'notices_primary')); $this->out->elementStart('div', array('id' =>'notices_primary'));
$this->out->element('h2', null, _('Notices')); $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)) { if (array_key_exists('root', $this->tree)) {
$rootid = $this->tree['root'][0]; $rootid = $this->tree['root'][0];
$this->showNoticePlus($rootid); $this->showNoticePlus($rootid);
} }
$this->out->elementEnd('ul'); $this->out->elementEnd('ol');
$this->out->elementEnd('div'); $this->out->elementEnd('div');
return $cnt; return $cnt;
@ -215,13 +215,13 @@ class ConversationTree extends NoticeList
if (array_key_exists($id, $this->tree)) { if (array_key_exists($id, $this->tree)) {
$children = $this->tree[$id]; $children = $this->tree[$id];
$this->out->elementStart('ul', array('class' => 'notices')); $this->out->elementStart('ol', array('class' => 'notices'));
foreach ($children as $child) { foreach ($children as $child) {
$this->showNoticePlus($child); $this->showNoticePlus($child);
} }
$this->out->elementEnd('ul'); $this->out->elementEnd('ol');
} }
$this->out->elementEnd('li'); $this->out->elementEnd('li');

View File

@ -70,7 +70,7 @@ class DesignsettingsAction extends AccountSettingsAction
function showContent() function showContent()
{ {
$user = common_current_user(); $user = common_current_user();
$this->elementStart('form', array('method' => 'POST', $this->elementStart('form', array('method' => 'post',
'id' => 'form_settings_design', 'id' => 'form_settings_design',
'class' => 'form_settings', 'class' => 'form_settings',
'action' => 'action' =>

View File

@ -208,10 +208,10 @@ class ShownoticeAction extends Action
function showContent() function showContent()
{ {
$this->elementStart('ul', array('class' => 'notices')); $this->elementStart('ol', array('class' => 'notices xoxo'));
$nli = new NoticeListItem($this->notice, $this); $nli = new NoticeListItem($this->notice, $this);
$nli->show(); $nli->show();
$this->elementEnd('ul'); $this->elementEnd('ol');
} }
/** /**

View File

@ -11,7 +11,7 @@ class Foreign_link extends Memcached_DataObject
public $__table = 'foreign_link'; // table name public $__table = 'foreign_link'; // table name
public $user_id; // int(4) primary_key not_null 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 $service; // int(4) primary_key not_null
public $credentials; // varchar(255) public $credentials; // varchar(255)
public $noticesync; // tinyint(1) not_null default_1 public $noticesync; // tinyint(1) not_null default_1

View File

@ -227,4 +227,22 @@ class Memcached_DataObject extends DB_DataObject
$c->set($ckey, $cached, MEMCACHE_COMPRESSED, $expiry); $c->set($ckey, $cached, MEMCACHE_COMPRESSED, $expiry);
return new ArrayWrapper($cached); 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;
}
} }

View File

@ -285,7 +285,7 @@ create table foreign_user (
create table foreign_link ( create table foreign_link (
user_id int comment 'link to user on this system, if exists' references user (id), 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), service int not null comment 'foreign key to service' references foreign_service(id),
credentials varchar(255) comment 'authc credentials, typically a password', 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', noticesync tinyint not null default 1 comment 'notice synchronization, bit 1 = sync outgoing, bit 2 = sync incoming, bit 3 = filter local replies',

View File

@ -2,6 +2,7 @@ INSERT INTO notice_source
(code, name, url, created) (code, name, url, created)
VALUES VALUES
('adium', 'Adium', 'http://www.adiumx.com/', now()), ('adium', 'Adium', 'http://www.adiumx.com/', now()),
('AgentSolo.com','AgentSolo.com','http://www.agentsolo.com/', now()),
('betwittered','BeTwittered','http://www.32hours.com/betwitteredinfo/', now()), ('betwittered','BeTwittered','http://www.32hours.com/betwitteredinfo/', now()),
('bti','bti','http://gregkh.github.com/bti/', now()), ('bti','bti','http://gregkh.github.com/bti/', now()),
('cliqset', 'Cliqset', 'http://www.cliqset.com/', now()), ('cliqset', 'Cliqset', 'http://www.cliqset.com/', now()),
@ -29,6 +30,7 @@ VALUES
('pingvine','PingVine','http://pingvine.com/', now()), ('pingvine','PingVine','http://pingvine.com/', now()),
('pocketwit','PockeTwit','http://code.google.com/p/pocketwit/', now()), ('pocketwit','PockeTwit','http://code.google.com/p/pocketwit/', now()),
('posty','Posty','http://spreadingfunkyness.com/posty/', now()), ('posty','Posty','http://spreadingfunkyness.com/posty/', now()),
('qtwitter','qTwitter','http://qtwitter.ayoy.net/', now()),
('royalewithcheese','Royale With Cheese','http://p.hellyeah.org/', now()), ('royalewithcheese','Royale With Cheese','http://p.hellyeah.org/', now()),
('rssdent','rssdent','http://github.com/zcopley/rssdent/tree/master', now()), ('rssdent','rssdent','http://github.com/zcopley/rssdent/tree/master', now()),
('rygh.no','rygh.no','http://rygh.no/', now()), ('rygh.no','rygh.no','http://rygh.no/', now()),

View File

@ -250,6 +250,7 @@ $(document).ready(function(){
$("#form_notice").each(addAjaxHidden); $("#form_notice").each(addAjaxHidden);
NoticeHover(); NoticeHover();
NoticeReply(); NoticeReply();
NoticeAttachments();
}); });
@ -288,3 +289,53 @@ function NoticeReplySet(nick,id) {
} }
return true; 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('<button>&#215;</button>');
$('#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');
}
);
}

View File

@ -83,11 +83,10 @@ class AttachmentList extends Widget
$atts = new File; $atts = new File;
$att = $atts->getAttachments($this->notice->id); $att = $atts->getAttachments($this->notice->id);
if (empty($att)) return 0; if (empty($att)) return 0;
$this->out->elementStart('dl', array('id' =>'attachments'));
$this->out->elementStart('dl', array('id' =>'attachment'));
$this->out->element('dt', null, _('Attachments')); $this->out->element('dt', null, _('Attachments'));
$this->out->elementStart('dd'); $this->out->elementStart('dd');
$this->out->elementStart('ul', array('class' => 'attachments')); $this->out->elementStart('ol', array('class' => 'attachments'));
foreach ($att as $n=>$attachment) { foreach ($att as $n=>$attachment) {
$item = $this->newListItem($attachment); $item = $this->newListItem($attachment);
@ -95,7 +94,7 @@ class AttachmentList extends Widget
} }
$this->out->elementEnd('dd'); $this->out->elementEnd('dd');
$this->out->elementEnd('ul'); $this->out->elementEnd('ol');
$this->out->elementEnd('dl'); $this->out->elementEnd('dl');
return count($att); return count($att);

View File

@ -208,6 +208,7 @@ $config['db'] =
'require_prefix' => 'classes/', 'require_prefix' => 'classes/',
'class_prefix' => '', 'class_prefix' => '',
'mirror' => null, 'mirror' => null,
'utf8' => true,
'db_driver' => 'DB', # XXX: JanRain libs only work with DB 'db_driver' => 'DB', # XXX: JanRain libs only work with DB
'quote_identifiers' => false, 'quote_identifiers' => false,
'type' => 'mysql' ); 'type' => 'mysql' );

View File

@ -86,7 +86,7 @@ class NoticeList extends Widget
{ {
$this->out->elementStart('div', array('id' =>'notices_primary')); $this->out->elementStart('div', array('id' =>'notices_primary'));
$this->out->element('h2', null, _('Notices')); $this->out->element('h2', null, _('Notices'));
$this->out->elementStart('ul', array('class' => 'notices')); $this->out->elementStart('ol', array('class' => 'notices xoxo'));
$cnt = 0; $cnt = 0;
@ -101,7 +101,7 @@ class NoticeList extends Widget
$item->show(); $item->show();
} }
$this->out->elementEnd('ul'); $this->out->elementEnd('ol');
$this->out->elementEnd('div'); $this->out->elementEnd('div');
return $cnt; return $cnt;

View File

@ -52,12 +52,12 @@ class NoticeSection extends Section
{ {
$notices = $this->getNotices(); $notices = $this->getNotices();
$cnt = 0; $cnt = 0;
$this->out->elementStart('ul', 'notices'); $this->out->elementStart('ol', 'notices xoxo');
while ($notices->fetch() && ++$cnt <= NOTICES_PER_SECTION) { while ($notices->fetch() && ++$cnt <= NOTICES_PER_SECTION) {
$this->showNotice($notices); $this->showNotice($notices);
} }
$this->out->elementEnd('ul'); $this->out->elementEnd('ol');
return ($cnt > NOTICES_PER_SECTION); return ($cnt > NOTICES_PER_SECTION);
} }

View File

@ -62,7 +62,28 @@ class FBConnectauthAction extends Action
parent::handle($args); parent::handle($args);
if (common_is_real_login()) { 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') { } else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$token = $this->trimmed('token'); $token = $this->trimmed('token');
@ -78,7 +99,7 @@ class FBConnectauthAction extends Action
} }
$this->createNewUser(); $this->createNewUser();
} else if ($this->arg('connect')) { } else if ($this->arg('connect')) {
$this->connectUser(); $this->connectNewUser();
} else { } else {
common_debug(print_r($this->args, true), __FILE__); common_debug(print_r($this->args, true), __FILE__);
$this->showForm(_('Something weird happened.'), $this->showForm(_('Something weird happened.'),
@ -259,7 +280,7 @@ class FBConnectauthAction extends Action
303); 303);
} }
function connectUser() function connectNewUser()
{ {
$nickname = $this->trimmed('nickname'); $nickname = $this->trimmed('nickname');
$password = $this->trimmed('password'); $password = $this->trimmed('password');
@ -290,6 +311,23 @@ class FBConnectauthAction extends Action
$this->goHome($user->nickname); $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() function tryLogin()
{ {
common_debug("Trying Facebook Login..."); common_debug("Trying Facebook Login...");

View File

@ -143,23 +143,6 @@ class FBConnectPlugin extends Plugin
if ($user) { 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); $flink = Foreign_link::getByUserId($user->id, FACEBOOK_CONNECT_SERVICE);
$fbuid = 0; $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'); $logout_url = common_local_url('logout');
$title = _('Logout from the site'); $title = _('Logout from the site');
@ -258,11 +257,19 @@ class FBConnectPlugin extends Plugin
return true; 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(); $facebook = getFacebook();
try {
$fbuid = $facebook->get_loggedin_user(); $fbuid = $facebook->get_loggedin_user();
if ($fbuid > 0) { if ($fbuid > 0) {
@ -275,4 +282,7 @@ class FBConnectPlugin extends Plugin
} }
} }
return true;
}
} }

View File

@ -78,41 +78,40 @@ class FBConnectSettingsAction extends ConnectSettingsAction
function showContent() function showContent()
{ {
$user = common_current_user(); $user = common_current_user();
$flink = Foreign_link::getByUserID($user->id, FACEBOOK_CONNECT_SERVICE); $flink = Foreign_link::getByUserID($user->id, FACEBOOK_CONNECT_SERVICE);
if (!$flink) {
$this->element('p', 'form_note',
_('There is no Facebook user connected to this account.'));
$this->element('fb:login-button', array('onlogin' => 'goto_login()',
'length' => 'long'));
return;
}
$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' => 'square',
'linked' => 'true',
'facebook-logo' => 'true'));
$this->elementEnd('fb:profile-pic');
$this->elementStart('fb:name', array('uid' => $flink->foreign_id));
$this->elementEnd('fb:name');
$this->elementEnd('p');
$this->elementStart('form', array('method' => 'post', $this->elementStart('form', array('method' => 'post',
'id' => 'form_settings_facebook', 'id' => 'form_settings_facebook',
'class' => 'form_settings', 'class' => 'form_settings',
'action' => 'action' =>
common_local_url('FBConnectSettings'))); common_local_url('FBConnectSettings')));
if (!$flink) {
$this->element('p', 'instructions',
_('There is no Facebook user connected to this account.'));
$this->element('fb:login-button', array('onlogin' => 'goto_login()',
'length' => 'long'));
} else {
$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->hidden('token', common_session_token()); $this->hidden('token', common_session_token());
$this->elementStart('fieldset'); $this->elementStart('fieldset');
@ -131,10 +130,21 @@ class FBConnectSettingsAction extends ConnectSettingsAction
$this->text(_(' first.')); $this->text(_(' first.'));
$this->elementEnd('p'); $this->elementEnd('p');
} else { } else {
$note = 'Keep your %s account but disconnect from Facebook. ' .
'You\'ll use your %s password to log in.';
$site = common_config('site', 'name');
$this->element('p', 'instructions',
sprintf($note, $site, $site));
$this->submit('disconnect', _('Disconnect')); $this->submit('disconnect', _('Disconnect'));
} }
$this->elementEnd('fieldset'); $this->elementEnd('fieldset');
}
$this->elementEnd('form'); $this->elementEnd('form');
} }
@ -171,8 +181,7 @@ class FBConnectSettingsAction extends ConnectSettingsAction
try { try {
// XXX: not sure what exactly to do here // Clear FB Connect cookies out
$facebook = getFacebook(); $facebook = getFacebook();
$facebook->clear_cookie_state(); $facebook->clear_cookie_state();
@ -182,7 +191,7 @@ class FBConnectSettingsAction extends ConnectSettingsAction
$e->getMessage()); $e->getMessage());
} }
$this->showForm(_('Facebook user disconnected.'), true); $this->showForm(_('You have disconnected from Facebook.'), true);
} else { } else {
$this->showForm(_('Not sure what you\'re trying to do.')); $this->showForm(_('Not sure what you\'re trying to do.'));

141
scripts/fixup_utf8.php Normal file
View File

@ -0,0 +1,141 @@
#!/usr/bin/env php
<?php
/*
* Laconica - a distributed open-source microblogging tool
* Copyright (C) 2009, Control Yourself, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
# 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);

View File

@ -855,20 +855,6 @@ display:inline-block;
text-transform:lowercase; 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 { .notice-options {
position:relative; position:relative;
font-size:0.95em; 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 { #usergroups #new_group {
float: left; float: left;
margin-right: 2em; margin-right: 2em;
@ -1182,6 +1237,7 @@ width:33%;
} }
#settings_design_color .form_data label { #settings_design_color .form_data label {
float:none; float:none;
display:block;
} }
#settings_design_color .form_data .swatch { #settings_design_color .form_data .swatch {
padding:11px; padding:11px;

View File

@ -30,3 +30,9 @@ margin-right:4px;
.entity_profile { .entity_profile {
width:64%; width:64%;
} }
#jOverlayContent .notice * {
z-index:1;
}
#jOverlayContent .notice .attachment img {
z-index:9999;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 B

View File

@ -175,6 +175,12 @@ background-image:url(../../base/images/icons/twotone/green/shield.gif);
} }
/* NOTICES */ /* 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 .notice_reply a,
.notice-options form input.submit { .notice-options form input.submit {
background-color:transparent; background-color:transparent;

View File

@ -175,6 +175,12 @@ background-image:url(../../base/images/icons/twotone/green/shield.gif);
} }
/* NOTICES */ /* 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 .notice_reply a,
.notice-options form input.submit { .notice-options form input.submit {
background-color:transparent; background-color:transparent;