Merge branch 'dev-0.7.x' into framebusting

Conflicts:
	lib/action.php
This commit is contained in:
Meitar Moscovitz 2009-02-18 13:01:52 +11:00
commit cca1d77748
26 changed files with 594 additions and 41 deletions

53
README
View File

@ -507,7 +507,7 @@ server is probably a good idea for high-volume sites.
needs as a parameter the install path; if you run it from the
Laconica dir, "." should suffice.
This will run six (for now) queue handlers:
This will run eight (for now) queue handlers:
* xmppdaemon.php - listens for new XMPP messages from users and stores
them as notices in the database.
@ -521,6 +521,10 @@ This will run six (for now) queue handlers:
of registered users.
* xmppconfirmhandler.php - sends confirmation messages to registered
users.
* twitterqueuehandler.php - sends queued notices to Twitter for user
who have opted to set up Twitter bridging.
* facebookqueuehandler.php - sends queued notices to Facebook for users
of the built-in Facebook application.
Note that these queue daemons are pretty raw, and need your care. In
particular, they leak memory, and you may want to restart them on a
@ -553,6 +557,53 @@ Sample cron job:
# Update Twitter friends subscriptions every half hour
0,30 * * * * /path/to/php /path/to/laconica/scripts/synctwitterfriends.php>&/dev/null
Built-in Facebook Application
-----------------------------
Laconica's Facebook application allows your users to automatically
update their Facebook statuses with their latest notices, invite
their friends to use the app (and thus your site), view their notice
timelines, and post notices -- all from within Facebook. The application
is built into Laconica and runs on your host. For automatic Facebook
status updating to work you will need to enable queuing and run the
facebookqueuehandler.php daemon (see the "Queues and daemons" section
above).
Quick setup instructions*:
Install the Facebook Developer application on Facebook:
http://www.facebook.com/developers/
Use it to create a new application and generate an API key and secret.
Uncomment the Facebook app section of your config.php and copy in the
key and secret, e.g.:
# Config section for the built-in Facebook application
$config['facebook']['apikey'] = 'APIKEY';
$config['facebook']['secret'] = 'SECRET';
In Facebook's application editor, specify the following URLs for your app:
- Callback URL: http://example.net/mublog/facebook/
- Post-Remove URL: http://example.net/mublog/facebook/remove
- Post-Add Redirect URL: http://apps.facebook.com/yourapp/
- Canvas URL: http://apps.facebook.com/yourapp/
(Replace 'example.net' with your host's URL, 'mublog' with the path
to your Laconica installation, and 'yourapp' with the name of the
Facebook application you created.)
Additionally, Choose "Web" for Application type in the Advanced tab.
In the "Canvas setting" section, choose the "FBML" for Render Method,
"Smart Size" for IFrame size, and "Full width (760px)" for Canvas Width.
Everything else can be left with default values.
*For more detailed instructions please see the installation guide on the
Laconica wiki:
http://laconi.ca/trac/wiki/FacebookApplication
Sitemaps
--------

View File

@ -164,6 +164,11 @@ class EmailsettingsAction extends AccountSettingsAction
$user->emailnotifymsg);
$this->elementEnd('li');
$this->elementStart('li');
$this->checkbox('emailnotifyattn',
_('Send me email when someone sends me an "@-reply".'),
$user->emailnotifyattn);
$this->elementEnd('li');
$this->elementStart('li');
$this->checkbox('emailnotifynudge',
_('Allow friends to nudge me and send me an email.'),
$user->emailnotifynudge);
@ -255,6 +260,7 @@ class EmailsettingsAction extends AccountSettingsAction
$emailnotifyfav = $this->boolean('emailnotifyfav');
$emailnotifymsg = $this->boolean('emailnotifymsg');
$emailnotifynudge = $this->boolean('emailnotifynudge');
$emailnotifyattn = $this->boolean('emailnotifyattn');
$emailmicroid = $this->boolean('emailmicroid');
$emailpost = $this->boolean('emailpost');
@ -270,6 +276,7 @@ class EmailsettingsAction extends AccountSettingsAction
$user->emailnotifyfav = $emailnotifyfav;
$user->emailnotifymsg = $emailnotifymsg;
$user->emailnotifynudge = $emailnotifynudge;
$user->emailnotifyattn = $emailnotifyattn;
$user->emailmicroid = $emailmicroid;
$user->emailpost = $emailpost;

