Merge branch '0.8.x' into 0.9.x

This commit is contained in:
Evan Prodromou 2009-07-23 14:46:54 -07:00
commit 8641cb4591
49 changed files with 1008 additions and 311 deletions

13
README
View File

@ -134,7 +134,7 @@ Prerequisites
The following software packages are *required* for this software to The following software packages are *required* for this software to
run correctly. run correctly.
- PHP 5.2.x. It may be possible to run this software on earlier - PHP 5.2.3+. It may be possible to run this software on earlier
versions of PHP, but many of the functions used are only available versions of PHP, but many of the functions used are only available
in PHP 5.2 or above. in PHP 5.2 or above.
- MySQL 5.x. The Laconica database is stored, by default, in a MySQL - MySQL 5.x. The Laconica database is stored, by default, in a MySQL
@ -262,13 +262,16 @@ especially if you've previously installed PHP/MySQL packages.
that user's default group instead. As a last resort, you can create that user's default group instead. As a last resort, you can create
a new group like "mublog" and add the Web server's user to the group. a new group like "mublog" and add the Web server's user to the group.
4. You should also take this moment to make your avatar subdirectory 4. You should also take this moment to make your avatar, background, and
writeable by the Web server. An insecure way to do this is: file subdirectories writeable by the Web server. An insecure way to do
this is:
chmod a+w /var/www/mublog/avatar chmod a+w /var/www/mublog/avatar
chmod a+w /var/www/mublog/background
chmod a+w /var/www/mublog/file
You can also make the avatar directory writeable by the Web server You can also make the avatar, background, and file directories
group, as noted above. writeable by the Web server group, as noted above.
5. Create a database to hold your microblog data. Something like this 5. Create a database to hold your microblog data. Something like this
should work: should work:

View File

@ -129,6 +129,7 @@ class ApiAction extends Action
'laconica/config', 'laconica/config',
'laconica/wadl', 'laconica/wadl',
'tags/timeline', 'tags/timeline',
'oembed/oembed',
'groups/timeline'); 'groups/timeline');
static $bareauth = array('statuses/user_timeline', static $bareauth = array('statuses/user_timeline',

View File

@ -98,6 +98,28 @@ class AttachmentAction extends Action
return $a->title(); return $a->title();
} }
function extraHead()
{
$this->element('link',array('rel'=>'alternate',
'type'=>'application/json+oembed',
'href'=>common_local_url(
'api',
array('apiaction'=>'oembed','method'=>'oembed.json'),
array('url'=>
common_local_url('attachment',
array('attachment' => $this->attachment->id)))),
'title'=>'oEmbed'),null);
$this->element('link',array('rel'=>'alternate',
'type'=>'text/xml+oembed',
'href'=>common_local_url(
'api',
array('apiaction'=>'oembed','method'=>'oembed.xml'),
array('url'=>
common_local_url('attachment',
array('attachment' => $this->attachment->id)))),
'title'=>'oEmbed'),null);
}
/** /**
* Handle input * Handle input
* *

View File

@ -83,7 +83,7 @@ class FinishopenidloginAction extends Action
function showContent() function showContent()
{ {
if (!empty($this->message_text)) { if (!empty($this->message_text)) {
$this->element('p', null, $this->message); $this->element('div', array('class' => 'error'), $this->message_text);
return; return;
} }

View File

@ -194,6 +194,9 @@ class RecoverpasswordAction extends Action
'or your registered email address.')); 'or your registered email address.'));
$this->elementEnd('li'); $this->elementEnd('li');
$this->elementEnd('ul'); $this->elementEnd('ul');
$this->element('input', array('name' => 'recover',
'type' => 'hidden',
'value' => _('Recover')));
$this->submit('recover', _('Recover')); $this->submit('recover', _('Recover'));
$this->elementEnd('fieldset'); $this->elementEnd('fieldset');
$this->elementEnd('form'); $this->elementEnd('form');

View File

@ -281,6 +281,20 @@ class ShownoticeAction extends OwnerDesignAction
$this->element('meta', array('name' => 'microid', $this->element('meta', array('name' => 'microid',
'content' => $id->toString())); 'content' => $id->toString()));
} }
$this->element('link',array('rel'=>'alternate',
'type'=>'application/json+oembed',
'href'=>common_local_url(
'api',
array('apiaction'=>'oembed','method'=>'oembed.json'),
array('url'=>$this->notice->uri)),
'title'=>'oEmbed'),null);
$this->element('link',array('rel'=>'alternate',
'type'=>'text/xml+oembed',
'href'=>common_local_url(
'api',
array('apiaction'=>'oembed','method'=>'oembed.xml'),
array('url'=>$this->notice->uri)),
'title'=>'oEmbed'),null);
} }
} }

View File

@ -171,4 +171,5 @@ class TwitapilaconicaAction extends TwitterapiAction
parent::handle($args); parent::handle($args);
$this->serverError(_('API method under construction.'), 501); $this->serverError(_('API method under construction.'), 501);
} }
} }

173
actions/twitapioembed.php Normal file
View File

@ -0,0 +1,173 @@
<?php
/**
* Laconica, the distributed open-source microblogging tool
*
* Laconica-only extensions to the Twitter-like API
*
* PHP version 5
*
* LICENCE: 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/>.
*
* @category Twitter
* @package Laconica
* @author Evan Prodromou <evan@controlyourself.ca>
* @copyright 2008 Control Yourself, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://laconi.ca/
*/
if (!defined('LACONICA')) {
exit(1);
}
require_once INSTALLDIR.'/lib/twitterapi.php';
/**
* Oembed provider implementation
*
* This class handles all /main/oembed(.xml|.json)/ requests.
*
* @category oEmbed
* @package Laconica
* @author Craig Andrews <candrews@integralblue.com>
* @copyright 2008 Control Yourself, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://laconi.ca/
*/
class TwitapioembedAction extends TwitterapiAction
{
function oembed($args, $apidata)
{
parent::handle($args);
common_debug("in oembed api action");
$this->auth_user = $apidata['user'];
$url = $args['url'];
if( substr(strtolower($url),0,strlen(common_root_url())) == strtolower(common_root_url()) ){
$path = substr($url,strlen(common_root_url()));
$r = Router::get();
$proxy_args = $r->map($path);
if (!$proxy_args) {
$this->serverError(_("$path not found"), 404);
}
$oembed=array();
$oembed['version']='1.0';
$oembed['provider_name']=common_config('site', 'name');
$oembed['provider_url']=common_root_url();
switch($proxy_args['action']){
case 'shownotice':
$oembed['type']='link';
$id = $proxy_args['notice'];
$notice = Notice::staticGet($id);
if(empty($notice)){
$this->serverError(_("notice $id not found"), 404);
}
$profile = $notice->getProfile();
if (empty($profile)) {
$this->serverError(_('Notice has no profile'), 500);
}
if (!empty($profile->fullname)) {
$authorname = $profile->fullname . ' (' . $profile->nickname . ')';
} else {
$authorname = $profile->nickname;
}
$oembed['title'] = sprintf(_('%1$s\'s status on %2$s'),
$authorname,
common_exact_date($notice->created));
$oembed['author_name']=$authorname;
$oembed['author_url']=$profile->profileurl;
$oembed['url']=($notice->url?$notice->url:$notice->uri);
$oembed['html']=$notice->rendered;
break;
case 'attachment':
$id = $proxy_args['attachment'];
$attachment = File::staticGet($id);
if(empty($attachment)){
$this->serverError(_("attachment $id not found"), 404);
}
if(empty($attachment->filename) && $file_oembed = File_oembed::staticGet('file_id', $attachment->id)){
// Proxy the existing oembed information
$oembed['type']=$file_oembed->type;
$oembed['provider']=$file_oembed->provider;
$oembed['provider_url']=$file_oembed->provider_url;
$oembed['width']=$file_oembed->width;
$oembed['height']=$file_oembed->height;
$oembed['html']=$file_oembed->html;
$oembed['title']=$file_oembed->title;
$oembed['author_name']=$file_oembed->author_name;
$oembed['author_url']=$file_oembed->author_url;
$oembed['url']=$file_oembed->url;
}else if(substr($attachment->mimetype,0,strlen('image/'))=='image/'){
$oembed['type']='photo';
//TODO set width and height
//$oembed['width']=
//$oembed['height']=
$oembed['url']=$attachment->url;
}else{
$oembed['type']='link';
$oembed['url']=common_local_url('attachment',
array('attachment' => $attachment->id));
}
if($attachment->title) $oembed['title']=$attachment->title;
break;
default:
$this->serverError(_("$path not supported for oembed requests"), 501);
}
switch($apidata['content-type']){
case 'xml':
$this->init_document('xml');
$this->elementStart('oembed');
$this->element('version',null,$oembed['version']);
$this->element('type',null,$oembed['type']);
if($oembed['provider_name']) $this->element('provider_name',null,$oembed['provider_name']);
if($oembed['provider_url']) $this->element('provider_url',null,$oembed['provider_url']);
if($oembed['title']) $this->element('title',null,$oembed['title']);
if($oembed['author_name']) $this->element('author_name',null,$oembed['author_name']);
if($oembed['author_url']) $this->element('author_url',null,$oembed['author_url']);
if($oembed['url']) $this->element('url',null,$oembed['url']);
if($oembed['html']) $this->element('html',null,$oembed['html']);
if($oembed['width']) $this->element('width',null,$oembed['width']);
if($oembed['height']) $this->element('height',null,$oembed['height']);
if($oembed['cache_age']) $this->element('cache_age',null,$oembed['cache_age']);
if($oembed['thumbnail_url']) $this->element('thumbnail_url',null,$oembed['thumbnail_url']);
if($oembed['thumbnail_width']) $this->element('thumbnail_width',null,$oembed['thumbnail_width']);
if($oembed['thumbnail_height']) $this->element('thumbnail_height',null,$oembed['thumbnail_height']);
$this->elementEnd('oembed');
$this->end_document('xml');
break;
case 'json':
$this->init_document('json');
print(json_encode($oembed));
$this->end_document('json');
break;
default:
$this->serverError(_('content type ' . $apidata['content-type'] . ' not supported'), 501);
}
}else{
$this->serverError(_('Only ' . common_root_url() . ' urls over plain http please'), 404);
}
}
}

View File

