Merge branch '0.8.x' into userdesign

Conflicts:
	lib/attachmentlist.php
	lib/noticelist.php
This commit is contained in:
Evan Prodromou 2009-05-26 23:24:01 -04:00
commit 648d967226
26 changed files with 493 additions and 637 deletions

26
README
View File

@ -1136,6 +1136,32 @@ welcome: nickname of a user account that sends welcome messages to new
If either of these special user accounts are specified, the users should If either of these special user accounts are specified, the users should
be created before the configuration is updated. be created before the configuration is updated.
snapshot
--------
The software will, by default, send statistical snapshots about the
local installation to a stats server on the laconi.ca Web site. This
data is used by the developers to prioritize development decisions. No
identifying data about users or organizations is collected. The data
is available to the public for review. Participating in this survey
helps Laconica developers take your needs into account when updating
the software.
run: string indicating when to run the statistics. Values can be 'web'
(run occasionally at Web time), 'cron' (run from a cron script),
or 'never' (don't ever run). If you set it to 'cron', remember to
schedule the script to run on a regular basis.
frequency: if run value is 'web', how often to report statistics.
Measured in Web hits; depends on how active your site is.
Default is 10000 -- that is, one report every 10000 Web hits,
on average.
reporturl: URL to post statistics to. Defaults to Laconica developers'
report system, but if they go evil or disappear you may
need to update this to another value. Note: if you
don't want to report stats, it's much better to
set 'run' to 'never' than to set this value to something
nonsensical.
Troubleshooting Troubleshooting
=============== ===============

View File

@ -31,8 +31,6 @@ if (!defined('LACONICA')) {
exit(1); exit(1);
} }
//require_once INSTALLDIR.'/lib/personalgroupnav.php';
//require_once INSTALLDIR.'/lib/feedlist.php';
require_once INSTALLDIR.'/lib/attachmentlist.php'; require_once INSTALLDIR.'/lib/attachmentlist.php';
/** /**
@ -67,11 +65,11 @@ class AttachmentAction extends Action
{ {
parent::prepare($args); parent::prepare($args);
$id = $this->arg('attachment'); if ($id = $this->trimmed('attachment')) {
$this->attachment = File::staticGet($id); $this->attachment = File::staticGet($id);
}
if (!$this->attachment) { if (empty($this->attachment)) {
$this->clientError(_('No such attachment.'), 404); $this->clientError(_('No such attachment.'), 404);
return false; return false;
} }
@ -178,10 +176,8 @@ class AttachmentAction extends Action
function showContent() function showContent()
{ {
$this->elementStart('ul', array('class' => 'attachments'));
$ali = new Attachment($this->attachment, $this); $ali = new Attachment($this->attachment, $this);
$cnt = $ali->show(); $cnt = $ali->show();
$this->elementEnd('ul');
} }
/** /**

View File

@ -45,26 +45,6 @@ require_once INSTALLDIR.'/actions/attachment.php';
class Attachment_ajaxAction extends AttachmentAction class Attachment_ajaxAction extends AttachmentAction
{ {
/**
* Load attributes based on database arguments
*
* Loads all the DB stuff
*
* @param array $args $_REQUEST array
*
* @return success flag
*/
function prepare($args)
{
parent::prepare($args);
if (!$this->attachment) {
$this->clientError(_('No such attachment.'), 404);
return false;
}
return true;
}
/** /**
* Show page, a template method. * Show page, a template method.
* *
@ -95,8 +75,6 @@ class Attachment_ajaxAction extends AttachmentAction
$this->elementEnd('div'); $this->elementEnd('div');
} }
/** /**
* Last-modified date for page * Last-modified date for page
* *

View File

@ -31,9 +31,7 @@ if (!defined('LACONICA')) {
exit(1); exit(1);
} }
//require_once INSTALLDIR.'/lib/personalgroupnav.php'; require_once INSTALLDIR.'/actions/attachment.php';
//require_once INSTALLDIR.'/lib/feedlist.php';
require_once INSTALLDIR.'/actions/attachments.php';
/** /**
* Show notice attachments * Show notice attachments
@ -45,39 +43,8 @@ require_once INSTALLDIR.'/actions/attachments.php';
* @link http://laconi.ca/ * @link http://laconi.ca/
*/ */
class Attachments_ajaxAction extends AttachmentsAction class Attachment_thumbnailAction extends AttachmentAction
{ {
function showContent()
{
}
/**
* Fill the content area of the page
*
* Shows a single notice list item.
*
* @return void
*/
function showContentBlock()
{
$al = new AttachmentList($this->notice, $this);
$cnt = $al->show();
}
/**
* Extra <head> content
*
* We show the microid(s) for the author, if any.
*
* @return void
*/
function extraHead()
{
}
/** /**
* Show page, a template method. * Show page, a template method.
* *
@ -100,16 +67,52 @@ class Attachments_ajaxAction extends AttachmentsAction
*/ */
function showCore() function showCore()
{ {
$this->elementStart('div', array('id' => 'core')); $file_thumbnail = File_thumbnail::staticGet('file_id', $this->attachment->id);
if (Event::handle('StartShowContentBlock', array($this))) { if (empty($file_thumbnail->url)) {
$this->showContentBlock(); return;
Event::handle('EndShowContentBlock', array($this));
} }
$this->elementEnd('div'); $this->element('img', array('src' => $file_thumbnail->url, 'alt' => 'Thumbnail'));
} }
/**
* Last-modified date for page
*
* When was the content of this page last modified? Based on notice,
* profile, avatar.
*
* @return int last-modified date as unix timestamp
*/
/*
function lastModified()
{
return max(strtotime($this->notice->created),
strtotime($this->profile->modified),
($this->avatar) ? strtotime($this->avatar->modified) : 0);
}
*/
/**
* An entity tag for this page
*
* Shows the ETag for the page, based on the notice ID and timestamps
* for the notice, profile, and avatar. It's weak, since we change
* the date text "one hour ago", etc.
*
* @return string etag
*/
/*
function etag()
{
$avtime = ($this->avatar) ?
strtotime($this->avatar->modified) : 0;
return 'W/"' . implode(':', array($this->arg('action'),
common_language(),
$this->notice->id,
strtotime($this->notice->created),
strtotime($this->profile->modified),
$avtime)) . '"';
}
*/
} }