View File

@ -292,11 +292,11 @@ class ShowstreamAction extends Action
$this->elementStart('ul', 'tags xoxo');
foreach ($tags as $tag) {
$this->elementStart('li');
$this->element('span', 'mark_hash', '#');
$this->element('a', array('rel' => 'tag',
'href' => common_local_url('peopletag',
array('tag' => $tag))),
$tag);
// Avoid space by using raw output.
$pt = '<span class="mark_hash">#</span><a rel="tag" href="' .
common_local_url('peopletag', array('tag' => $tag)) .
'">' . $tag . '</a>';
$this->raw($pt);
$this->elementEnd('li');
}
$this->elementEnd('ul');

BIN
bin/flowplayer-3.0.5.swf Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -48,8 +48,9 @@ class Notice extends Memcached_DataObject
public $source; // varchar(32)
/* Static get */
function staticGet($k,$v=null)
{ return Memcached_DataObject::staticGet('Notice',$k,$v); }
function staticGet($k,$v=NULL) {
return Memcached_DataObject::staticGet('Notice',$k,$v);
}
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
@ -94,22 +95,27 @@ class Notice extends Memcached_DataObject
/* Add them to the database */
foreach(array_unique($match[1]) as $hashtag) {
/* elide characters we don't want in the tag */
$this->saveTag($hashtag);
}
return true;
}
function saveTag($hashtag)
{
$hashtag = common_canonical_tag($hashtag);
$tag = DB_DataObject::factory('Notice_tag');
$tag = new Notice_tag();
$tag->notice_id = $this->id;
$tag->tag = $hashtag;
$tag->created = $this->created;
$id = $tag->insert();
if (!$id) {
$last_error = PEAR::getStaticProperty('DB_DataObject','lastError');
common_log(LOG_ERR, 'DB error inserting hashtag: ' . $last_error->message);
common_server_error(sprintf(_('DB error inserting hashtag: %s'), $last_error->message));
throw new ServerException(sprintf(_('DB error inserting hashtag: %s'),
$last_error->message));
return;
}
}
return true;
}
static function saveNew($profile_id, $content, $source=null, $is_local=1, $reply_to=null, $uri=null) {
@ -136,10 +142,12 @@ class Notice extends Memcached_DataObject
$notice->profile_id = $profile_id;
$blacklist = common_config('public', 'blacklist');
$autosource = common_config('public', 'autosource');
# Blacklisted are non-false, but not 1, either
if ($blacklist && in_array($profile_id, $blacklist)) {
if (($blacklist && in_array($profile_id, $blacklist)) ||
($source && $autosource && in_array($source, $autosource))) {
$notice->is_local = -1;
} else {
$notice->is_local = $is_local;
@ -619,6 +627,15 @@ class Notice extends Memcached_DataObject
continue;
}
// we automatically add a tag for every group name, too
$tag = Notice_tag::pkeyGet(array('tag' => common_canonical_tag($nickname),
'notice_id' => $this->id));
if (is_null($tag)) {
$this->saveTag($nickname);
}
if ($profile->isMember($group)) {
$gi = new Group_inbox();
@ -730,10 +747,19 @@ class Notice extends Memcached_DataObject
if (!$id) {
common_log_db_error($reply, 'INSERT', __FILE__);
return;
} else {
$replied[$recipient->id] = 1;
}
}
}
}
}
foreach (array_keys($replied) as $recipient) {
$user = User::staticGet('id', $recipient);
if ($user) {
mail_notify_attn($user, $this);
}
}
}
}

View File

@ -54,4 +54,9 @@ class Notice_tag extends Memcached_DataObject
$cache->delete(common_cache_key('notice_tag:notice_stream:' . $this->tag));
}
}
function &pkeyGet($kv)
{
return Memcached_DataObject::pkeyGet('Notice_tag', $kv);
}
}

View File