@ -59,7 +59,7 @@ class Fave extends Memcached_DataObject
$qry = 'SELECT fave.* FROM fave '; $qry = 'SELECT fave.* FROM fave ';
$qry .= 'INNER JOIN notice ON fave.notice_id = notice.id '; $qry .= 'INNER JOIN notice ON fave.notice_id = notice.id ';
$qry .= 'WHERE fave.user_id = ' . $user_id . ' '; $qry .= 'WHERE fave.user_id = ' . $user_id . ' ';
$qry .= 'AND notice.is_local != ' . NOTICE_GATEWAY . ' '; $qry .= 'AND notice.is_local != ' . Notice::GATEWAY . ' ';
} }
if ($since_id != 0) { if ($since_id != 0) {
@ -79,7 +79,7 @@ class Fave extends Memcached_DataObject
$qry .= 'ORDER BY modified DESC '; $qry .= 'ORDER BY modified DESC ';
if (!is_null($offset)) { if (!is_null($offset)) {
$qry .= "LIMIT $offset, $limit"; $qry .= "LIMIT $limit OFFSET $offset";
} }
$fav->query($qry); $fav->query($qry);

View File

@ -79,9 +79,8 @@ class File extends Memcached_DataObject
if (isset($redir_data['type']) if (isset($redir_data['type'])
&& ('text/html' === substr($redir_data['type'], 0, 9)) && ('text/html' === substr($redir_data['type'], 0, 9))
&& ($oembed_data = File_oembed::_getOembed($given_url)) && ($oembed_data = File_oembed::_getOembed($given_url))) {
&& isset($oembed_data['json'])) { File_oembed::saveNew($oembed_data, $file_id);
File_oembed::saveNew($oembed_data['json'], $file_id);
} }
return $x; return $x;
} }
@ -123,6 +122,7 @@ class File extends Memcached_DataObject
} }
function isRespectsQuota($user,$fileSize) { function isRespectsQuota($user,$fileSize) {
if ($fileSize > common_config('attachments', 'file_quota')) { if ($fileSize > common_config('attachments', 'file_quota')) {
return sprintf(_('No file may be larger than %d bytes ' . return sprintf(_('No file may be larger than %d bytes ' .
'and the file you sent was %d bytes. Try to upload a smaller version.'), 'and the file you sent was %d bytes. Try to upload a smaller version.'),
@ -136,8 +136,7 @@ class File extends Memcached_DataObject
if ($total > common_config('attachments', 'user_quota')) { if ($total > common_config('attachments', 'user_quota')) {
return sprintf(_('A file this large would exceed your user quota of %d bytes.'), common_config('attachments', 'user_quota')); return sprintf(_('A file this large would exceed your user quota of %d bytes.'), common_config('attachments', 'user_quota'));
} }
$query .= ' AND EXTRACT(month FROM file.modified) = EXTRACT(month FROM now()) and EXTRACT(year FROM file.modified) = EXTRACT(year FROM now())';
$query .= ' month(modified) = month(now()) and year(modified) = year(now())';
$this->query($query); $this->query($query);
$this->fetch(); $this->fetch();
$total = $this->total + $fileSize; $total = $this->total + $fileSize;

View File

@ -56,33 +56,46 @@ class File_oembed extends Memcached_DataObject
return array(false, false, false); return array(false, false, false);
} }
function _getOembed($url, $maxwidth = 500, $maxheight = 400, $format = 'json') { function _getOembed($url, $maxwidth = 500, $maxheight = 400) {
$cmd = common_config('oohembed', 'endpoint') . '?url=' . urlencode($url); require_once INSTALLDIR.'/extlib/Services/oEmbed.php';
if (is_int($maxwidth)) $cmd .= "&maxwidth=$maxwidth"; $parameters = array(
if (is_int($maxheight)) $cmd .= "&maxheight=$maxheight"; 'maxwidth'=>$maxwidth,
if (is_string($format)) $cmd .= "&format=$format"; 'maxheight'=>$maxheight,
$oe = @file_get_contents($cmd); );
if (false === $oe) return false; try{
return array($format => (('json' === $format) ? json_decode($oe, true) : $oe)); $oEmbed = new Services_oEmbed($url);
$object = $oEmbed->getObject($parameters);
return $object;
}catch(Exception $e){
try{
$oEmbed = new Services_oEmbed($url, array(
Services_oEmbed::OPTION_API => common_config('oohembed', 'endpoint')
));
$object = $oEmbed->getObject($parameters);
return $object;
}catch(Exception $ex){
return false;
}
}
} }
function saveNew($data, $file_id) { function saveNew($data, $file_id) {
$file_oembed = new File_oembed; $file_oembed = new File_oembed;
$file_oembed->file_id = $file_id; $file_oembed->file_id = $file_id;
$file_oembed->version = $data['version']; $file_oembed->version = $data->version;
$file_oembed->type = $data['type']; $file_oembed->type = $data->type;
if (!empty($data['provider_name'])) $file_oembed->provider = $data['provider_name']; if (!empty($data->provider_name)) $file_oembed->provider = $data->provider_name;
if (!isset($file_oembed->provider) && !empty($data['provide'])) $file_oembed->provider = $data['provider']; if (!empty($data->provider)) $file_oembed->provider = $data->provider;
if (!empty($data['provide_url'])) $file_oembed->provider_url = $data['provider_url']; if (!empty($data->provide_url)) $file_oembed->provider_url = $data->provider_url;
if (!empty($data['width'])) $file_oembed->width = intval($data['width']); if (!empty($data->width)) $file_oembed->width = intval($data->width);
if (!empty($data['height'])) $file_oembed->height = intval($data['height']); if (!empty($data->height)) $file_oembed->height = intval($data->height);
if (!empty($data['html'])) $file_oembed->html = $data['html']; if (!empty($data->html)) $file_oembed->html = $data->html;
if (!empty($data['title'])) $file_oembed->title = $data['title']; if (!empty($data->title)) $file_oembed->title = $data->title;
if (!empty($data['author_name'])) $file_oembed->author_name = $data['author_name']; if (!empty($data->author_name)) $file_oembed->author_name = $data->author_name;
if (!empty($data['author_url'])) $file_oembed->author_url = $data['author_url']; if (!empty($data->author_url)) $file_oembed->author_url = $data->author_url;
if (!empty($data['url'])) $file_oembed->url = $data['url']; if (!empty($data->url)) $file_oembed->url = $data->url;
$file_oembed->insert(); $file_oembed->insert();
if (!empty($data['thumbnail_url'])) { if (!empty($data->thumbnail_url)) {
File_thumbnail::saveNew($data, $file_id); File_thumbnail::saveNew($data, $file_id);
} }
} }

View File

@ -51,9 +51,9 @@ class File_thumbnail extends Memcached_DataObject
function saveNew($data, $file_id) { function saveNew($data, $file_id) {
$tn = new File_thumbnail; $tn = new File_thumbnail;
$tn->file_id = $file_id; $tn->file_id = $file_id;
$tn->url = $data['thumbnail_url']; $tn->url = $data->thumbnail_url;
$tn->width = intval($data['thumbnail_width']); $tn->width = intval($data->thumbnail_width);
$tn->height = intval($data['thumbnail_height']); $tn->height = intval($data->thumbnail_height);
$tn->insert(); $tn->insert();
} }
} }

View File

@ -32,7 +32,6 @@ define('NOTICE_CACHE_WINDOW', 61);
define('NOTICE_LOCAL_PUBLIC', 1); define('NOTICE_LOCAL_PUBLIC', 1);
define('NOTICE_REMOTE_OMB', 0); define('NOTICE_REMOTE_OMB', 0);
define('NOTICE_LOCAL_NONPUBLIC', -1); define('NOTICE_LOCAL_NONPUBLIC', -1);
define('NOTICE_GATEWAY', -2);
define('MAX_BOXCARS', 128); define('MAX_BOXCARS', 128);
@ -63,6 +62,8 @@ class Notice extends Memcached_DataObject
/* the code above is auto generated do not remove the tag below */ /* the code above is auto generated do not remove the tag below */
###END_AUTOCODE ###END_AUTOCODE
const GATEWAY = -2;
function getProfile() function getProfile()
{ {
return Profile::staticGet('id', $this->profile_id); return Profile::staticGet('id', $this->profile_id);
@ -111,13 +112,21 @@ class Notice extends Memcached_DataObject
function saveTags() function saveTags()
{ {
/* extract all #hastags */ /* extract all #hastags */
$count = preg_match_all('/(?:^|\s)#([A-Za-z0-9_\-\.]{1,64})/', strtolower($this->content), $match); $count = preg_match_all('/(?:^|\s)#([\pL\pN_\-\.]{1,64})/', strtolower($this->content), $match);
if (!$count) { if (!$count) {
return true; return true;
} }
//turn each into their canonical tag
//this is needed to remove dupes before saving e.g. #hash.tag = #hashtag
$hashtags = array();
for($i=0; $i<count($match[1]); $i++) {
$hashtags[] = common_canonical_tag($match[1][$i]);
}
/* Add them to the database */ /* Add them to the database */
foreach(array_unique($match[1]) as $hashtag) { foreach(array_unique($hashtags) as $hashtag) {
/* elide characters we don't want in the tag */ /* elide characters we don't want in the tag */
$this->saveTag($hashtag); $this->saveTag($hashtag);
} }
@ -126,8 +135,6 @@ class Notice extends Memcached_DataObject
function saveTag($hashtag) function saveTag($hashtag)
{ {
$hashtag = common_canonical_tag($hashtag);
$tag = new Notice_tag(); $tag = new Notice_tag();
$tag->notice_id = $this->id; $tag->notice_id = $this->id;
$tag->tag = $hashtag; $tag->tag = $hashtag;
@ -887,7 +894,7 @@ class Notice extends Memcached_DataObject
if ($cnt > 0) { if ($cnt > 0) {
$qry .= ', '; $qry .= ', ';
} }
$qry .= '('.$id.', '.$this->id.', '.$source.', "'.$this->created.'") '; $qry .= '('.$id.', '.$this->id.', '.$source.", '".$this->created. "') ";
$cnt++; $cnt++;
if (rand() % NOTICE_INBOX_SOFT_LIMIT == 0) { if (rand() % NOTICE_INBOX_SOFT_LIMIT == 0) {
Notice_inbox::gc($id); Notice_inbox::gc($id);
@ -913,10 +920,14 @@ class Notice extends Memcached_DataObject
{ {
$user = new User(); $user = new User();
if(common_config('db','quote_identifiers'))
$user_table = '"user"';
else $user_table = 'user';
$qry = $qry =
'SELECT id ' . 'SELECT id ' .
'FROM user JOIN subscription '. 'FROM '. $user_table .' JOIN subscription '.
'ON user.id = subscription.subscriber ' . 'ON '. $user_table .'.id = subscription.subscriber ' .
'WHERE subscription.subscribed = %d '; 'WHERE subscription.subscribed = %d ';
$user->query(sprintf($qry, $this->profile_id)); $user->query(sprintf($qry, $this->profile_id));

View File

@ -199,7 +199,7 @@ class Profile extends Memcached_DataObject
$query .= ' order by id DESC'; $query .= ' order by id DESC';
if (!is_null($offset)) { if (!is_null($offset)) {
$query .= " limit $offset, $limit"; $query .= " LIMIT $limit OFFSET $offset";
} }
$notice->query($query); $notice->query($query);
@ -360,7 +360,6 @@ class Profile extends Memcached_DataObject
$c->set(common_cache_key('profile:subscription_count:'.$this->id), $cnt); $c->set(common_cache_key('profile:subscription_count:'.$this->id), $cnt);
} }
common_debug("subscriptionCount == $cnt");
return $cnt; return $cnt;
} }
@ -385,7 +384,6 @@ class Profile extends Memcached_DataObject
$c->set(common_cache_key('profile:subscriber_count:'.$this->id), $cnt); $c->set(common_cache_key('profile:subscriber_count:'.$this->id), $cnt);
} }
common_debug("subscriberCount == $cnt");
return $cnt; return $cnt;
} }
@ -407,7 +405,6 @@ class Profile extends Memcached_DataObject
$c->set(common_cache_key('profile:fave_count:'.$this->id), $cnt); $c->set(common_cache_key('profile:fave_count:'.$this->id), $cnt);
} }
common_debug("faveCount == $cnt");
return $cnt; return $cnt;
} }
@ -430,7 +427,6 @@ class Profile extends Memcached_DataObject
$c->set(common_cache_key('profile:notice_count:'.$this->id), $cnt); $c->set(common_cache_key('profile:notice_count:'.$this->id), $cnt);
} }
common_debug("noticeCount == $cnt");
return $cnt; return $cnt;
} }