View File

@ -1,292 +0,0 @@
<?php
/**
* Laconica, the distributed open-source microblogging tool
*
* Show notice attachments
*
* 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 Personal
* @package Laconica
* @author Evan Prodromou <evan@controlyourself.ca>
* @copyright 2008-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);
}
//require_once INSTALLDIR.'/lib/personalgroupnav.php';
//require_once INSTALLDIR.'/lib/feedlist.php';
require_once INSTALLDIR.'/lib/attachmentlist.php';
/**
* Show notice attachments
*
* @category Personal
* @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/
*/
class AttachmentsAction extends Action
{
/**
* Notice object to show
*/
var $notice = null;
/**
* Profile of the notice object
*/
var $profile = null;
/**
* Avatar of the profile of the notice object
*/
var $avatar = null;
/**
* Is this action read-only?
*
* @return boolean true
*/
function isReadOnly($args)
{
return true;
}
/**
* Last-modified date for page
*
* When was the content of this page last modified? Based on notice,
* profile, avatar.
*
* @return int last-modified date as unix timestamp
*/
function lastModified()
{
return max(strtotime($this->notice->created),
strtotime($this->profile->modified),
($this->avatar) ? strtotime($this->avatar->modified) : 0);
}
/**
* An entity tag for this page
*
* Shows the ETag for the page, based on the notice ID and timestamps
* for the notice, profile, and avatar. It's weak, since we change
* the date text "one hour ago", etc.
*
* @return string etag
*/
function etag()
{
$avtime = ($this->avatar) ?
strtotime($this->avatar->modified) : 0;
return 'W/"' . implode(':', array($this->arg('action'),
common_language(),
$this->notice->id,
strtotime($this->notice->created),
strtotime($this->profile->modified),
$avtime)) . '"';
}
/**
* Title of the page
*
* @return string title of the page
*/
function title()
{
return sprintf(_('%1$s\'s status on %2$s'),
$this->profile->nickname,
common_exact_date($this->notice->created));
}
/**
* Load attributes based on database arguments
*
* Loads all the DB stuff
*
* @param array $args $_REQUEST array
*
* @return success flag
*/
function prepare($args)
{
parent::prepare($args);
$id = $this->arg('notice');
$this->notice = Notice::staticGet($id);
if (!$this->notice) {
$this->clientError(_('No such notice.'), 404);
return false;
}
/*
// STOP if there are no attachments
// maybe even redirect if there's a single one
// RYM FIXME TODO
$this->clientError(_('No such attachment.'), 404);
return false;
*/
$this->profile = $this->notice->getProfile();
if (!$this->profile) {
$this->serverError(_('Notice has no profile'), 500);
return false;
}
$this->avatar = $this->profile->getAvatar(AVATAR_PROFILE_SIZE);
return true;
}
/**
* Handle input
*
* Only handles get, so just show the page.
*
* @param array $args $_REQUEST data (unused)
*
* @return void
*/
function handle($args)
{
parent::handle($args);
if ($this->notice->is_local == 0) {
if (!empty($this->notice->url)) {
common_redirect($this->notice->url, 301);
} else if (!empty($this->notice->uri) && preg_match('/^https?:/', $this->notice->uri)) {
common_redirect($this->notice->uri, 301);
}
} else {
$f2p = new File_to_post;
$f2p->post_id = $this->notice->id;
$file = new File;
$file->joinAdd($f2p);
$file->selectAdd();
$file->selectAdd('file.id as id');
$count = $file->find(true);
if (!$count) return;
if (1 === $count) {
common_redirect(common_local_url('attachment', array('attachment' => $file->id)), 301);
} else {
$this->showPage();
}
}
}
/**
* Don't show local navigation
*
* @return void
*/
function showLocalNavBlock()
{
}
/**
* Fill the content area of the page
*
* Shows a single notice list item.
*
* @return void
*/
function showContent()
{
$al = new AttachmentList($this->notice, $this);
$cnt = $al->show();
}
/**
* Don't show page notice
*
* @return void
*/
function showPageNoticeBlock()
{
}
/**
* Don't show aside
*
* @return void
*/
function showAside() {
}
/**
* Extra <head> content
*
* We show the microid(s) for the author, if any.
*
* @return void
*/
function extraHead()
{
$user = User::staticGet($this->profile->id);
if (!$user) {
return;
}
if ($user->emailmicroid && $user->email && $this->notice->uri) {
$id = new Microid('mailto:'. $user->email,
$this->notice->uri);
$this->element('meta', array('name' => 'microid',
'content' => $id->toString()));
}
if ($user->jabbermicroid && $user->jabber && $this->notice->uri) {
$id = new Microid('xmpp:', $user->jabber,
$this->notice->uri);
$this->element('meta', array('name' => 'microid',
'content' => $id->toString()));
}
}
}