@ -40,6 +40,7 @@ class User extends Memcached_DataObject
public $emailnotifyfav; // tinyint(1) default_1
public $emailnotifynudge; // tinyint(1) default_1
public $emailnotifymsg; // tinyint(1) default_1
public $emailnotifyattn; // tinyint(1) default_1
public $emailmicroid; // tinyint(1) default_1
public $language; // varchar(50)
public $timezone; // varchar(50)
@ -62,8 +63,10 @@ class User extends Memcached_DataObject
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
/* Static get */
function staticGet($k,$v=null)
{ return Memcached_DataObject::staticGet('User',$k,$v); }
function staticGet($k,$v=NULL)
{
return Memcached_DataObject::staticGet('User',$k,$v);
}
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE

View File

@ -332,6 +332,7 @@ emailnotifysub = 17
emailnotifyfav = 17
emailnotifynudge = 17
emailnotifymsg = 17
emailnotifyattn = 17
emailmicroid = 17
language = 2
timezone = 2

View File

@ -18,6 +18,8 @@ $config['site']['server'] = 'localhost';
$config['site']['path'] = 'laconica';
#$config['site']['fancy'] = false;
#$config['site']['theme'] = 'default';
#To enable the built-in mobile style sheet, defaults to false.
#$config['site']['mobile'] = true;
#For contact email, defaults to $_SERVER["SERVER_ADMIN"]
#$config['site']['email'] = 'admin@example.net';
#Brought by...
@ -107,6 +109,14 @@ $config['sphinx']['port'] = 3312;
#$config['public']['blacklist'][] = 123;
#$config['public']['blacklist'][] = 2307;
#Mark certain notice sources as automatic and thus not
#appropriate for public feed
#$config['public]['autosource'][] = 'twitterfeed';
#$config['public]['autosource'][] = 'rssdent';
#$config['public]['autosource'][] = 'Ping.Fm';
#$config['public]['autosource'][] = 'HelloTxt';
#$config['public]['autosource'][] = 'Updating.Me';
#Do notice broadcasts offline
#If you use this, you must run the six offline daemons in the
#background. See the README for details.
@ -139,7 +149,7 @@ $config['sphinx']['port'] = 3312;
#$config['profile']['banned'][] = 'hacker';
#$config['profile']['banned'][] = 12345;
# config section for the built-in Facebook application
# Config section for the built-in Facebook application
#$config['facebook']['apikey'] = 'APIKEY';
#$config['facebook']['secret'] = 'SECRET';

View File