View File

@ -106,14 +106,11 @@ class Session extends Memcached_DataObject
{ {
self::logdeb("garbage collection (maxlifetime = $maxlifetime)"); self::logdeb("garbage collection (maxlifetime = $maxlifetime)");
$epoch = time() - $maxlifetime; $epoch = common_sql_date(time() - $maxlifetime);
$qry = 'DELETE FROM session ' .
'WHERE modified < "'.$epoch.'"';
$session = new Session(); $session = new Session();
$session->whereAdd('modified < "'.$epoch.'"');
$result = $session->query($qry); $result = $session->delete(DB_DATAOBJECT_WHEREADD_ONLY);
self::logdeb("garbage collection result = $result"); self::logdeb("garbage collection result = $result");
} }

View File

@ -443,7 +443,7 @@ class User extends Memcached_DataObject
'SELECT notice.* ' . 'SELECT notice.* ' .
'FROM notice JOIN subscription ON notice.profile_id = subscription.subscribed ' . 'FROM notice JOIN subscription ON notice.profile_id = subscription.subscribed ' .
'WHERE subscription.subscriber = %d ' . 'WHERE subscription.subscriber = %d ' .
'AND notice.is_local != ' . NOTICE_GATEWAY; 'AND notice.is_local != ' . Notice::GATEWAY;
return Notice::getStream(sprintf($qry, $this->id), return Notice::getStream(sprintf($qry, $this->id),
'user:notices_with_friends:' . $this->id, 'user:notices_with_friends:' . $this->id,
$offset, $limit, $since_id, $before_id, $offset, $limit, $since_id, $before_id,

View File

@ -275,11 +275,14 @@ class User_group extends Memcached_DataObject
// XXX: cache this // XXX: cache this
$user = new User(); $user = new User();
if(common_config('db','quote_identifiers'))
$user_table = '"user"';
else $user_table = 'user';
$qry = $qry =
'SELECT id ' . 'SELECT id ' .
'FROM user JOIN group_member '. 'FROM '. $user_table .' JOIN group_member '.
'ON user.id = group_member.profile_id ' . 'ON '. $user_table .'.id = group_member.profile_id ' .
'WHERE group_member.group_id = %d '; 'WHERE group_member.group_id = %d ';
$user->query(sprintf($qry, $this->id)); $user->query(sprintf($qry, $this->id));

View File

@ -40,6 +40,19 @@ create table sms_carrier (
modified timestamp /* comment 'date this record was modified ' */ modified timestamp /* comment 'date this record was modified ' */
); );
create sequence design_seq;
create table design (
id bigint default nextval('design_seq') /* comment 'design ID'*/,
backgroundcolor integer /* comment 'main background color'*/ ,
contentcolor integer /*comment 'content area background color'*/ ,
sidebarcolor integer /*comment 'sidebar background color'*/ ,
textcolor integer /*comment 'text color'*/ ,
linkcolor integer /*comment 'link color'*/,
backgroundimage varchar(255) /*comment 'background image, if any'*/,
disposition int default 1 /*comment 'bit 1 = hide background image, bit 2 = display background image, bit 4 = tile background image'*/,
primary key (id)
);
/* local users */ /* local users */
create table "user" ( create table "user" (
@ -71,6 +84,8 @@ create table "user" (
autosubscribe integer default 0 /* comment 'automatically subscribe to users who subscribe to us' */, autosubscribe integer default 0 /* comment 'automatically subscribe to users who subscribe to us' */,
urlshorteningservice varchar(50) default 'ur1.ca' /* comment 'service to use for auto-shortening URLs' */, urlshorteningservice varchar(50) default 'ur1.ca' /* comment 'service to use for auto-shortening URLs' */,
inboxed integer default 0 /* comment 'has an inbox been created for this user?' */, inboxed integer default 0 /* comment 'has an inbox been created for this user?' */,
design_id integer /* comment 'id of a design' */references design(id),
viewdesigns integer default 1 /* comment 'whether to view user-provided designs'*/,
created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
modified timestamp /* comment 'date this record was modified' */ modified timestamp /* comment 'date this record was modified' */
@ -184,7 +199,7 @@ create table token (
create table nonce ( create table nonce (
consumer_key varchar(255) not null /* comment 'unique identifier, root URL' */, consumer_key varchar(255) not null /* comment 'unique identifier, root URL' */,
tok char(32) not null /* comment 'identifying value' */, tok char(32) /* comment 'buggy old value, ignored' */,
nonce char(32) null /* comment 'buggy old value, ignored */, nonce char(32) null /* comment 'buggy old value, ignored */,
ts integer not null /* comment 'timestamp sent' values are epoch, and only used internally */, ts integer not null /* comment 'timestamp sent' values are epoch, and only used internally */,
@ -390,6 +405,8 @@ create table user_group (
homepage_logo varchar(255) /* comment 'homepage (profile) size logo' */, homepage_logo varchar(255) /* comment 'homepage (profile) size logo' */,
stream_logo varchar(255) /* comment 'stream-sized logo' */, stream_logo varchar(255) /* comment 'stream-sized logo' */,
mini_logo varchar(255) /* comment 'mini logo' */, mini_logo varchar(255) /* comment 'mini logo' */,
design_id integer /*comment 'id of a design' */ references design(id),
created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
modified timestamp /* comment 'date this record was modified' */ modified timestamp /* comment 'date this record was modified' */
@ -424,7 +441,6 @@ create table group_inbox (
group_id integer not null /* comment 'group receiving the message' references user_group (id) */, group_id integer not null /* comment 'group receiving the message' references user_group (id) */,
notice_id integer not null /* comment 'notice received' references notice (id) */, notice_id integer not null /* comment 'notice received' references notice (id) */,
created timestamp not null default CURRENT_TIMESTAMP /* comment 'date the notice was created' */, created timestamp not null default CURRENT_TIMESTAMP /* comment 'date the notice was created' */,
primary key (group_id, notice_id) primary key (group_id, notice_id)
); );
create index group_inbox_created_idx on group_inbox using btree(created); create index group_inbox_created_idx on group_inbox using btree(created);
@ -439,13 +455,14 @@ create table file (
size integer, size integer,
title varchar(255), title varchar(255),
date integer, date integer,
protected integer protected integer,
filename text /* comment 'if a local file, name of the file' */,
modified timestamp default CURRENT_TIMESTAMP /* comment 'date this record was modified'*/
); );
create sequence file_oembed_seq; create sequence file_oembed_seq;
create table file_oembed ( create table file_oembed (
id bigint default nextval('file_oembed_seq') primary key /* comment 'unique identifier' */, file_id bigint default nextval('file_oembed_seq') primary key /* comment 'unique identifier' */,
file_id bigint unique,
version varchar(20), version varchar(20),
type varchar(20), type varchar(20),
provider varchar(50), provider varchar(50),
@ -461,8 +478,7 @@ create table file_oembed (
create sequence file_redirection_seq; create sequence file_redirection_seq;
create table file_redirection ( create table file_redirection (
id bigint default nextval('file_redirection_seq') primary key /* comment 'unique identifier' */, url varchar(255) primary key,
url varchar(255) unique,
file_id bigint, file_id bigint,
redirections integer, redirections integer,
httpcode integer httpcode integer
@ -470,8 +486,7 @@ create table file_redirection (
create sequence file_thumbnail_seq; create sequence file_thumbnail_seq;
create table file_thumbnail ( create table file_thumbnail (
id bigint default nextval('file_thumbnail_seq') primary key /* comment 'unique identifier' */, file_id bigint primary key,
file_id bigint unique,
url varchar(255) unique, url varchar(255) unique,
width integer, width integer,
height integer height integer
@ -479,26 +494,32 @@ create table file_thumbnail (
create sequence file_to_post_seq; create sequence file_to_post_seq;
create table file_to_post ( create table file_to_post (
id bigint default nextval('file_to_post_seq') primary key /* comment 'unique identifier' */,
file_id bigint, file_id bigint,
post_id bigint, post_id bigint,
unique(file_id, post_id) primary key (file_id, post_id)
); );
create sequence design_seq; create table group_block (
create table design ( group_id integer not null /* comment 'group profile is blocked from' */ references user_group (id),
id bigint default nextval('design_seq') /* comment 'design ID'*/, blocked integer not null /* comment 'profile that is blocked' */references profile (id),
backgroundcolor integer /* comment 'main background color'*/ , blocker integer not null /* comment 'user making the block'*/ references "user" (id),
contentcolor integer /*comment 'content area background color'*/ , modified timestamp /* comment 'date of blocking'*/ ,
sidebarcolor integer /*comment 'sidebar background color'*/ ,
textcolor integer /*comment 'text color'*/ , primary key (group_id, blocked)
linkcolor integer /*comment 'link color'*/,
backgroundimage varchar(255) /*comment 'background image, if any'*/,
disposition int default 1 /*comment 'bit 1 = hide background image, bit 2 = display background image, bit 4 = tile background image'*/,
primary key (id)
); );
create table group_alias (
alias varchar(64) /* comment 'additional nickname for the group'*/ ,
group_id integer not null /* comment 'group profile is blocked from'*/ references user_group (id),
modified timestamp /* comment 'date alias was created'*/,
primary key (alias)
);
create index group_alias_group_id_idx on group_alias (group_id);
/* Textsearch stuff */ /* Textsearch stuff */
create index textsearch_idx on profile using gist(textsearch); create index textsearch_idx on profile using gist(textsearch);

View File

@ -60,4 +60,5 @@ VALUES
(100112, 'Cincinnati Bell Wireless', '%s@gocbw.com', now()), (100112, 'Cincinnati Bell Wireless', '%s@gocbw.com', now()),
(100113, 'T-Mobile Germany', '%s@t-mobile-sms.de', now()), (100113, 'T-Mobile Germany', '%s@t-mobile-sms.de', now()),
(100114, 'Vodafone Germany', '%s@vodafone-sms.de', now()), (100114, 'Vodafone Germany', '%s@vodafone-sms.de', now()),
(100115, 'E-Plus', '%s@smsmail.eplus.de', now()); (100115, 'E-Plus', '%s@smsmail.eplus.de', now()),
(100116, 'Cellular South', '%s@csouth1.com', now());

View File

@ -162,7 +162,7 @@ class Services_oEmbed
} }
if ($this->options[self::OPTION_API] === null) { if ($this->options[self::OPTION_API] === null) {
$this->options[self::OPTION_API] = $this->discover(); $this->options[self::OPTION_API] = $this->discover($url);
} }
} }
@ -319,7 +319,7 @@ class Services_oEmbed
} }
} }
return (isset($ret['json']) ? $ret['json'] : array_pop($ret)); return (isset($ret['application/json']) ? $ret['application/json'] : array_pop($ret));
} }
/** /**

View File

@ -1,12 +1,14 @@
RewriteEngine On <IfModule mod_rewrite.c>
RewriteEngine On
# NOTE: change this to your actual Laconica path; may be "/". # NOTE: change this to your actual Laconica path; may be "/".
RewriteBase /mublog/ RewriteBase /mublog/
RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*) index.php?p=$1 [L,QSA] RewriteRule (.*) index.php?p=$1 [L,QSA]
</IfModule>
<FilesMatch "\.(ini)"> <FilesMatch "\.(ini)">
Order allow,deny Order allow,deny

View File

@ -165,7 +165,8 @@ function main()
if (!$user && common_config('site', 'private') && if (!$user && common_config('site', 'private') &&
!in_array($action, array('login', 'openidlogin', 'finishopenidlogin', !in_array($action, array('login', 'openidlogin', 'finishopenidlogin',
'recoverpassword', 'api', 'doc', 'register'))) { 'recoverpassword', 'api', 'doc', 'register')) &&
!preg_match('/rss$/', $action)) {
common_redirect(common_local_url('login')); common_redirect(common_local_url('login'));
return; return;
} }

View File

@ -43,12 +43,12 @@ function checkPrereqs()
$pass = false; $pass = false;
} }
if (version_compare(PHP_VERSION, '5.0.0', '<')) { if (version_compare(PHP_VERSION, '5.2.3', '<')) {
?><p class="error">Require PHP version 5 or greater.</p><?php ?><p class="error">Require PHP version 5.2.3 or greater.</p><?php
$pass = false; $pass = false;
} }
$reqs = array('gd', 'mysql', 'curl', $reqs = array('gd', 'curl',
'xmlwriter', 'mbstring', 'xmlwriter', 'mbstring',
'gettext'); 'gettext');
@ -58,6 +58,10 @@ function checkPrereqs()
$pass = false; $pass = false;
} }
} }
if (!checkExtension('pgsql') && !checkExtension('mysql')) {
?><p class="error">Cannot find mysql or pgsql extension. You need one or the other: <code><?php echo $req; ?></code></p><?php
$pass = false;
}
if (!is_writable(INSTALLDIR)) { if (!is_writable(INSTALLDIR)) {
?><p class="error">Cannot write config file to: <code><?php echo INSTALLDIR; ?></code></p> ?><p class="error">Cannot write config file to: <code><?php echo INSTALLDIR; ?></code></p>
@ -66,17 +70,16 @@ function checkPrereqs()
$pass = false; $pass = false;
} }
if (!is_writable(INSTALLDIR.'/avatar/')) { // Check the subdirs used for file uploads
?><p class="error">Cannot write avatar directory: <code><?php echo INSTALLDIR; ?>/avatar/</code></p> $fileSubdirs = array('avatar', 'background', 'file');
<p>On your server, try this command: <code>chmod a+w <?php echo INSTALLDIR; ?>/avatar/</code></p> foreach ($fileSubdirs as $fileSubdir) {
$fileFullPath = INSTALLDIR."/$fileSubdir/";
if (!is_writable($fileFullPath)) {
?><p class="error">Cannot write <?php echo $fileSubdir; ?> directory: <code><?php echo $fileFullPath; ?></code></p>
<p>On your server, try this command: <code>chmod a+w <?php echo $fileFullPath; ?></code></p>
<? <?
$pass = false; $pass = false;
} }
if (!is_writable(INSTALLDIR.'/background/')) {
?><p class="error">Cannot write background directory: <code><?php echo INSTALLDIR; ?>/background/</code></p>
<p>On your server, try this command: <code>chmod a+w <?php echo INSTALLDIR; ?>/background/</code></p>
<?
$pass = false;
} }
return $pass; return $pass;
@ -127,7 +130,15 @@ function showForm()
<p class="form_guide">Database hostname</p> <p class="form_guide">Database hostname</p>
</li> </li>
<li> <li>
<label for="host">Database</label>
<label for="dbtype">Type</label>
<input type="radio" name="dbtype" id="fancy-mysql" value="mysql" checked='checked' /> MySQL<br />
<input type="radio" name="dbtype" id="dbtype-pgsql" value="pgsql" /> PostgreSQL<br />
<p class="form_guide">Database type</p>
</li>
<li>
<label for="database">Name</label>
<input type="text" id="database" name="database" /> <input type="text" id="database" name="database" />
<p class="form_guide">Database name</p> <p class="form_guide">Database name</p>
</li> </li>
@ -139,7 +150,7 @@ function showForm()
<li> <li>
<label for="password">Password</label> <label for="password">Password</label>
<input type="password" id="password" name="password" /> <input type="password" id="password" name="password" />
<p class="form_guide">Database password</p> <p class="form_guide">Database password (optional)</p>
</li> </li>
</ul> </ul>
<input type="submit" name="submit" class="submit" value="Submit" /> <input type="submit" name="submit" class="submit" value="Submit" />
@ -163,6 +174,7 @@ function handlePost()
<?php <?php
$host = $_POST['host']; $host = $_POST['host'];
$dbtype = $_POST['dbtype'];
$database = $_POST['database']; $database = $_POST['database'];
$username = $_POST['username']; $username = $_POST['username'];
$password = $_POST['password']; $password = $_POST['password'];
@ -191,10 +203,10 @@ function handlePost()
$fail = true; $fail = true;
} }
if (empty($password)) { // if (empty($password)) {
updateStatus("No password specified.", true); // updateStatus("No password specified.", true);
$fail = true; // $fail = true;
} // }
if (empty($sitename)) { if (empty($sitename)) {
updateStatus("No sitename specified.", true); updateStatus("No sitename specified.", true);
@ -206,8 +218,76 @@ function handlePost()
return; return;
} }
switch($dbtype) {
case 'mysql': mysql_db_installer($host, $database, $username, $password, $sitename);
break;
case 'pgsql': pgsql_db_installer($host, $database, $username, $password, $sitename);
break;
default:
}
if ($path) $path .= '/';
updateStatus("You can visit your <a href='/$path'>new Laconica site</a>.");
?>
<?php
}
function pgsql_db_installer($host, $database, $username, $password, $sitename) {
$connstring = "dbname=$database host=$host user=$username";
//No password would mean trust authentication used.
if (!empty($password)) {
$connstring .= " password=$password";
}
updateStatus("Starting installation..."); updateStatus("Starting installation...");
updateStatus("Checking database..."); updateStatus("Checking database...");
$conn = pg_connect($connstring);
updateStatus("Running database script...");
//wrap in transaction;
pg_query($conn, 'BEGIN');
$res = runDbScript(INSTALLDIR.'/db/laconica_pg.sql', $conn, 'pgsql');
if ($res === false) {
updateStatus("Can't run database script.", true);
showForm();
return;
}
foreach (array('sms_carrier' => 'SMS carrier',
'notice_source' => 'notice source',
'foreign_services' => 'foreign service')
as $scr => $name) {
updateStatus(sprintf("Adding %s data to database...", $name));
$res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn, 'pgsql');
if ($res === false) {
updateStatus(sprintf("Can't run %d script.", $name), true);
showForm();
return;
}
}
pg_query($conn, 'COMMIT');
updateStatus("Writing config file...");
if (empty($password)) {
$sqlUrl = "pgsql://$username@$host/$database";
}
else {
$sqlUrl = "pgsql://$username:$password@$host/$database";
}
$res = writeConf($sitename, $sqlUrl, $fancy, 'pgsql');
if (!$res) {
updateStatus("Can't write config file.", true);
showForm();
return;
}
updateStatus("Done!");
}
function mysql_db_installer($host, $database, $username, $password, $sitename) {
updateStatus("Starting installation...");
updateStatus("Checking database...");
$conn = mysql_connect($host, $username, $password); $conn = mysql_connect($host, $username, $password);
if (!$conn) { if (!$conn) {
updateStatus("Can't connect to server '$host' as '$username'.", true); updateStatus("Can't connect to server '$host' as '$username'.", true);
@ -240,6 +320,7 @@ function handlePost()
return; return;
} }
} }
updateStatus("Writing config file..."); updateStatus("Writing config file...");
$sqlUrl = "mysqli://$username:$password@$host/$database"; $sqlUrl = "mysqli://$username:$password@$host/$database";
$res = writeConf($sitename, $sqlUrl, $fancy); $res = writeConf($sitename, $sqlUrl, $fancy);
@ -249,14 +330,8 @@ function handlePost()
return; return;
} }
updateStatus("Done!"); updateStatus("Done!");
if ($path) $path .= '/'; }
updateStatus("You can visit your <a href='/$path'>new Laconica site</a>."); function writeConf($sitename, $sqlUrl, $fancy, $type='mysql')
?>
<?php
}
function writeConf($sitename, $sqlUrl, $fancy)
{ {
$res = file_put_contents(INSTALLDIR.'/config.php', $res = file_put_contents(INSTALLDIR.'/config.php',
"<?php\n". "<?php\n".
@ -264,11 +339,13 @@ function writeConf($sitename, $sqlUrl, $fancy)
"\$config['site']['name'] = \"$sitename\";\n\n". "\$config['site']['name'] = \"$sitename\";\n\n".
($fancy ? "\$config['site']['fancy'] = true;\n\n":''). ($fancy ? "\$config['site']['fancy'] = true;\n\n":'').
"\$config['db']['database'] = \"$sqlUrl\";\n\n". "\$config['db']['database'] = \"$sqlUrl\";\n\n".
($type == 'pgsql' ? "\$config['db']['quote_identifiers'] = true;\n\n" .
"\$config['db']['type'] = \"$type\";\n\n" : '').
"?>"); "?>");
return $res; return $res;
} }
function runDbScript($filename, $conn) function runDbScript($filename, $conn, $type='mysql')
{ {
$sql = trim(file_get_contents($filename)); $sql = trim(file_get_contents($filename));
$stmts = explode(';', $sql); $stmts = explode(';', $sql);
@ -277,8 +354,13 @@ function runDbScript($filename, $conn)
if (!mb_strlen($stmt)) { if (!mb_strlen($stmt)) {
continue; continue;
} }
if ($type == 'mysql') {
$res = mysql_query($stmt, $conn); $res = mysql_query($stmt, $conn);
} elseif ($type=='pgsql') {
$res = pg_query($conn, $stmt);
}
if ($res === false) { if ($res === false) {
updateStatus("FAILED SQL: $stmt");
return $res; return $res;
} }
} }

View File

@ -19,7 +19,7 @@
if (!defined('LACONICA')) { exit(1); } if (!defined('LACONICA')) { exit(1); }
define('LACONICA_VERSION', '0.9.0dev'); define('LACONICA_VERSION', '0.8.1dev');
define('AVATAR_PROFILE_SIZE', 96); define('AVATAR_PROFILE_SIZE', 96);
define('AVATAR_STREAM_SIZE', 48); define('AVATAR_STREAM_SIZE', 48);

View File

@ -24,6 +24,7 @@ if (!defined('LACONICA')) {
class Daemon class Daemon
{ {
var $daemonize = true; var $daemonize = true;
var $_id = 'generic';
function __construct($daemonize = true) function __construct($daemonize = true)
{ {
@ -35,6 +36,16 @@ class Daemon
return null; return null;
} }
function get_id()
{
return $this->_id;
}
function set_id($id)
{
$this->_id = $id;
}
function background() function background()
{ {
$pid = pcntl_fork(); $pid = pcntl_fork();

View File

@ -55,7 +55,8 @@ class DBQueueManager extends QueueManager
{ {
while (true) { while (true) {
$this->_log(LOG_DEBUG, 'Checking for notices...'); $this->_log(LOG_DEBUG, 'Checking for notices...');
$notice = $this->_nextItem($queue, null); $timeout = $handler->timeout();
$notice = $this->_nextItem($queue, $timeout);
if (empty($notice)) { if (empty($notice)) {
$this->_log(LOG_DEBUG, 'No notices waiting; idling.'); $this->_log(LOG_DEBUG, 'No notices waiting; idling.');
// Nothing in the queue. Do you // Nothing in the queue. Do you
@ -87,7 +88,9 @@ class DBQueueManager extends QueueManager
do { do {
$qi = Queue_item::top($queue); $qi = Queue_item::top($queue);
if (!empty($qi)) { if (empty($qi)) {
sleep(1);
} else {
$notice = Notice::staticGet('id', $qi->notice_id); $notice = Notice::staticGet('id', $qi->notice_id);
if (!empty($notice)) { if (!empty($notice)) {
$result = $notice; $result = $notice;

View File

@ -48,7 +48,7 @@ class GroupsByMembersSection extends GroupSection
$qry = 'SELECT user_group.*, count(*) as value ' . $qry = 'SELECT user_group.*, count(*) as value ' .
'FROM user_group JOIN group_member '. 'FROM user_group JOIN group_member '.
'ON user_group.id = group_member.group_id ' . 'ON user_group.id = group_member.group_id ' .
'GROUP BY user_group.id,user_group.nickname,user_group.fullname,user_group.homepage,user_group.description,user_group.location,user_group.original_logo,user_group.homepage_logo,user_group.stream_logo,user_group.mini_logo,user_group.created,user_group.modified ' . 'GROUP BY user_group.id,user_group.nickname,user_group.fullname,user_group.homepage,user_group.description,user_group.location,user_group.original_logo,user_group.homepage_logo,user_group.stream_logo,user_group.mini_logo,user_group.created,user_group.modified,user_group.design_id ' .
'ORDER BY value DESC '; 'ORDER BY value DESC ';
$limit = GROUPS_PER_SECTION; $limit = GROUPS_PER_SECTION;

View File

@ -48,7 +48,7 @@ class GroupsByPostsSection extends GroupSection
$qry = 'SELECT user_group.*, count(*) as value ' . $qry = 'SELECT user_group.*, count(*) as value ' .
'FROM user_group JOIN group_inbox '. 'FROM user_group JOIN group_inbox '.
'ON user_group.id = group_inbox.group_id ' . 'ON user_group.id = group_inbox.group_id ' .
'GROUP BY user_group.id,user_group.nickname,user_group.fullname,user_group.homepage,user_group.description,user_group.location,user_group.original_logo,user_group.homepage_logo,user_group.stream_logo,user_group.mini_logo,user_group.created,user_group.modified ' . 'GROUP BY user_group.id,user_group.nickname,user_group.fullname,user_group.homepage,user_group.description,user_group.location,user_group.original_logo,user_group.homepage_logo,user_group.stream_logo,user_group.mini_logo,user_group.created,user_group.modified,user_group.design_id ' .
'ORDER BY value DESC '; 'ORDER BY value DESC ';
$limit = GROUPS_PER_SECTION; $limit = GROUPS_PER_SECTION;

View File

@ -73,7 +73,7 @@ class GroupTagCloudSection extends TagCloudSection
$quoted = array(); $quoted = array();
foreach ($names as $name) { foreach ($names as $name) {
$quoted[] = "\"$name\""; $quoted[] = "'$name'";
} }
$namestring = implode(',', $quoted); $namestring = implode(',', $quoted);

View File

@ -53,7 +53,7 @@ function client_prefered_language($httplang)
if (!empty($httplang[2][$i])) { if (!empty($httplang[2][$i])) {
// if no q default to 1.0 // if no q default to 1.0
$client_langs[$httplang[2][$i]] = $client_langs[$httplang[2][$i]] =
($httplang[6][$i]? (float) $httplang[6][$i] : 1.0); ($httplang[6][$i]? (float) $httplang[6][$i] : 1.0 - ($i*0.01));
} }
if (!empty($httplang[3][$i]) && empty($client_langs[$httplang[3][$i]])) { if (!empty($httplang[3][$i]) && empty($client_langs[$httplang[3][$i]])) {
// if a catchall default 0.01 lower // if a catchall default 0.01 lower

View File

@ -121,7 +121,7 @@ function mail_notify_from()
$domain = mail_domain(); $domain = mail_domain();
$notifyfrom = common_config('site', 'name') .' <noreply@'.$domain.'>'; $notifyfrom = '"'.common_config('site', 'name') .'" <noreply@'.$domain.'>';
} }
return $notifyfrom; return $notifyfrom;

View File

@ -140,6 +140,12 @@ class MessageForm extends Form
'rows' => 4, 'rows' => 4,
'name' => 'content'), 'name' => 'content'),
($this->content) ? $this->content : ''); ($this->content) ? $this->content : '');
$this->out->elementStart('dl', 'form_note');
$this->out->element('dt', null, _('Available characters'));
$this->out->element('dd', array('id' => 'notice_text-count'),
'140');
$this->out->elementEnd('dl');
} }
/** /**

View File

@ -74,11 +74,7 @@ class PopularNoticeSection extends NoticeSection
$offset = 0; $offset = 0;
$limit = NOTICES_PER_SECTION + 1; $limit = NOTICES_PER_SECTION + 1;
if (common_config('db', 'type') == 'pgsql') {
$qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
} else {
$qry .= ' LIMIT ' . $offset . ', ' . $limit;
}
$notice = Memcached_DataObject::cachedQuery('Notice', $notice = Memcached_DataObject::cachedQuery('Notice',
sprintf($qry, common_config('popular', 'dropoff')), sprintf($qry, common_config('popular', 'dropoff')),

View File

@ -29,7 +29,6 @@ define('QUEUE_HANDLER_HIT_IDLE', 0);
class QueueHandler extends Daemon class QueueHandler extends Daemon
{ {
var $_id = 'generic';
function __construct($id=null, $daemonize=true) function __construct($id=null, $daemonize=true)
{ {
@ -55,16 +54,6 @@ class QueueHandler extends Daemon
return strtolower($this->class_name().'.'.$this->get_id()); return strtolower($this->class_name().'.'.$this->get_id());
} }
function get_id()
{
return $this->_id;
}
function set_id($id)
{
$this->_id = $id;
}
function transport() function transport()
{ {
return null; return null;

View File

@ -129,6 +129,11 @@ class Router
$m->connect('index.php?action=' . $action, array('action' => $action)); $m->connect('index.php?action=' . $action, array('action' => $action));
} }
$m->connect('main/:method',
array('action' => 'api',
'method' => 'oembed(.xml|.json)?',
'apiaction' => 'oembed'));
// settings // settings
foreach (array('profile', 'avatar', 'password', 'openid', 'im', foreach (array('profile', 'avatar', 'password', 'openid', 'im',
@ -206,7 +211,7 @@ class Router
array('tag' => '[a-zA-Z0-9]+')); array('tag' => '[a-zA-Z0-9]+'));
$m->connect('tag/:tag', $m->connect('tag/:tag',
array('action' => 'tag'), array('action' => 'tag'),
array('tag' => '[a-zA-Z0-9]+')); array('tag' => '[\pL\pN_\-\.]{1,64}'));
$m->connect('peopletag/:tag', $m->connect('peopletag/:tag',
array('action' => 'peopletag'), array('action' => 'peopletag'),
@ -390,6 +395,10 @@ class Router
// laconica // laconica
$m->connect('api/laconica/:method',
array('action' => 'api',
'apiaction' => 'laconica'));
$m->connect('api/laconica/:method', $m->connect('api/laconica/:method',
array('action' => 'api', array('action' => 'api',
'apiaction' => 'laconica')); 'apiaction' => 'laconica'));

View File

@ -39,6 +39,7 @@ class Rss10Action extends Action
var $creators = array(); var $creators = array();
var $limit = DEFAULT_RSS_LIMIT; var $limit = DEFAULT_RSS_LIMIT;
var $notices = null; var $notices = null;
var $tags_already_output = array();
/** /**
* Constructor * Constructor
@ -96,15 +97,48 @@ class Rss10Action extends Action
{ {
// Parent handling, including cache check // Parent handling, including cache check
parent::handle($args); parent::handle($args);
// Get the list of notices
if (empty($this->tag)) { if (common_config('site', 'private')) {
$this->notices = $this->getNotices($this->limit); if (!isset($_SERVER['PHP_AUTH_USER'])) {
# This header makes basic auth go
header('WWW-Authenticate: Basic realm="Laconica RSS"');
# If the user hits cancel -- bam!
$this->show_basic_auth_error();
return;
} else { } else {
$this->notices = $this->getTaggedNotices($this->tag, $this->limit); $nickname = $_SERVER['PHP_AUTH_USER'];
$password = $_SERVER['PHP_AUTH_PW'];
if (!common_check_user($nickname, $password)) {
# basic authentication failed
list($proxy, $ip) = common_client_ip();
common_log(LOG_WARNING, "Failed RSS auth attempt, nickname = $nickname, proxy = $proxy, ip = $ip.");
$this->show_basic_auth_error();
return;
} }
}
}
// Get the list of notices
$this->notices = $this->getNotices($this->limit);
$this->showRss(); $this->showRss();
} }
function show_basic_auth_error()
{
header('HTTP/1.1 401 Unauthorized');
header('Content-Type: application/xml; charset=utf-8');
$this->startXML();
$this->elementStart('hash');
$this->element('error', null, 'Could not authenticate you.');
$this->element('request', null, $_SERVER['REQUEST_URI']);
$this->elementEnd('hash');
$this->endXML();
}
/** /**
* Get the notices to output in this stream * Get the notices to output in this stream
* *
@ -192,24 +226,6 @@ class Rss10Action extends Action
} }
} }
// XXX: Surely there should be a common function to do this?
function extract_tags ($string)
{
$count = preg_match_all('/(?:^|\s)#([A-Za-z0-9_\-\.]{1,64})/', strtolower($string), $match);
if (!count)
{
return array();
}
$rv = array();
foreach ($match[1] as $tag)
{
$rv[] = common_canonical_tag($tag);
}
return array_unique($rv);
}
function showItem($notice) function showItem($notice)
{ {
$profile = Profile::staticGet($notice->profile_id); $profile = Profile::staticGet($notice->profile_id);
@ -234,6 +250,11 @@ class Rss10Action extends Action
$replyurl = common_local_url('shownotice', array('notice' => $notice->reply_to)); $replyurl = common_local_url('shownotice', array('notice' => $notice->reply_to));
$this->element('sioc:reply_of', array('rdf:resource' => $replyurl)); $this->element('sioc:reply_of', array('rdf:resource' => $replyurl));
} }
if (!empty($notice->conversation)) {
$conversationurl = common_local_url('conversation',
array('id' => $notice->conversation));
$this->element('sioc:has_discussion', array('rdf:resource' => $conversationurl));
}
$attachments = $notice->attachments(); $attachments = $notice->attachments();
if($attachments){ if($attachments){
foreach($attachments as $attachment){ foreach($attachments as $attachment){
@ -263,18 +284,28 @@ class Rss10Action extends Action
$this->element('sioc:links_to', array('rdf:resource'=>$attachment->url)); $this->element('sioc:links_to', array('rdf:resource'=>$attachment->url));
} }
} }
$tags = $this->extract_tags($notice->content);
if (!empty($tags)) { $tag = new Notice_tag();
foreach ($tags as $tag) $tag->notice_id = $notice->id;
{ if ($tag->find()) {
$tagpage = common_local_url('tag', array('tag' => $tag)); $entry['tags']=array();
$tagrss = common_local_url('tagrss', array('tag' => $tag)); while ($tag->fetch()) {
$tagpage = common_local_url('tag', array('tag' => $tag->tag));
if ( in_array($tag, $this->tags_already_output) ) {
$this->element('ctag:tagged', array('rdf:resource'=>$tagpage.'#concept'));
continue;
}
$tagrss = common_local_url('tagrss', array('tag' => $tag->tag));
$this->elementStart('ctag:tagged'); $this->elementStart('ctag:tagged');
$this->elementStart('ctag:Tag', array('rdf:about'=>$tagpage.'#concept', 'ctag:label'=>$tag)); $this->elementStart('ctag:Tag', array('rdf:about'=>$tagpage.'#concept', 'ctag:label'=>$tag->tag));
$this->element('foaf:page', array('rdf:resource'=>$tagpage)); $this->element('foaf:page', array('rdf:resource'=>$tagpage));
$this->element('rdfs:seeAlso', array('rdf:resource'=>$tagrss)); $this->element('rdfs:seeAlso', array('rdf:resource'=>$tagrss));
$this->elementEnd('ctag:Tag'); $this->elementEnd('ctag:Tag');
$this->elementEnd('ctag:tagged'); $this->elementEnd('ctag:tagged');
$this->tags_already_output[] = $tag->tag;
} }
} }
$this->elementEnd('item'); $this->elementEnd('item');
@ -320,6 +351,8 @@ class Rss10Action extends Action
'http://rdfs.org/sioc/ns#', 'http://rdfs.org/sioc/ns#',
'xmlns:sioct' => 'xmlns:sioct' =>
'http://rdfs.org/sioc/types#', 'http://rdfs.org/sioc/types#',
'xmlns:rdfs' =>
'http://www.w3.org/2000/01/rdf-schema#',
'xmlns:laconica' => 'xmlns:laconica' =>
'http://laconi.ca/ont/', 'http://laconi.ca/ont/',
'xmlns' => 'http://purl.org/rss/1.0/')); 'xmlns' => 'http://purl.org/rss/1.0/'));

View File

@ -186,19 +186,22 @@ class TwitterapiAction extends Action
$twitter_status['favorited'] = false; $twitter_status['favorited'] = false;
} }
# Enclosures // Enclosures
$attachments = $notice->attachments(); $attachments = $notice->attachments();
$twitter_status['attachments']=array(); $enclosures = array();
if($attachments){
foreach($attachments as $attachment){ foreach ($attachments as $attachment) {
if ($attachment->isEnclosure()) { if ($attachment->isEnclosure()) {
$enclosure=array(); $enclosure = array();
$enclosure['url']=$attachment->url; $enclosure['url'] = $attachment->url;
$enclosure['mimetype']=$attachment->mimetype; $enclosure['mimetype'] = $attachment->mimetype;
$enclosure['size']=$attachment->size; $enclosure['size'] = $attachment->size;
$twitter_status['attachments'][]=$enclosure; $enclosures[] = $enclosure;
} }
} }
if (!empty($enclosures)) {
$twitter_status['attachments'] = $enclosures;
} }
if ($include_user) { if ($include_user) {
@ -215,7 +218,7 @@ class TwitterapiAction extends Action
$profile = $notice->getProfile(); $profile = $notice->getProfile();
$entry = array(); $entry = array();
# We trim() to avoid extraneous whitespace in the output // We trim() to avoid extraneous whitespace in the output
$entry['content'] = common_xml_safe_str(trim($notice->rendered)); $entry['content'] = common_xml_safe_str(trim($notice->rendered));
$entry['title'] = $profile->nickname . ': ' . common_xml_safe_str(trim($notice->content)); $entry['title'] = $profile->nickname . ': ' . common_xml_safe_str(trim($notice->content));
@ -228,7 +231,26 @@ class TwitterapiAction extends Action
$entry['updated'] = $entry['published']; $entry['updated'] = $entry['published'];
$entry['author'] = $profile->getBestName(); $entry['author'] = $profile->getBestName();
# Enclosure // Enclosures
$attachments = $notice->attachments();
$enclosures = array();
foreach ($attachments as $attachment) {
if ($attachment->isEnclosure()) {
$enclosure = array();
$enclosure['url'] = $attachment->url;
$enclosure['mimetype'] = $attachment->mimetype;
$enclosure['size'] = $attachment->size;
$enclosures[] = $enclosure;
}
}
if (!empty($enclosures)) {
$entry['enclosures'] = $enclosures;
}
/*
// Enclosure
$attachments = $notice->attachments(); $attachments = $notice->attachments();
if($attachments){ if($attachments){
$entry['enclosures']=array(); $entry['enclosures']=array();
@ -242,8 +264,20 @@ class TwitterapiAction extends Action
} }
} }
} }
*/
# RSS Item specific // Tags/Categories
$tag = new Notice_tag();
$tag->notice_id = $notice->id;
if ($tag->find()) {
$entry['tags']=array();
while ($tag->fetch()) {
$entry['tags'][]=$tag->tag;
}
}
$tag->free();
// RSS Item specific
$entry['description'] = $entry['content']; $entry['description'] = $entry['content'];
$entry['pubDate'] = common_date_rfc2822($notice->created); $entry['pubDate'] = common_date_rfc2822($notice->created);
$entry['guid'] = $entry['link']; $entry['guid'] = $entry['link'];
@ -369,6 +403,9 @@ class TwitterapiAction extends Action
case 'text': case 'text':
$this->element($element, null, common_xml_safe_str($value)); $this->element($element, null, common_xml_safe_str($value));
break; break;
case 'attachments':
$this->show_xml_attachments($twitter_status['attachments']);
break;
default: default:
$this->element($element, null, $value); $this->element($element, null, $value);
} }
@ -389,6 +426,20 @@ class TwitterapiAction extends Action
$this->elementEnd($role); $this->elementEnd($role);
} }
function show_xml_attachments($attachments) {
if (!empty($attachments)) {
$this->elementStart('attachments', array('type' => 'array'));
foreach ($attachments as $attachment) {
$attrs = array();
$attrs['url'] = $attachment['url'];
$attrs['mimetype'] = $attachment['mimetype'];
$attrs['size'] = $attachment['size'];
$this->element('enclosure', $attrs, '');
}
$this->elementEnd('attachments');
}
}
function show_twitter_rss_item($entry) function show_twitter_rss_item($entry)
{ {
$this->elementStart('item'); $this->elementStart('item');
@ -404,6 +455,12 @@ class TwitterapiAction extends Action
$this->element('enclosure', array('url'=>$enclosure['url'],'type'=>$enclosure['mimetype'],'length'=>$enclosure['size']), null); $this->element('enclosure', array('url'=>$enclosure['url'],'type'=>$enclosure['mimetype'],'length'=>$enclosure['size']), null);
} }
if($entry['tags']){
foreach($entry['tags'] as $tag){
$this->element('category', null,$tag);
}
}
$this->elementEnd('item'); $this->elementEnd('item');
} }

View File

@ -404,7 +404,7 @@ function common_render_text($text)
$r = preg_replace('/[\x{0}-\x{8}\x{b}-\x{c}\x{e}-\x{19}]/', '', $r); $r = preg_replace('/[\x{0}-\x{8}\x{b}-\x{c}\x{e}-\x{19}]/', '', $r);
$r = common_replace_urls_callback($r, 'common_linkify'); $r = common_replace_urls_callback($r, 'common_linkify');
$r = preg_replace('/(^|\(|\[|\s+)#([A-Za-z0-9_\-\.]{1,64})/e', "'\\1#'.common_tag_link('\\2')", $r); $r = preg_replace('/(^|\(|\[|\s+)#([\pL\pN_\-\.]{1,64})/e', "'\\1#'.common_tag_link('\\2')", $r);
// XXX: machine tags // XXX: machine tags
return $r; return $r;
} }
@ -414,9 +414,9 @@ function common_replace_urls_callback($text, $callback, $notice_id = null) {
$regex = '#'. $regex = '#'.
'(?:'. '(?:'.
'(?:'. '(?:'.
'(?:https?|ftps?|mms|rtsp|gopher|news|nntp|telnet|wais|file|prospero|webcal|xmpp|irc)://'. '(?:https?|ftps?|mms|rtsp|gopher|news|nntp|telnet|wais|file|prospero|webcal|irc)://'.
'|'. '|'.
'(?:mailto|aim|tel):'. '(?:mailto|aim|tel|xmpp):'.
')'. ')'.
'[^.\s]+\.[^\s]+'. '[^.\s]+\.[^\s]+'.
'|'. '|'.

View File

@ -87,18 +87,25 @@ class PiwikAnalyticsPlugin extends Plugin
function onEndShowScripts($action) function onEndShowScripts($action)
{ {
$js1 = 'var pkBaseURL = (("https:" == document.location.protocol) ? "https://'. $piwikCode = <<<ENDOFPIWIK
$this->piwikroot.'" : "http://'.$this->piwikroot.
'"); document.write(unescape("%3Cscript src=\'" + pkBaseURL + "piwik.js\''. <!-- Piwik -->
' type=\'text/javascript\'%3E%3C/script%3E"));'; <script type="text/javascript">
$js2 = 'piwik_action_name = ""; piwik_idsite = '.$this->piwikid. var pkBaseURL = (("https:" == document.location.protocol) ? "https://{$this->piwikroot}" : "http://{$this->piwikroot}");
'; piwik_url = pkBaseURL + "piwik.php"; piwik_log(piwik_action_name, piwik_idsite, piwik_url);'; document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/javascript'%3E%3C/script%3E"));
$action->elementStart('script', array('type' => 'text/javascript')); </script>
$action->raw($js1); <script type="text/javascript">
$action->elementEnd('script'); try {
$action->elementStart('script', array('type' => 'text/javascript')); var piwikTracker = Piwik.getTracker(pkBaseURL + "piwik.php", 4);
$action->raw($js2); piwikTracker.trackPageView();
$action->elementEnd('script'); piwikTracker.enableLinkTracking();
} catch( err ) {}
</script>
<!-- End Piwik Tag -->
ENDOFPIWIK;
$action->raw($piwikCode);
return true; return true;
} }
} }

View File

@ -28,7 +28,8 @@
define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
$helptext = <<<ENDOFHELP $helptext = <<<ENDOFHELP
getvaliddaemons.php - print out the currently configured PID directory getvaliddaemons.php - print out a list of valid daemons that should be started
by the startdaemons script
ENDOFHELP; ENDOFHELP;

View File

@ -299,25 +299,40 @@ class MailerDaemon
$attachments = array(); $attachments = array();
$this->extract_part($parsed,$msg,$attachments);
return array($from, $to, $msg, $attachments);
}
function extract_part($parsed,&$msg,&$attachments){
if ($parsed->ctype_primary == 'multipart') { if ($parsed->ctype_primary == 'multipart') {
foreach ($parsed->parts as $part) { if($parsed->ctype_secondary == 'alternative'){
if ($part->ctype_primary == 'text' && $altmsg = $this->extract_msg_from_multipart_alternative_part($parsed);
$part->ctype_secondary == 'plain') { if(!empty($altmsg)) $msg = $altmsg;
$msg = $part->body;
}else{ }else{
if ($part->body) { foreach($parsed->parts as $part){
$this->extract_part($part,$msg,$attachments);
}
}
} else if ($parsed->ctype_primary == 'text'
&& $parsed->ctype_secondary=='plain') {
$msg = $parsed->body;
}else if(!empty($parsed->body)){
if(common_config('attachments', 'uploads')){
//only save attachments if uploads are enabled
$attachment = tmpfile(); $attachment = tmpfile();
fwrite($attachment, $part->body); fwrite($attachment, $parsed->body);
$attachments[] = $attachment; $attachments[] = $attachment;
} }
} }
} }
} else if ($type == 'text/plain') {
$msg = $parsed->body; function extract_msg_from_multipart_alternative_part($parsed){
} else { foreach ($parsed->parts as $part) {
$this->unsupported_type($type); $this->extract_part($part,$msg,$attachments);
} }
return array($from, $to, $msg, $attachments); //we don't want any attachments that are a result of this parsing
return $msg;
} }
function unsupported_type($type) function unsupported_type($type)