View File

@ -49,8 +49,6 @@ class TagAction extends Action
{ {
$pop = new PopularNoticeSection($this); $pop = new PopularNoticeSection($this);
$pop->show(); $pop->show();
$freqatt = new FrequentAttachmentSection($this);
$freqatt->show();
} }
function title() function title()

View File

@ -206,3 +206,12 @@ $config['sphinx']['port'] = 3312;
// print "Error\n"; // print "Error\n";
// exit(1); // exit(1);
// } // }
// How often to send snapshots; in # of web hits. Ideally,
// try to do this once per month (that is, make this equal to number
// of hits per month)
// $config['snapshot']['frequency'] = 10000;
// If you don't want to report statistics to the central server, uncomment.
// $config['snapshot']['run'] = 'never';
// If you want to report statistics in a cron job instead.
// $config['snapshot']['run'] = 'cron';

View File

@ -9,6 +9,7 @@ VALUES
('Do','Gnome Do','http://do.davebsd.com/wiki/index.php?title=Microblog_Plugin', now()), ('Do','Gnome Do','http://do.davebsd.com/wiki/index.php?title=Microblog_Plugin', now()),
('Facebook','Facebook','http://apps.facebook.com/identica/', now()), ('Facebook','Facebook','http://apps.facebook.com/identica/', now()),
('feed2omb','feed2omb','http://projects.ciarang.com/p/feed2omb/', now()), ('feed2omb','feed2omb','http://projects.ciarang.com/p/feed2omb/', now()),
('gravity', 'Gravity', 'http://mobileways.de/gravity', now()),
('Gwibber','Gwibber','http://launchpad.net/gwibber', now()), ('Gwibber','Gwibber','http://launchpad.net/gwibber', now()),
('HelloTxt','HelloTxt','http://hellotxt.com/', now()), ('HelloTxt','HelloTxt','http://hellotxt.com/', now()),
('identicatools','Laconica Tools','http://bitbucketlabs.net/laconica-tools/', now()), ('identicatools','Laconica Tools','http://bitbucketlabs.net/laconica-tools/', now()),

View File