@ -50,6 +50,7 @@ create table user (
emailnotifyfav tinyint default 1 comment 'Notify by email of favorites',
emailnotifynudge tinyint default 1 comment 'Notify by email of nudges',
emailnotifymsg tinyint default 1 comment 'Notify by email of direct messages',
emailnotifyattn tinyint default 1 comment 'Notify by email of @-replies',
emailmicroid tinyint default 1 comment 'whether to publish email microid',
language varchar(50) comment 'preferred language',
timezone varchar(50) comment 'timezone',

24
js/flowplayer-3.0.5.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,8 @@
/*
* SimpleModal 1.2.2 - jQuery Plugin
* http://www.ericmmartin.com/projects/simplemodal/
* Copyright (c) 2008 Eric Martin
* Dual licensed under the MIT and GPL licenses
* Revision: $Id: jquery.simplemodal.js 181 2008-12-16 16:51:44Z emartin24 $
*/
eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(g($){m f=$.Q.1Q&&1a($.Q.1D)==6&&!10[\'2g\'],1f=$.Q.1Q&&!$.2a,w=[];$.y=g(a,b){I $.y.12.1n(a,b)};$.y.D=g(){$.y.12.D()};$.1P.y=g(a){I $.y.12.1n(3,a)};$.y.1O={V:29,1J:\'r-H\',1B:{},1z:\'r-n\',20:{},1Z:{},v:2t,D:1o,1T:\'<a 2j="2h" 2f="2e"></a>\',X:\'r-D\',l:F,1g:K,1e:F,1d:F,1c:F};$.y.12={7:F,4:{},1n:g(a,b){8(3.4.j){I K}3.7=$.U({},$.y.1O,b);3.v=3.7.v;3.1w=K;8(J a==\'27\'){a=a 25 1A?a:$(a);8(a.1v().1v().23()>0){3.4.T=a.1v();8(!3.7.1g){3.4.21=a.2x(1o)}}}q 8(J a==\'2w\'||J a==\'1r\'){a=$(\'<1q/>\').2s(a)}q{2r(\'2q 2p: 2o j 2l: \'+J a);I K}3.4.j=a.11(\'r-j\').E(3.7.1Z);a=F;3.1S();3.1R();8($.1m(3.7.1d)){3.7.1d.1l(3,[3.4])}I 3},1S:g(){w=3.1k();8(f){3.4.x=$(\'<x 2d="2c:K;"/>\').E($.U(3.7.2b,{1j:\'1i\',V:0,l:\'1h\',A:w[0],z:w[1],v:3.7.v,L:0,B:0})).O(\'u\')}3.4.H=$(\'<1q/>\').1N(\'1M\',3.7.1J).11(\'r-H\').E($.U(3.7.1B,{1j:\'1i\',V:3.7.V/1b,A:w[0],z:w[1],l:\'1h\',B:0,L:0,v:3.7.v+1})).O(\'u\');3.4.n=$(\'<1q/>\').1N(\'1M\',3.7.1z).11(\'r-n\').E($.U(3.7.20,{1j:\'1i\',l:\'1h\',v:3.7.v+2})).1K(3.7.D?$(3.7.1T).11(3.7.X):\'\').O(\'u\');3.19();8(f||1f){3.18()}3.4.n.1K(3.4.j.1I())},1H:g(){m a=3;$(\'.\'+3.7.X).1G(\'1L.r\',g(e){e.28();a.D()});$(10).1G(\'1F.r\',g(){w=a.1k();a.19();8(f||1f){a.18()}q{a.4.x&&a.4.x.E({A:w[0],z:w[1]});a.4.H.E({A:w[0],z:w[1]})}})},1E:g(){$(\'.\'+3.7.X).1C(\'1L.r\');$(10).1C(\'1F.r\')},18:g(){m p=3.7.l;$.26([3.4.x||F,3.4.H,3.4.n],g(i,e){8(e){m a=\'k.u.17\',N=\'k.u.1W\',16=\'k.u.24\',S=\'k.u.1y\',R=\'k.u.1x\',15=\'k.u.22\',1t=\'k.P.17\',1s=\'k.P.1W\',C=\'k.P.1y\',G=\'k.P.1x\',s=e[0].2v;s.l=\'2u\';8(i<2){s.14(\'A\');s.14(\'z\');s.Z(\'A\',\'\'+16+\' > \'+a+\' ? \'+16+\' : \'+a+\' + "o"\');s.Z(\'z\',\'\'+15+\' > \'+N+\' ? \'+15+\' : \'+N+\' + "o"\')}q{m b,W;8(p&&p.1Y==1X){8(p[0]){m c=J p[0]==\'1r\'?p[0].1V():p[0].13(/o/,\'\');b=c.1U(\'%\')==-1?c+\' + (t = \'+G+\' ? \'+G+\' : \'+R+\') + "o"\':1a(c.13(/%/,\'\'))+\' * ((\'+1t+\' || \'+a+\') / 1b) + (t = \'+G+\' ? \'+G+\' : \'+R+\') + "o"\'}8(p[1]){m d=J p[1]==\'1r\'?p[1].1V():p[1].13(/o/,\'\');W=d.1U(\'%\')==-1?d+\' + (t = \'+C+\' ? \'+C+\' : \'+S+\') + "o"\':1a(d.13(/%/,\'\'))+\' * ((\'+1s+\' || \'+N+\') / 1b) + (t = \'+C+\' ? \'+C+\' : \'+S+\') + "o"\'}}q{b=\'(\'+1t+\' || \'+a+\') / 2 - (3.2n / 2) + (t = \'+G+\' ? \'+G+\' : \'+R+\') + "o"\';W=\'(\'+1s+\' || \'+N+\') / 2 - (3.2m / 2) + (t = \'+C+\' ? \'+C+\' : \'+S+\') + "o"\'}s.14(\'L\');s.14(\'B\');s.Z(\'L\',b);s.Z(\'B\',W)}}})},1k:g(){m a=$(10);m h=$.Q.2k&&$.Q.1D>\'9.5\'&&$.1P.2i<=\'1.2.6\'?k.P[\'17\']:a.A();I[h,a.z()]},19:g(){m a,B,1u=(w[0]/2)-((3.4.n.A()||3.4.j.A())/2),1p=(w[1]/2)-((3.4.n.z()||3.4.j.z())/2);8(3.7.l&&3.7.l.1Y==1X){a=3.7.l[0]||1u;B=3.7.l[1]||1p}q{a=1u;B=1p}3.4.n.E({B:B,L:a})},1R:g(){3.4.x&&3.4.x.Y();8($.1m(3.7.1e)){3.7.1e.1l(3,[3.4])}q{3.4.H.Y();3.4.n.Y();3.4.j.Y()}3.1H()},D:g(){8(!3.4.j){I K}8($.1m(3.7.1c)&&!3.1w){3.1w=1o;3.7.1c.1l(3,[3.4])}q{8(3.4.T){8(3.7.1g){3.4.j.1I().O(3.4.T)}q{3.4.j.M();3.4.21.O(3.4.T)}}q{3.4.j.M()}3.4.n.M();3.4.H.M();3.4.x&&3.4.x.M();3.4={}}3.1E()}}})(1A);',62,158,'|||this|dialog|||opts|if||||||||function|||data|document|position|var|container|px||else|simplemodal|||body|zIndex||iframe|modal|width|height|left|sl|close|css|null|st|overlay|return|typeof|false|top|remove|bcw|appendTo|documentElement|browser|bst|bsl|parentNode|extend|opacity|le|closeClass|show|setExpression|window|addClass|impl|replace|removeExpression|bsw|bsh|clientHeight|fixIE|setPosition|parseInt|100|onClose|onShow|onOpen|ieQuirks|persist|fixed|none|display|getDimensions|apply|isFunction|init|true|vCenter|div|number|cw|ch|hCenter|parent|occb|scrollTop|scrollLeft|containerId|jQuery|overlayCss|unbind|version|unbindEvents|resize|bind|bindEvents|hide|overlayId|append|click|id|attr|defaults|fn|msie|open|create|closeHTML|indexOf|toString|clientWidth|Array|constructor|dataCss|containerCss|orig|scrollWidth|size|scrollHeight|instanceof|each|object|preventDefault|50|boxModel|iframeCss|javascript|src|Close|title|XMLHttpRequest|modalCloseImg|jquery|class|opera|type|offsetWidth|offsetHeight|Unsupported|Error|SimpleModal|alert|html|1000|absolute|style|string|clone'.split('|'),0,{}))

9
js/video.js Normal file
View File

@ -0,0 +1,9 @@
$('document').ready(function() {
$('a.media, a.mediamp3').append(' <sup>[PLAY]</sup>');
$('a.mediamp3').html('').css('display', 'block').css('width', '224px').css('height','24px').flowplayer('../bin/flowplayer-3.0.5.swf');
$('a.media').click(function() {
$('<a id="p1i"></a>').attr('href', $(this).attr('href')).flowplayer('../bin/flowplayer-3.0.5.swf').modal({'closeHTML':'<a class="modalCloseImg" title="Close"><img src="x.png" /></a>'});
return false;
});
});

View File

@ -153,14 +153,26 @@ class Action extends HTMLOutputter // lawsuit
{
if (Event::handle('StartShowStyles', array($this))) {
if (Event::handle('StartShowLaconicaStyles', array($this))) {
$this->element('link', array('rel' => 'stylesheet',
'type' => 'text/css',
'href' => theme_path('css/display.css', 'base') . '?version=' . LACONICA_VERSION,
'media' => 'screen, projection, tv'));
$this->element('link', array('rel' => 'stylesheet',
'type' => 'text/css',
'href' => theme_path('css/modal.css', 'base') . '?version=' . LACONICA_VERSION,
'media' => 'screen, projection, tv'));
$this->element('link', array('rel' => 'stylesheet',
'type' => 'text/css',
'href' => theme_path('css/display.css', null) . '?version=' . LACONICA_VERSION,
'media' => 'screen, projection, tv'));
if (common_config('site', 'mobile')) {
$this->element('link', array('rel' => 'stylesheet',
'type' => 'text/css',
'href' => theme_path('css/mobile.css', 'base') . '?version=' . LACONICA_VERSION,
// TODO: "handheld" CSS for other mobile devices
'media' => 'only screen and (max-device-width: 480px)')); // Mobile WebKit
}
Event::handle('EndShowLaconicaStyles', array($this));
}
if (Event::handle('StartShowUAStyles', array($this))) {
@ -196,6 +208,13 @@ class Action extends HTMLOutputter // lawsuit
$this->element('script', array('type' => 'text/javascript',
'src' => common_path('js/jquery.form.js')),
' ');
$this->element('script', array('type' => 'text/javascript',
'src' => common_path('js/jquery.simplemodal-1.2.2.pack.js')),
' ');
Event::handle('EndShowJQueryScripts', array($this));
}
if (Event::handle('StartShowLaconicaScripts', array($this))) {
@ -208,6 +227,14 @@ class Action extends HTMLOutputter // lawsuit
// Frame-busting code to avoid clickjacking attacks.
$this->element('script', array('type' => 'text/javascript'),
'if (window.top !== window.self) { window.top.location.href = window.self.location.href; }');
$this->element('script', array('type' => 'text/javascript',
'src' => common_path('js/flowplayer-3.0.5.min.js')),
' ');
$this->element('script', array('type' => 'text/javascript',
'src' => common_path('js/video.js')),
' ');
Event::handle('EndShowLaconicaScripts', array($this));
}
Event::handle('EndShowScripts', array($this));

View File

@ -106,7 +106,8 @@ $config =
array('server' => null),
'public' =>
array('localonly' => true,
'blacklist' => array()),
'blacklist' => array(),
'autosource' => array()),
'theme' =>
array('server' => null),
'throttle' =>

View File

@ -573,3 +573,53 @@ function mail_notify_fave($other, $user, $notice)
common_init_locale();
mail_to_user($other, $subject, $body);
}
/**
* notify a user that they have received an "attn:" message AKA "@-reply"
*
* @param User $user The user who recevied the notice
* @param Notice $notice The notice that was sent
*
* @return void
*/
function mail_notify_attn($user, $notice)
{
if (!$user->email || !$user->emailnotifyattn) {
return;
}
$sender = $notice->getProfile();
$bestname = $sender->getBestName();
common_init_locale($user->language);
$subject = sprintf(_('%s sent a notice to your attention'), $bestname);
$body = sprintf(_("%1\$s just sent a notice to your attention (an '@-reply') on %2\$s.\n\n".
"The notice is here:\n\n".
"\t%3\$s\n\n" .
"It reads:\n\n".
"\t%4\$s\n\n" .
"You can reply back here:\n\n".
"\t%5\$s\n\n" .
"The list of all @-replies for you here:\n\n" .
"%6\$s\n\n" .
"Faithfully yours,\n" .
"%2\$s\n\n" .
"P.S. You can turn off these email notifications here: %7\$s\n"),
$bestname,
common_config('site', 'name'),
common_local_url('shownotice',
array('notice' => $notice->id)),
$notice->content,
common_local_url('newnotice',
array('replyto' => $sender->nickname)),
common_local_url('replies',
array('nickname' => $user->nickname)),
common_local_url('emailsettings'));
common_init_locale();
mail_to_user($user, $subject, $body);
}

View File

@ -474,12 +474,18 @@ function common_replace_urls_callback($text, $callback) {
function common_linkify($url) {
// It comes in special'd, so we unspecial it before passing to the stringifying
// functions
$ext = pathinfo($url, PATHINFO_EXTENSION);
$url = htmlspecialchars_decode($url);
$video_ext = array('mp4', 'flv', 'avi', 'mpg', 'mp3', 'ogg');
$display = $url;
$url = (!preg_match('#^([a-z]+://|(mailto|aim|tel):)#i', $url)) ? 'http://'.$url : $url;
$attrs = array('href' => $url, 'rel' => 'external');
if (in_array($ext, $video_ext)) {
$attrs['class'] = 'media';
}
if ($longurl = common_longurl($url)) {
$attrs['title'] = $longurl;
}
@ -590,7 +596,7 @@ function common_tag_link($tag)
$xs->element('a', array('href' => $url,
'rel' => 'tag'),
$tag);
$xs->elementEnd();
$xs->elementEnd('span');
return $xs->getString();
}

View File

@ -0,0 +1,144 @@
<?php
/**
* Laconica, the distributed open-source microblogging tool
*
* Plugin to check submitted notices with blogspam.net
*
* 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 Plugin
* @package Laconica
* @author Evan Prodromou <evan@controlyourself.ca>
* @copyright 2009 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);
}
define('BLOGSPAMNETPLUGIN_VERSION', '0.1');
/**
* Plugin to check submitted notices with blogspam.net
*
* When new notices are saved, we check their text with blogspam.net (or
* a compatible service).
*
* Blogspam.net is supposed to catch blog comment spam, and I found that
* some of its tests (min/max size, bayesian match) gave a lot of false positives.
* So, I've turned those tests off by default. This may not get as many
* hits, but it's better than nothing.
*
* @category Plugin
* @package Laconica
* @author Evan Prodromou <evan@controlyourself.ca>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://laconi.ca/
*
* @see Event
*/
class BlogspamNetPlugin extends Plugin
{
var $baseUrl = 'http://test.blogspam.net:8888/';
function __construct($url=null)
{
parent::__construct();
if ($url) {
$this->baseUrl = $url;
}
}
function onStartNoticeSave($notice)
{
$args = $this->testArgs($notice);
common_debug("Blogspamnet args = " . print_r($args, TRUE));
$request = xmlrpc_encode_request('testComment', array($args));
$context = stream_context_create(array('http' => array('method' => "POST",
'header' =>
"Content-Type: text/xml\r\n".
"User-Agent: " . $this->userAgent(),
'content' => $request)));
$file = file_get_contents($this->baseUrl, false, $context);
$response = xmlrpc_decode($file);
if (xmlrpc_is_fault($response)) {
throw new ServerException("$response[faultString] ($response[faultCode])", 500);
} else {
common_debug("Blogspamnet results = " . $response);
if (preg_match('/^ERROR(:(.*))?$/', $response, $match)) {
throw new ServerException(sprintf(_("Error from %s: %s"), $this->baseUrl, $match[2]), 500);
} else if (preg_match('/^SPAM(:(.*))?$/', $response, $match)) {
throw new ClientException(sprintf(_("Spam checker results: %s"), $match[2]), 400);
} else if (preg_match('/^OK$/', $response)) {
// don't do anything
} else {
throw new ServerException(sprintf(_("Unexpected response from %s: %s"), $this->baseUrl, $response), 500);
}
}
return true;
}
function testArgs($notice)
{
$args = array();
$args['comment'] = $notice->content;
$args['ip'] = $this->getClientIP();
if (isset($_SERVER) && array_key_exists('HTTP_USER_AGENT', $_SERVER)) {
$args['agent'] = $_SERVER['HTTP_USER_AGENT'];
}
$profile = $notice->getProfile();
if ($profile && $profile->homepage) {
$args['link'] = $profile->homepage;
}
if ($profile && $profile->fullname) {
$args['name'] = $profile->fullname;
} else {
$args['name'] = $profile->nickname;
}
$args['site'] = common_root_url();
$args['version'] = $this->userAgent();
$args['options'] = "max-size=140,min-size=0,min-words=0,exclude=bayasian";
return $args;
}
function getClientIP()
{
if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
// Note: order matters here; use proxy-forwarded stuff first
foreach (array('HTTP_X_FORWARDED_FOR', 'CLIENT-IP', 'REMOTE_ADDR') as $k) {
if (isset($_SERVER[$k])) {
return $_SERVER[$k];
}
}
}
return '127.0.0.1';
}
function userAgent()
{
return 'BlogspamNetPlugin/'.BLOGSPAMNETPLUGIN_VERSION . ' Laconica/' . LACONICA_VERSION;
}
}

View File

@ -0,0 +1,71 @@
#!/usr/bin/env php
<?php
/*
* Laconica - a distributed open-source microblogging tool
* Copyright (C) 2008, Controlez-Vous, 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();
}
define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
define('LACONICA', true);
require_once(INSTALLDIR . '/lib/common.php');
require_once(INSTALLDIR . '/lib/facebookutil.php');
require_once(INSTALLDIR . '/lib/queuehandler.php');
set_error_handler('common_error_handler');
class FacebookQueueHandler extends QueueHandler
{
function transport()
{
return 'facebook';
}
function start()
{
$this->log(LOG_INFO, "INITIALIZE");
return true;
}
function handle_notice($notice)
{
return facebookBroadcastNotice($notice);
}
function finish()
{
}
}
ini_set("max_execution_time", "0");
ini_set("max_input_time", "0");
set_time_limit(0);
mb_internal_encoding('UTF-8');
$id = ($argc > 1) ? $argv[1] : null;
$handler = new FacebookQueueHandler($id);
$handler->runOnce();

View File

@ -22,7 +22,6 @@ line-height:1.65;
position:relative;
}
h1,h2,h3,h4,h5,h6 {
text-transform:capitalize;
margin-bottom:7px;
overflow:hidden;
}

72
theme/base/css/mobile.css Normal file
View File

@ -0,0 +1,72 @@
/** theme: base
*
* @package Laconica
* @author Meitar Moscovitz <meitar@maymay.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://laconi.ca/
*/
/* Go linear. */
#header,
#header address,
#site_nav_global_primary,
#anon_notice,
#site_nav_local_views .nav,
#form_notice,
#form_notice .form_data li,
#core,
#content_inner,
#notices_primary,
.notice,
.notice .entry-title,
.notice div.entry-content,
.notice-options,
.notice .notice-options a,
.pagination,
.pagination .nav,
.aside .section { float: none; }
.notice-options .notice_reply,
.notice-options .notice_delete,
.notice-options .form_favor,
.notice-options .form_disfavor { position: static; }
#form_notice,
#anon_notice,
#content_inner,
#footer { width: auto; }
/* And liquid. */
#wrap { width: 95%; }
/* Make things bigger on smaller screens. */
body { font-size: 2em; }
.notices { font-size: 1.5em; }
#site_nav_global_primary, #site_nav_global_secondary { text-align: center; }
.notice div.entry-content { margin-left: 0; }
address { margin: 0; }
#anon_notice, #footer { clear: left; font-size: .5em; }
#form_notice textarea { width: 80%; height: 5em; }
#form_notice .form_note { right: 20%; top: 6em; }
#form_notice .form_actions input.submit { width: auto; }
#content { padding: 18px 0; width: 100%; }
#content h1, #page_notice, #content_inner { padding: 0 18px; }
.notices .entry-title, .notices div.entry-content { width: 90%; }
.notice .author .photo { height: 4.5em; width: 4.5em; } /* about double physical size; TODO: do this scaling better */
.notice-options { position: absolute; top: 0; right: 0; padding-left: 7%; width: 3%; }
.notice-options .notice_delete a { float: left; } /* Works, but feels like it shouldn't. */
/* TODO: Make the icons of the notice options bigger. Probably with mobile-specific images. */
.pagination .nav { overflow: auto; }
#aside_primary { margin: 10px 0 0 0; border: none; padding: 0; width: 100%; }
#popular_notices { float: none; width: auto; }
/* Columns for supplemental info. */
.aside .section { clear: none; padding: 9px; width: 45%; }
#top_groups_by_post { float: left; }
#featured_users { float: right; }
#export_data { display: none; }

22
theme/base/css/modal.css Normal file
View File

@ -0,0 +1,22 @@
/*
* SimpleModal Basic Modal Dialog
* http://www.ericmmartin.com/projects/simplemodal/
* http://code.google.com/p/simplemodal/
*
* Copyright (c) 2008 Eric Martin - http://ericmmartin.com
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/mit-license.php
*
* Revision: $Id: basic.css 162 2008-12-01 23:36:58Z emartin24 $
*
*/
/* Overlay */
#simplemodal-overlay {background-color:#000; cursor:wait;}
/* Container */
#simplemodal-container {height:240px; width:320px; background-color:#fff; border:3px solid #ccc;}
#simplemodal-container a.modalCloseImg {background:url(../images/x.png) no-repeat; width:25px; height:29px; display:inline; z-index:3200; position:absolute; top:-15px; right:-18px; cursor:pointer;}
#simplemodal-container #basicModalContent {padding:8px;}

View File

@ -0,0 +1,16 @@
/*
* SimpleModal Basic Modal Dialog
* http://www.ericmmartin.com/projects/simplemodal/
* http://code.google.com/p/simplemodal/
*
* Copyright (c) 2008 Eric Martin - http://ericmmartin.com
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/mit-license.php
*
* Revision: $Id: basic_ie.css 162 2008-12-01 23:36:58Z emartin24 $
*
*/
/* IE 6 hacks*/
#simplemodal-container a.modalCloseImg {background:none; right:-14px; width:22px; height:26px; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='../images/x.png',sizingMethod='scale');}

BIN
theme/base/images/x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB