forked from GNUsocial/gnu-social
Merge branch '0.8.x' into userdesign
Conflicts: lib/attachmentlist.php lib/noticelist.php
This commit is contained in:
commit
648d967226
26
README
26
README
@ -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
|
||||||
===============
|
===============
|
||||||
|
|
||||||
|
@ -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 (empty($this->attachment)) {
|
||||||
|
|
||||||
if (!$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');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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
|
||||||
*
|
*
|
||||||
|
@ -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)) . '"';
|
||||||
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
@ -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()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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()
|
||||||
|
@ -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';
|
||||||
|
@ -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()),
|
||||||
|
@ -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."));
|
||||||
|
18
install.php
18
install.php
@ -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">
|
||||||
|
50
js/util.js
50
js/util.js
@ -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;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -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() {
|
||||||
|
@ -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');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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');
|
||||||
|
@ -97,11 +97,11 @@ class FacebookAction extends Action
|
|||||||
{
|
{
|
||||||
// Add a timestamp to the file so Facebook cache wont ignore our changes
|
// Add a timestamp to the file so Facebook cache wont ignore our changes
|
||||||
$ts = filemtime(INSTALLDIR.'/theme/base/css/display.css');
|
$ts = filemtime(INSTALLDIR.'/theme/base/css/display.css');
|
||||||
|
|
||||||
|
$this->element('link', array('rel' => 'stylesheet',
|
||||||
|
'type' => 'text/css',
|
||||||
|
'href' => theme_path('css/display.css', 'base') . '?ts=' . $ts));
|
||||||
|
|
||||||
$this->element('link', array('rel' => 'stylesheet',
|
|
||||||
'type' => 'text/css',
|
|
||||||
'href' => theme_path('css/display.css', 'base') . '?ts=' . $ts));
|
|
||||||
|
|
||||||
$theme = common_config('site', 'theme');
|
$theme = common_config('site', 'theme');
|
||||||
|
|
||||||
$ts = filemtime(INSTALLDIR. '/theme/' . $theme .'/css/display.css');
|
$ts = filemtime(INSTALLDIR. '/theme/' . $theme .'/css/display.css');
|
||||||
|
@ -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';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
|
||||||
$file->joinAdd($f2p);
|
|
||||||
$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';
|
|
||||||
}
|
}
|
||||||
|
$al = new AttachmentList($this->notice, $this->out);
|
||||||
|
$al->show();
|
||||||
|
}
|
||||||
|
|
||||||
$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()
|
||||||
|
@ -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
227
lib/snapshot.php
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
21
lib/util.php
21
lib/util.php
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
37
scripts/reportsnapshot.php
Normal file
37
scripts/reportsnapshot.php
Normal 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();
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
BIN
theme/base/images/icons/clip-inline.png
Normal file
BIN
theme/base/images/icons/clip-inline.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
@ -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 */
|
||||||
|
|
||||||
|
@ -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 */
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user