@ -64,11 +64,13 @@ function handleError($error)
function main() function main()
{ {
// quick check for fancy URL auto-detection support in installer. // quick check for fancy URL auto-detection support in installer.
if (isset($_SERVER['REDIRECT_URL']) && ('/check-fancy' === $_SERVER['REDIRECT_URL'])) { if (isset($_SERVER['REDIRECT_URL']) && ((dirname($_SERVER['REQUEST_URI']) . '/check-fancy') === $_SERVER['REDIRECT_URL'])) {
die("Fancy URL support detection succeeded. We suggest you enable this to get fancy (pretty) URLs."); die("Fancy URL support detection succeeded. We suggest you enable this to get fancy (pretty) URLs.");
} }
global $user, $action, $config; global $user, $action, $config;
Snapshot::check();
if (!_have_config()) { if (!_have_config()) {
$msg = sprintf(_("No configuration file found. Try running ". $msg = sprintf(_("No configuration file found. Try running ".
"the installation program first.")); "the installation program first."));

View File

@ -116,16 +116,16 @@ function showForm()
<input type="radio" name="fancy" id="fancy-disable" value="" /> disable<br /> <input type="radio" name="fancy" id="fancy-disable" value="" /> disable<br />
<p class="form_guide" id='fancy-form_guide'>Enable fancy (pretty) URLs. Auto-detection failed, it depends on Javascript.</p> <p class="form_guide" id='fancy-form_guide'>Enable fancy (pretty) URLs. Auto-detection failed, it depends on Javascript.</p>
</li> </li>
<li>
<label for="host">Hostname</label>
<input type="text" id="host" name="host" />
<p class="form_guide">Database hostname</p>
</li>
<li> <li>
<label for="host">Site path</label> <label for="host">Site path</label>
<input type="text" id="path" name="path" value="$config_path" /> <input type="text" id="path" name="path" value="$config_path" />
<p class="form_guide">Site path, following the "/" after the domain name in the URL. Empty is fine. Field should be filled automatically.</p> <p class="form_guide">Site path, following the "/" after the domain name in the URL. Empty is fine. Field should be filled automatically.</p>
</li> </li>
<li>
<label for="host">Hostname</label>
<input type="text" id="host" name="host" />
<p class="form_guide">Database hostname</p>
</li>
<li> <li>
<label for="host">Database</label> <label for="host">Database</label>
<input type="text" id="database" name="database" /> <input type="text" id="database" name="database" />
@ -295,13 +295,13 @@ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en_US" lang="en_US"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en_US" lang="en_US">
<head> <head>
<title>Install Laconica</title> <title>Install Laconica</title>
<link rel="stylesheet" type="text/css" href="theme/base/css/display.css?version=0.8" media="screen, projection, tv"/> <link rel="shortcut icon" href="favicon.ico"/>
<link rel="stylesheet" type="text/css" href="theme/default/css/display.css?version=0.8" media="screen, projection, tv"/> <link rel="stylesheet" type="text/css" href="theme/default/css/display.css?version=0.8" media="screen, projection, tv"/>
<!--[if IE]><link rel="stylesheet" type="text/css" href="theme/base/css/ie.css?version=0.8" /><![endif]--> <!--[if IE]><link rel="stylesheet" type="text/css" href="theme/base/css/ie.css?version=0.8" /><![endif]-->
<!--[if lte IE 6]><link rel="stylesheet" type="text/css" theme/base/css/ie6.css?version=0.8" /><![endif]--> <!--[if lte IE 6]><link rel="stylesheet" type="text/css" theme/base/css/ie6.css?version=0.8" /><![endif]-->
<!--[if IE]><link rel="stylesheet" type="text/css" href="theme/earthy/css/ie.css?version=0.8" /><![endif]--> <!--[if IE]><link rel="stylesheet" type="text/css" href="theme/default/css/ie.css?version=0.8" /><![endif]-->
<script src='js/jquery.min.js'></script> <script src="js/jquery.min.js"></script>
<script src='js/install.js'></script> <script src="js/install.js"></script>
</head> </head>
<body id="install"> <body id="install">
<div id="wrap"> <div id="wrap">

View File

@ -17,9 +17,29 @@
*/ */
$(document).ready(function(){ $(document).ready(function(){
$('.attachments').click(function() {$().jOverlay({zIndex:999, success:function(html) {$('.attachment').click(function() {$().jOverlay({url:$(this).attr('href') + '/ajax'}); return false; }); $('a.attachment').click(function() {$().jOverlay({url: $('address .url')[0].href+'/attachment/' + ($(this).attr('id').substring('attachment'.length + 1)) + '/ajax'}); return false; });
}, url:$(this).attr('href') + '/ajax'}); return false; }); $("a.thumbnail").hover(
$('.attachment').click(function() {$().jOverlay({url:$(this).attr('href') + '/ajax'}); return false; }); function() {
var anchor = $(this);
$("a.thumbnail").children('img').remove();
setTimeout(function() {
anchor.closest(".entry-title").addClass('ov');
$.get($('address .url')[0].href+'/attachment/' + (anchor.attr('id').substring('attachment'.length + 1)) + '/thumbnail', null, function(data) {
anchor.append(data);
});
}, 250);
setTimeout(function() {
anchor.children('img').remove();
anchor.closest(".entry-title").removeClass('ov');
}, 3000);
},
function() {
$(this).children('img').remove();
$(this).closest(".entry-title").removeClass('ov');
}
);
// count character on keyup // count character on keyup
function counter(event){ function counter(event){
@ -203,7 +223,6 @@ $(document).ready(function(){
$("#notices_primary .notices").prepend(document._importNode(li, true)); $("#notices_primary .notices").prepend(document._importNode(li, true));
$("#notices_primary .notice:first").css({display:"none"}); $("#notices_primary .notice:first").css({display:"none"});
$("#notices_primary .notice:first").fadeIn(2500); $("#notices_primary .notice:first").fadeIn(2500);
NoticeHover();
NoticeReply(); NoticeReply();
} }
} }
@ -221,24 +240,23 @@ $(document).ready(function(){
NoticeReply(); NoticeReply();
}); });
function NoticeHover() { function NoticeHover() {
$("#content .notice").hover( function mouseHandler(e) {
function () { $(e.target).closest('li.hentry')[(e.type === 'mouseover') ? 'addClass' : 'removeClass']('hover');
$(this).addClass('hover'); };
}, $('#content .notices').mouseover(mouseHandler);
function () { $('#content .notices').mouseout(mouseHandler);
$(this).removeClass('hover');
}
);
} }
function NoticeReply() { function NoticeReply() {
if ($('#notice_data-text').length > 0) { if ($('#notice_data-text').length > 0) {
$('#content .notice').each(function() { $('#content .notice').each(function() {
var notice = $(this); var notice = $(this)[0];
$('.notice_reply', $(this)).click(function() { $($('.notice_reply', notice)[0]).click(function() {
var nickname = ($('.author .nickname', notice).length > 0) ? $('.author .nickname', notice) : $('.author .nickname'); var nickname = ($('.author .nickname', notice).length > 0) ? $($('.author .nickname', notice)[0]) : $('.author .nickname');
NoticeReplySet(nickname.text(), $('.notice_id', notice).text()); NoticeReplySet(nickname.text(), $($('.notice_id', notice)[0]).text());
return false; return false;
}); });
}); });

View File

@ -79,9 +79,9 @@ class AttachmentList extends Widget
function show() function show()
{ {
// $this->out->elementStart('div', array('id' =>'attachments_primary')); $this->out->elementStart('dl', array('id' =>'attachment'));
$this->out->elementStart('div', array('id' =>'content')); $this->out->element('dt', null, _('Attachments'));
$this->out->element('h2', null, _('Attachments')); $this->out->elementStart('dd');
$this->out->elementStart('ul', array('class' => 'attachments')); $this->out->elementStart('ul', array('class' => 'attachments'));
$atts = new File; $atts = new File;
@ -91,8 +91,9 @@ class AttachmentList extends Widget
$item->show(); $item->show();
} }
$this->out->elementEnd('dd');
$this->out->elementEnd('ul'); $this->out->elementEnd('ul');
$this->out->elementEnd('div'); $this->out->elementEnd('dl');
return count($att); return count($att);
} }
@ -170,7 +171,7 @@ class AttachmentListItem extends Widget
} }
function linkTitle() { function linkTitle() {
return 'Our page for ' . $this->title(); return $this->title();
} }
/** /**
@ -190,33 +191,25 @@ class AttachmentListItem extends Widget
} }
function linkAttr() { function linkAttr() {
return array('class' => 'attachment', 'href' => common_local_url('attachment', array('attachment' => $this->attachment->id))); return array('class' => 'attachment', 'href' => $this->attachment->url, 'id' => 'attachment-' . $this->attachment->id);
} }
function showLink() { function showLink() {
$attr = $this->linkAttr(); $this->out->elementStart('a', $this->linkAttr());
$text = $this->linkTitle(); $this->out->element('span', null, $this->linkTitle());
$this->out->elementStart('h4'); $this->showRepresentation();
$this->out->element('a', $attr, $text); $this->out->elementEnd('a');
if ($this->attachment->url !== $this->title())
$this->out->element('span', null, " ({$this->attachment->url})");
$this->out->elementEnd('h4');
} }
function showNoticeAttachment() function showNoticeAttachment()
{ {
$this->showLink(); $this->showLink();
$this->showRepresentation();
} }
function showRepresentation() { function showRepresentation() {
$thumbnail = File_thumbnail::staticGet('file_id', $this->attachment->id); $thumbnail = File_thumbnail::staticGet('file_id', $this->attachment->id);
if (!empty($thumbnail)) { if (!empty($thumbnail)) {
$this->out->elementStart('a', $this->linkAttr()/*'href' => $this->linkTo()*/);
$this->out->element('img', array('alt' => 'nothing to say', 'src' => $thumbnail->url, 'width' => $thumbnail->width, 'height' => $thumbnail->height)); $this->out->element('img', array('alt' => 'nothing to say', 'src' => $thumbnail->url, 'width' => $thumbnail->width, 'height' => $thumbnail->height));
$this->out->elementEnd('a');
} }
} }
@ -258,7 +251,7 @@ class Attachment extends AttachmentListItem
} }
function linkTitle() { function linkTitle() {
return 'Direct link to ' . $this->title(); return $this->attachment->url;
} }
function showRepresentation() { function showRepresentation() {

View File

@ -1,80 +0,0 @@
<?php
/**
* Laconica, the distributed open-source microblogging tool
*
* Base class for sections showing lists of attachments
*
* 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 Widget
* @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('ATTACHMENTS_PER_SECTION', 6);
/**
* Base class for sections showing lists of attachments
*
* These are the widgets that show interesting data about a person
* group, or site.
*
* @category Widget
* @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/
*/
class AttachmentSection extends Section
{
function showContent()
{
$attachments = $this->getAttachments();
$cnt = 0;
$this->out->elementStart('ul', 'attachments');
while ($attachments->fetch() && ++$cnt <= ATTACHMENTS_PER_SECTION) {
$this->showAttachment($attachments);
}
$this->out->elementEnd('ul');
return ($cnt > ATTACHMENTS_PER_SECTION);
}
function getAttachments()
{
return null;
}
function showAttachment($attachment)
{
$this->out->elementStart('li');
$this->out->element('a', array('class' => 'attachment', 'href' => common_local_url('attachment', array('attachment' => $attachment->file_id))), "Attachment tagged {$attachment->c} times");
$this->out->elementEnd('li');
}
}

View File

@ -159,6 +159,10 @@ $config =
'newuser' => 'newuser' =>
array('subscribe' => null, array('subscribe' => null,
'welcome' => null), 'welcome' => null),
'snapshot' =>
array('run' => 'web',
'frequency' => 10000,
'reporturl' => 'http://laconi.ca/stats/report'),
); );
$config['db'] = &PEAR::getStaticProperty('DB_DataObject','options'); $config['db'] = &PEAR::getStaticProperty('DB_DataObject','options');

View File

@ -1,66 +0,0 @@
<?php
/**
* Laconica, the distributed open-source microblogging tool
*
* FIXME
*
* 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 Widget
* @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);
}
/**
* FIXME
*
* These are the widgets that show interesting data about a person
* group, or site.
*
* @category Widget
* @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/
*/
class FrequentAttachmentSection extends AttachmentSection
{
function getAttachments() {
$notice_tag = new Notice_tag;
$query = 'select file_id, count(file_id) as c from notice_tag join file_to_post on post_id = notice_id where tag="' . $notice_tag->escape($this->out->tag) . '" group by file_id order by c desc';
$notice_tag->query($query);
return $notice_tag;
}
function title()
{
return sprintf(_('Attachments frequently tagged with %s'), $this->out->tag);
}
function divId()
{
return 'frequent_attachments';
}
}

View File

@ -34,6 +34,7 @@ if (!defined('LACONICA')) {
require_once INSTALLDIR.'/lib/favorform.php'; require_once INSTALLDIR.'/lib/favorform.php';
require_once INSTALLDIR.'/lib/disfavorform.php'; require_once INSTALLDIR.'/lib/disfavorform.php';
require_once INSTALLDIR.'/lib/attachmentlist.php';
/** /**
* widget for displaying a list of notices * widget for displaying a list of notices
@ -192,45 +193,24 @@ class NoticeListItem extends Widget
$this->out->elementEnd('div'); $this->out->elementEnd('div');
} }
function showNoticeAttachments() function showNoticeAttachments() {
{ if ($this->isUsedInList()) {
$f2p = new File_to_post; return;
$f2p->post_id = $this->notice->id; }
$file = new File; $al = new AttachmentList($this->notice, $this->out);
$file->joinAdd($f2p); $al->show();
$file->selectAdd();
$file->selectAdd('file.id as id');
$count = $file->find(true);
if (!$count) return;
if (1 === $count) {
$href = common_local_url('attachment', array('attachment' => $file->id));
$att_class = 'attachment';
} else {
$href = common_local_url('attachments', array('notice' => $this->notice->id));
$att_class = 'attachments';
} }
$clip = theme_path('images/icons/clip', 'base'); function isUsedInList() {
if ('shownotice' === $this->out->args['action']) { return 'shownotice' !== $this->out->args['action'];
$height = '96px';
$width = '83%';
$width_att = '15%';
$clip .= '-big.png';
$top = '70px';
} else {
$height = '48px';
$width = '90%';
$width_att = '8%';
$clip .= '.png';
$top = '20px';
} }
if(0)
$this->out->elementStart('div', 'entry-attachments');
else
$this->out->elementStart('p', array('class' => 'entry-attachments', 'style' => "float: right; width: $width_att; background: url($clip) no-repeat; text-align: right; height: $height;"));
$this->out->element('a', array('class' => $att_class, 'style' => "text-decoration: none; padding-top: $top; display: block; height: $height;", 'href' => $href, 'title' => "# of attachments: $count"), $count === 1 ? '' : $count);
$this->out->elementEnd('p'); function attachmentCount($discriminant = true) {
$file_oembed = new File_oembed;
$query = "select count(*) as c from file_oembed join file_to_post on file_oembed.file_id = file_to_post.file_id where post_id=" . $this->notice->id;
$file_oembed->query($query);
$file_oembed->fetch();
return intval($file_oembed->c);
} }
function showNoticeInfo() function showNoticeInfo()

View File

@ -153,24 +153,17 @@ class Router
$m->connect('attachment/:attachment/ajax', $m->connect('attachment/:attachment/ajax',
array('action' => 'attachment_ajax'), array('action' => 'attachment_ajax'),
array('notice' => '[0-9]+')); array('attachment' => '[0-9]+'));
$m->connect('attachment/:attachment', $m->connect('attachment/:attachment/thumbnail',
array('action' => 'attachment'), array('action' => 'attachment_thumbnail'),
array('notice' => '[0-9]+')); array('attachment' => '[0-9]+'));
// notice
$m->connect('notice/new', array('action' => 'newnotice')); $m->connect('notice/new', array('action' => 'newnotice'));
$m->connect('notice/new?replyto=:replyto', $m->connect('notice/new?replyto=:replyto',
array('action' => 'newnotice'), array('action' => 'newnotice'),
array('replyto' => '[A-Za-z0-9_-]+')); array('replyto' => '[A-Za-z0-9_-]+'));
$m->connect('notice/:notice/attachments/ajax',
array('action' => 'attachments_ajax'),
array('notice' => '[0-9]+'));
$m->connect('notice/:notice/attachments',
array('action' => 'attachments'),
array('notice' => '[0-9]+'));
$m->connect('notice/:notice', $m->connect('notice/:notice',
array('action' => 'shownotice'), array('action' => 'shownotice'),
array('notice' => '[0-9]+')); array('notice' => '[0-9]+'));

227
lib/snapshot.php Normal file
View File

@ -0,0 +1,227 @@
<?php
/**
* Laconica, the distributed open-source microblogging tool
*
* A snapshot of site stats that can report itself to headquarters
*
* 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 Stats
* @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);
}
/**
* A snapshot of site stats that can report itself to headquarters
*
* This class will collect statistics on the site and report them to
* a statistics server of the admin's choice. (Default is the big one
* at laconi.ca.)
*
* It can either be called from a cron job, or run occasionally by the
* Web site.
*
* @category Stats
* @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/
*
*/
class Snapshot
{
var $stats = null;
/**
* Constructor for a snapshot
*/
function __construct()
{
}
/**
* Static function for reporting statistics
*
* This function checks whether it should report statistics, based on
* the current configuation settings. If it should, it creates a new
* Snapshot object, takes a snapshot, and reports it to headquarters.
*
* @return void
*/
static function check()
{
switch (common_config('snapshot', 'run')) {
case 'web':
// skip if we're not running on the Web.
if (!isset($_SERVER) || !array_key_exists('REQUEST_METHOD', $_SERVER)) {
break;
}
// Run once every frequency hits
// XXX: do frequency by time (once a week, etc.) rather than
// hits
if (rand() % common_config('snapshot', 'frequency') == 0) {
$snapshot = new Snapshot();
$snapshot->take();
$snapshot->report();
}
break;
case 'cron':
// skip if we're running on the Web
if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
break;
}
common_log(LOG_INFO, 'Running snapshot from cron job');
// We're running from the command line; assume
$snapshot = new Snapshot();
$snapshot->take();
common_log(LOG_INFO, count($snapshot->stats) . " statistics being uploaded.");
$snapshot->report();
break;
case 'never':
break;
default:
common_log(LOG_WARNING, "Unrecognized value for snapshot run config.");
}
}
/**
* Take a snapshot of the server
*
* Builds an array of statistical and configuration data based
* on the local database and config files. We avoid grabbing any
* information that could be personal or private.
*
* @return void
*/
function take()
{
$this->stats = array();
// Some basic identification stuff
$this->stats['version'] = LACONICA_VERSION;
$this->stats['phpversion'] = phpversion();
$this->stats['name'] = common_config('site', 'name');
$this->stats['root'] = common_root_url();
// non-identifying stats on various tables. Primary
// interest is size and rate of activity of service.
$tables = array('user',
'notice',
'subscription',
'remote_profile',
'user_group');
foreach ($tables as $table) {
$this->tableStats($table);
}
// stats on some important config options
$this->stats['theme'] = common_config('site', 'theme');
$this->stats['dbtype'] = common_config('db', 'type');
$this->stats['xmpp'] = common_config('xmpp', 'enabled');
$this->stats['inboxes'] = common_config('inboxes', 'enabled');
$this->stats['queue'] = common_config('queue', 'enabled');
$this->stats['license'] = common_config('license', 'url');
$this->stats['fancy'] = common_config('site', 'fancy');
$this->stats['private'] = common_config('site', 'private');
$this->stats['closed'] = common_config('site', 'closed');
$this->stats['memcached'] = common_config('memcached', 'enabled');
$this->stats['language'] = common_config('site', 'language');
$this->stats['timezone'] = common_config('site', 'timezone');
}
/**
* Reports statistics to headquarters
*
* Posts statistics to a reporting server.
*
* @return void
*/
function report()
{
// XXX: Use OICU2 and OAuth to make authorized requests
$postdata = http_build_query($this->stats);
$opts =
array('http' =>
array(
'method' => 'POST',
'header' => 'Content-type: '.
'application/x-www-form-urlencoded',
'content' => $postdata,
'user_agent' => 'Laconica/'.LACONICA_VERSION
)
);
$context = stream_context_create($opts);
$reporturl = common_config('snapshot', 'reporturl');
$result = @file_get_contents($reporturl, false, $context);
return $result;
}
/**
* Updates statistics for a single table
*
* Determines the size of a table and its oldest and newest rows.
* Goal here is to see how active a site is. Note that it
* fills up the instance stats variable.
*
* @param string $table name of table to check
*
* @return void
*/
function tableStats($table)
{
$inst = DB_DataObject::factory($table);
$inst->selectAdd();
$inst->selectAdd('count(*) as cnt, '.
'min(created) as first, '.
'max(created) as last');
if ($inst->find(true)) {
$this->stats[$table.'count'] = $inst->cnt;
$this->stats[$table.'first'] = $inst->first;
$this->stats[$table.'last'] = $inst->last;
}
$inst->free();
unset($inst);
}
}