View File

@ -25,19 +25,18 @@ define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
define('MAXCHILDREN', 2); define('MAXCHILDREN', 2);
define('POLL_INTERVAL', 60); // in seconds define('POLL_INTERVAL', 60); // in seconds
$shortoptions = 'i::'; $shortoptions = 'di::';
$longoptions = array('id::'); $longoptions = array('id::', 'debug');
$helptext = <<<END_OF_TRIM_HELP $helptext = <<<END_OF_TRIM_HELP
Batch script for retrieving Twitter messages from foreign service. Batch script for retrieving Twitter messages from foreign service.
-i --id Identity (default 'generic') -i --id Identity (default 'generic')
-d --debug Debug (lots of log output)
END_OF_TRIM_HELP; END_OF_TRIM_HELP;
require_once INSTALLDIR.'/scripts/commandline.inc'; require_once INSTALLDIR .'/scripts/commandline.inc';
require_once INSTALLDIR . '/lib/common.php';
require_once INSTALLDIR . '/lib/daemon.php'; require_once INSTALLDIR . '/lib/daemon.php';
/** /**
@ -61,6 +60,15 @@ class TwitterStatusFetcher extends Daemon
{ {
private $_children = array(); private $_children = array();
function __construct($id=null, $daemonize=true)
{
parent::__construct($daemonize);
if ($id) {
$this->set_id($id);
}
}
/** /**
* Name of this daemon * Name of this daemon
* *
@ -80,6 +88,11 @@ class TwitterStatusFetcher extends Daemon
function run() function run()
{ {
if (defined('SCRIPT_DEBUG')) {
common_debug($this->name() .
': debugging log output enabled.');
}
do { do {
$flinks = $this->refreshFlinks(); $flinks = $this->refreshFlinks();
@ -317,7 +330,7 @@ class TwitterStatusFetcher extends Daemon
$notice->rendered = common_render_content($notice->content, $notice); $notice->rendered = common_render_content($notice->content, $notice);
$notice->source = 'twitter'; $notice->source = 'twitter';
$notice->reply_to = null; // XXX lookup reply $notice->reply_to = null; // XXX lookup reply
$notice->is_local = NOTICE_GATEWAY; $notice->is_local = Notice::GATEWAY;
if (Event::handle('StartNoticeSave', array(&$notice))) { if (Event::handle('StartNoticeSave', array(&$notice))) {
$id = $notice->insert(); $id = $notice->insert();
@ -640,6 +653,10 @@ if (have_option('i')) {
$id = null; $id = null;
} }
if (have_option('d') || have_option('debug')) {
define('SCRIPT_DEBUG', true);
}
$fetcher = new TwitterStatusFetcher($id); $fetcher = new TwitterStatusFetcher($id);
$fetcher->runOnce(); $fetcher->runOnce();

View File

@ -175,6 +175,10 @@ class XMPPDaemon extends Daemon
$user = $this->get_user($from); $user = $this->get_user($from);
// For common_current_user to work
global $_cur;
$_cur = $user;
if (!$user) { if (!$user) {
$this->from_site($from, 'Unknown user; go to ' . $this->from_site($from, 'Unknown user; go to ' .
common_local_url('imsettings') . common_local_url('imsettings') .
@ -211,6 +215,7 @@ class XMPPDaemon extends Daemon
$user->free(); $user->free();
unset($user); unset($user);
unset($_cur);
unset($pl['xml']); unset($pl['xml']);
$pl['xml'] = null; $pl['xml'] = null;

View File

@ -482,7 +482,7 @@ height:16px;
} }
#form_notice .form_note { #form_notice .form_note {
position:absolute; position:absolute;
top:99px; bottom:2px;
right:98px; right:98px;
z-index:9; z-index:9;
} }
@ -863,7 +863,7 @@ clear:left;
float:left; float:left;
font-size:0.95em; font-size:0.95em;
margin-left:59px; margin-left:59px;
width:60%; width:50%;
} }
#showstream .notice div.entry-content, #showstream .notice div.entry-content,
#shownotice .notice div.entry-content { #shownotice .notice div.entry-content {

View File

@ -12,7 +12,7 @@ margin:0 auto;
} }
#content { #content {
width:70%; width:69%;
} }
#aside_primary { #aside_primary {
padding:5%; padding:5%;

View File

@ -12,12 +12,12 @@ img { display:block; border:0; }
a abbr { cursor: pointer; border-bottom:0; } a abbr { cursor: pointer; border-bottom:0; }
table { border-collapse:collapse; } table { border-collapse:collapse; }
ol { list-style-position:inside; } ol { list-style-position:inside; }
html { font-size: 100%; background-color:#fff; } html { font-size: 87.5%; }
body { body {
background-color:#fff; background-color:#fff;
color:#000; color:#000;
font-family:sans-serif; font-family:sans-serif;
font-size:0.75em; font-size:1em;
line-height:normal; line-height:normal;
position:relative; position:relative;
height:100%; height:100%;
@ -471,6 +471,24 @@ margin-bottom:7px;
#form_notice #notice_submit label { #form_notice #notice_submit label {
display:none; display:none;
} }
#form_notice label[for=notice_data-attach],
#form_notice #notice_data-attach {
position:absolute;
top:87px;
cursor:pointer;
}
#form_notice label[for=notice_data-attach] {
text-indent:-9999px;
left:82%;
width:16px;
height:16px;
}
#form_notice #notice_data-attach {
left:34.6%;
padding:0;
height:16px;
}
#form_notice .form_note { #form_notice .form_note {
position:absolute; position:absolute;
top:-10px; top:-10px;
@ -488,6 +506,7 @@ font-weight:bold;
line-height:1.15; line-height:1.15;
padding:1px 2px; padding:1px 2px;
} }
#form_notice #notice_action-submit { #form_notice #notice_action-submit {
width:14%; width:14%;
height:35px; height:35px;
@ -505,6 +524,26 @@ margin-bottom:7px;
margin-left:18px; margin-left:18px;
float:left; float:left;
} }
#form_notice .error,
#form_notice .success {
float:left;
clear:both;
width:81.5%;
margin-bottom:0;
line-height:1.618;
}
#form_notice #notice_data-attach_selected code {
float:left;
width:90%;
display:block;
font-size:1.1em;
line-height:1.8;
overflow:auto;
}
#form_notice #notice_data-attach_selected button {
float:right;
font-size:0.8em;
}
/* entity_profile */ /* entity_profile */
@ -746,17 +785,18 @@ float:left;
width:100%; width:100%;
border-top-width:1px; border-top-width:1px;
border-top-style:dotted; border-top-style:dotted;
font-size:1.2em;
} }
.notices li { .notices li {
list-style-type:none; list-style-type:none;
line-height:1.1; line-height:1.1;
width:94%;
padding-right:5%;
padding-left:1%;
min-height:47px; min-height:47px;
} }
.notices .notices {
margin-top:7px;
margin-left:2%;
width:98%;
float:left;
}
/* NOTICES */ /* NOTICES */
#notices_primary { #notices_primary {
@ -805,13 +845,17 @@ text-decoration:underline;
.notice .entry-title { .notice .entry-title {
float:none; float:none;
display:inline;
width:100%;
overflow:hidden; overflow:hidden;
display:inline;
} }
#shownotice .notice .entry-title { #shownotice .notice .entry-title {
font-size:2.2em; font-size:2.2em;
} }
#conversation .notice .entry-title {
display:block;
width:90%;
}
.notice p.entry-content { .notice p.entry-content {
display:inline; display:inline;
@ -887,13 +931,12 @@ outline:none;
.notice-options { .notice-options {
padding-left:2%; padding-left:2%;
float:left; float:left;
width:50%;
font-size:0.95em; font-size:0.95em;
width:12.5%; width:16px;
float:right; float:right;
display:none; display:none;
} }
.notices li.hover div.notice-options { .notices li:hover div.notice-options {
display:block; display:block;
} }
@ -917,7 +960,7 @@ top:30px;
right:7px; right:7px;
} }
.notice-options .notice_delete { .notice-options .notice_delete {
bottom:7px; top:47px;
right:7px; right:7px;
} }
.notice-options .notice_reply dt { .notice-options .notice_reply dt {
@ -958,6 +1001,97 @@ border:0;
padding:0; 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:425px;
}
#jOverlayContent #content {
border-radius:7px;
-moz-border-radius:7px;
-webkit-border-radius:7px;
}
#jOverlayLoading {
top:5%;
left:40%;
}
#attachment_view img {
max-width:480px;
max-height:480px;
}
#attachment_view #oembed_info {
margin-top:11px;
}
#attachment_view #oembed_info dt,
#attachment_view #oembed_info dd {
float:left;
}
#attachment_view #oembed_info dt {
clear:left;
margin-right:11px;
font-weight:bold;
}
#attachment_view #oembed_info dt:after {
content: ":";
}
#usergroups #new_group { #usergroups #new_group {
float: left; float: left;
@ -1218,7 +1352,8 @@ clear:both;
#outbox.user_in #content, #outbox.user_in #content,
#subscriptions.user_in #content, #subscriptions.user_in #content,
#subscribers.user_in #content, #subscribers.user_in #content,
#showgroup.user_in #content { #showgroup.user_in #content,
#conversation.user_in #content {
padding-top:160px; padding-top:160px;
} }
@ -1243,6 +1378,14 @@ padding-top:160px;
display:none; display:none;
} }
#jOverlayContent #core #content {
padding-top:11px;
}
#jOverlayContent #core {
background:none;
padding-top:0;
}
html, html,
body, body,
@ -1311,24 +1454,23 @@ border-top-color:#87B4C8;
} }
#content .notice p.entry-content a:visited {
background-color:#fcfcfc;
}
#content .notice p.entry-content .vcard a {
background-color:#fcfffc;
}
#aside_primary { #aside_primary {
background-color:#DDFFCC; background-color:#DDFFCC;
} }
#notice_text-count { #notice_text-count {
color:#333; color:#000000;
} }
#form_notice.warning #notice_text-count { #form_notice.warning #notice_text-count {
color:#000; color:#000000;
} }
#form_notice label[for=notice_data-attach] {
background:transparent url(../../base/images/icons/twotone/green/clip-01.gif) no-repeat 0 45%;
}
#form_notice #notice_data-attach {
opacity:0;
}
#form_notice.processing #notice_action-submit { #form_notice.processing #notice_action-submit {
background:#fff url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%; background:#fff url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%;
cursor:wait; cursor:wait;
@ -1426,8 +1568,11 @@ background-image:url(../images/icons/twotone/green/shield.gif);
/* NOTICES */ /* NOTICES */
.notices li.over { .notice .attachment {
background-color:#fcfcfc; 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,
@ -1451,10 +1596,24 @@ background:transparent url(../images/icons/icon_trash.gif) no-repeat 0 45%;
.notices div.notice-options { .notices div.notice-options {
opacity:0.4; opacity:0.4;
} }
.notices li.hover div.entry-content, .notices li:hover div.entry-content,
.notices li.hover div.notice-options { .notices li:hover div.notice-options {
opacity:1; opacity:1;
} }
.notices .notices {
background-color:rgba(200, 200, 200, 0.01);
}
.notices .notices .notices {
background-color:rgba(200, 200, 200, 0.02);
}
.notices .notices .notices .notices {
background-color:rgba(200, 200, 200, 0.03);
}
.notices .notices .notices .notices .notices {
background-color:rgba(200, 200, 200, 0.04);
}
div.entry-content { div.entry-content {
color:#333; color:#333;
} }
@ -1462,8 +1621,11 @@ div.notice-options a,
div.notice-options input { div.notice-options input {
font-family:sans-serif; font-family:sans-serif;
} }
.notices li.hover { #content .notices li:hover {
background-color:#fcfcfc; background-color:rgba(240, 240, 240, 0.2);
}
#conversation .notices li:hover {
background-color:transparent;
} }
/*END: NOTICES */ /*END: NOTICES */