View File

@ -496,6 +496,27 @@ function common_linkify($url) {
} }
$attrs = array('href' => $longurl, 'rel' => 'external'); $attrs = array('href' => $longurl, 'rel' => 'external');
// if this URL is an attachment, then we set class='attachment' and id='attahcment-ID'
// where ID is the id of the attachment for the given URL.
$query = "select file_oembed.file_id as file_id from file join file_oembed on file.id = file_oembed.file_id where file.url='$longurl'";
$file = new File;
$file->query($query);
$file->fetch();
if (!empty($file->file_id)) {
$query = "select file_thumbnail.file_id as file_id from file join file_thumbnail on file.id = file_thumbnail.file_id where file.url='$longurl'";
$file2 = new File;
$file2->query($query);
$file2->fetch();
if (empty($file2->file_id)) {
$attrs['class'] = 'attachment';
} else {
$attrs['class'] = 'attachment thumbnail';
}
$attrs['id'] = "attachment-{$file->file_id}";
}
return XMLStringer::estring('a', $attrs, $display); return XMLStringer::estring('a', $attrs, $display);
} }

View File

@ -0,0 +1,37 @@
#!/usr/bin/env php
<?php
/*
* Laconica - a distributed open-source microblogging tool
* Copyright (C) 2009, Control Yourself, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
# Abort if called from a web server
if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
print "This script must be run from the command line\n";
exit(1);
}
ini_set("max_execution_time", "0");
ini_set("max_input_time", "0");
set_time_limit(0);
mb_internal_encoding('UTF-8');
define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
define('LACONICA', true);
require_once(INSTALLDIR . '/lib/common.php');
Snapshot::check();

View File

@ -742,15 +742,10 @@ border-top-style:dotted;
.notices li { .notices li {
list-style-type:none; list-style-type:none;
} }
.notices li.hover {
border-radius:4px;
-moz-border-radius:4px;
-webkit-border-radius:4px;
}
.notices .notices { .notices .notices {
margin-top:7px; margin-top:7px;
margin-left:3%; margin-left:5%;
width:97%; width:95%;
float:left; float:left;
} }
@ -803,6 +798,9 @@ float:left;
width:100%; width:100%;
overflow:hidden; overflow:hidden;
} }
.notice .entry-title.ov {
overflow:visible;
}
#shownotice .notice .entry-title { #shownotice .notice .entry-title {
font-size:2.2em; font-size:2.2em;
} }
@ -827,7 +825,7 @@ clear:left;
float:left; float:left;
font-size:0.95em; font-size:0.95em;
margin-left:59px; margin-left:59px;
width:65%; width:60%;
} }
#showstream .notice div.entry-content, #showstream .notice div.entry-content,
#shownotice .notice div.entry-content { #shownotice .notice div.entry-content {
@ -857,15 +855,26 @@ display:inline-block;
text-transform:lowercase; text-transform:lowercase;
} }
.notice .attachment {
position:relative;
}
.notice .attachment img {
position:absolute;
top:18px;
left:0;
z-index:99;
}
#shownotice .notice .attachment img {
position:static;
}
.notice-options { .notice-options {
padding-left:2%;
float:left;
width:50%;
position:relative; position:relative;
font-size:0.95em; font-size:0.95em;
width:12.5%; width:90px;
float:right; float:right;
margin-right:11px;
} }
.notice-options a { .notice-options a {
@ -1049,8 +1058,6 @@ margin-left:18px;
} }
/* TOP_POSTERS */ /* TOP_POSTERS */
.section tbody td { .section tbody td {
padding-right:18px; padding-right:18px;

View File

@ -1,6 +1,3 @@
@import url("display.css");
@import url("../../identica/css/display.css");
* { * {
font-size:14px; font-size:14px;
font-family:"Lucida Sans Unicode", "Lucida Grande", sans-serif; font-family:"Lucida Sans Unicode", "Lucida Grande", sans-serif;

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -193,7 +193,9 @@ background:transparent url(../../base/images/icons/twotone/green/trash.gif) no-r
} }
.notices div.entry-content, .notices div.entry-content,
.notices div.notice-options { .notices div.notice-options,
.notices li.hover .notices div.entry-content,
.notices li.hover .notices div.notice-options {
opacity:0.4; opacity:0.4;
} }
.notices li.hover div.entry-content, .notices li.hover div.entry-content,
@ -212,16 +214,16 @@ background-color:#fcfcfc;
} }
.notices .notices { .notices .notices {
background-color:rgba(200, 200, 200, 0.025);
}
.notices .notices .notices {
background-color:rgba(200, 200, 200, 0.050); background-color:rgba(200, 200, 200, 0.050);
} }
.notices .notices .notices {
background-color:rgba(200, 200, 200, 0.100);
}
.notices .notices .notices .notices { .notices .notices .notices .notices {
background-color:rgba(200, 200, 200, 0.075); background-color:rgba(200, 200, 200, 0.150);
} }
.notices .notices .notices .notices .notices { .notices .notices .notices .notices .notices {
background-color:rgba(200, 200, 200, 0.100); background-color:rgba(200, 200, 200, 0.300);
} }
/*END: NOTICES */ /*END: NOTICES */

View File

@ -193,7 +193,9 @@ background:transparent url(../../base/images/icons/twotone/green/trash.gif) no-r
} }
.notices div.entry-content, .notices div.entry-content,
.notices div.notice-options { .notices div.notice-options,
.notices li.hover .notices div.entry-content,
.notices li.hover .notices div.notice-options {
opacity:0.4; opacity:0.4;
} }
.notices li.hover div.entry-content, .notices li.hover div.entry-content,
@ -212,16 +214,16 @@ background-color:#fcfcfc;
} }
.notices .notices { .notices .notices {
background-color:rgba(200, 200, 200, 0.025);
}
.notices .notices .notices {
background-color:rgba(200, 200, 200, 0.050); background-color:rgba(200, 200, 200, 0.050);
} }
.notices .notices .notices {
background-color:rgba(200, 200, 200, 0.100);
}
.notices .notices .notices .notices { .notices .notices .notices .notices {
background-color:rgba(200, 200, 200, 0.075); background-color:rgba(200, 200, 200, 0.150);
} }
.notices .notices .notices .notices .notices { .notices .notices .notices .notices .notices {
background-color:rgba(200, 200, 200, 0.100); background-color:rgba(200, 200, 200, 0.300);
} }
/*END: NOTICES */ /*END: NOTICES */