View File

@ -8,6 +8,16 @@ color:#fff;
background-color:#ddffcc; background-color:#ddffcc;
} }
#form_notice .form_note + label {
position:absolute;
top:25px;
left:83%;
text-indent:-9999px;
height:16px;
width:16px;
display:block;
}
#aside_primary { #aside_primary {
width:181px; width:181px;
} }
@ -32,3 +42,13 @@ top:158px;
#subscribers #content { #subscribers #content {
padding-top:138px; padding-top:138px;
} }
.notice {
z-index:1;
}
.notice:hover {
z-index:9999;
}
.notice .thumbnail img {
z-index:9999;
}

View File

@ -383,7 +383,7 @@ margin-bottom:1em;
} }
#content { #content {
width:49.009%; width:50%;
min-height:259px; min-height:259px;
float:left; float:left;
padding:0 18px; padding:0 18px;
@ -402,7 +402,7 @@ float:left;
width:45.917%; width:45.917%;
min-height:259px; min-height:259px;
float:left; float:left;
margin-left:1.385%; margin-left:0.25%;
padding-bottom:47px; padding-bottom:47px;
} }
@ -736,11 +736,10 @@ margin-right:11px;
.notice, .notice,
.profile { .profile {
position:relative; position:relative;
padding-top:11px; padding:11px 2%;
padding-bottom:11px;
clear:both; clear:both;
float:left; float:left;
width:96.41%; width:95.7%;
border-width:1px; border-width:1px;
border-style:solid; border-style:solid;
margin-bottom:11px; margin-bottom:11px;
@ -993,13 +992,36 @@ font-weight:bold;
padding:0; padding:0;
} }
#jOverlayContent h1 { #jOverlayContent h1 {
max-width:475px; max-width:425px;
} }
#jOverlayContent #content { #jOverlayContent #content {
border-radius:7px; border-radius:7px;
-moz-border-radius:7px; -moz-border-radius:7px;
-webkit-border-radius:7px; -webkit-border-radius:7px;
} }
#jOverlayLoading {
top:5%;
left:40%;
}
#attachment_view img {
max-width:480px;
max-height:480px;
}
#attachment_view #oembed_info {
margin-top:11px;
}
#attachment_view #oembed_info dt,
#attachment_view #oembed_info dd {
float:left;
}
#attachment_view #oembed_info dt {
clear:left;
margin-right:11px;
font-weight:bold;
}
#attachment_view #oembed_info dt:after {
content: ":";
}
#usergroups #new_group { #usergroups #new_group {
float: left; float: left;
@ -1058,8 +1080,6 @@ top:3px;
left:3px; left:3px;
} }
.pagination { .pagination {
float:left; float:left;
clear:both; clear:both;
@ -1105,7 +1125,6 @@ padding-right:30px;
} }
/* END: NOTICE */ /* END: NOTICE */
.hentry .entry-content p { .hentry .entry-content p {
margin-bottom:18px; margin-bottom:18px;
} }
@ -1122,7 +1141,6 @@ margin-bottom:18px;
margin-left:18px; margin-left:18px;
} }
/* TOP_POSTERS */ /* TOP_POSTERS */
.section tbody td { .section tbody td {
padding-right:11px; padding-right:11px;
@ -1150,7 +1168,6 @@ margin-right:0;
display:none; display:none;
} }
/* tagcloud */ /* tagcloud */
.tag-cloud { .tag-cloud {
list-style-type:none; list-style-type:none;
@ -1233,6 +1250,11 @@ clear:both;
margin-bottom:0; margin-bottom:0;
} }
#form_settings_design #settings_design_background-image img {
max-width:480px;
max-height:480px;
}
#form_settings_design #settings_design_color .form_data, #form_settings_design #settings_design_color .form_data,
#form_settings_design #color-picker { #form_settings_design #color-picker {
float:left; float:left;

View File

@ -14,7 +14,8 @@ background:url(../images/illustrations/illu_pigeons-01.png) no-repeat 0 100%;
} }
body, body,
a:active { a:active,
#content {
background-color:#AEA187; background-color:#AEA187;
} }
body { body {