Merge branch 'master' into 0.9.x
This commit is contained in:
commit
b09276635c
40
EVENTS.txt
40
EVENTS.txt
|
@ -1057,3 +1057,43 @@ StartCloseNoticeListItemElement: Before the closing </li> of a notice list eleme
|
|||
|
||||
EndCloseNoticeListItemElement: After the closing </li> of a notice list element
|
||||
- $nli: The notice list item being shown
|
||||
|
||||
StartGroupEditFormData: Beginning the group edit form entries
|
||||
- $form: The form widget being shown
|
||||
|
||||
EndGroupEditFormData: Ending the group edit form entries
|
||||
- $form: The form widget being shown
|
||||
|
||||
StartGroupSave: After initializing but before saving a group
|
||||
- &$group: group about to be saved
|
||||
|
||||
EndGroupSave: After saving a group, aliases, and first member
|
||||
- $group: group that was saved
|
||||
|
||||
StartInterpretCommand: Before running a command
|
||||
- $cmd: First word in the string, 'foo' in 'foo argument'
|
||||
- $arg: Argument, if any, like 'argument' in 'foo argument'
|
||||
- $user: User who issued the command
|
||||
- &$result: Resulting command; you can set this!
|
||||
|
||||
EndInterpretCommand: Before running a command
|
||||
- $cmd: First word in the string, 'foo' in 'foo argument'
|
||||
- $arg: Argument, if any, like 'argument' in 'foo argument'
|
||||
- $user: User who issued the command
|
||||
- $result: Resulting command
|
||||
|
||||
StartGroupActionsList: Start the list of actions on a group profile page (after <ul>, before first <li>)
|
||||
- $action: action being executed (for output and params)
|
||||
- $group: group for the page
|
||||
|
||||
EndGroupActionsList: End the list of actions on a group profile page (before </ul>, after last </li>)
|
||||
- $action: action being executed (for output and params)
|
||||
- $group: group for the page
|
||||
|
||||
StartGroupProfileElements: Start showing stuff about the group on its profile page
|
||||
- $action: action being executed (for output and params)
|
||||
- $group: group for the page
|
||||
|
||||
EndGroupProfileElements: Start showing stuff about the group on its profile page
|
||||
- $action: action being executed (for output and params)
|
||||
- $group: group for the page
|
||||
|
|
19
README
19
README
|
@ -1279,7 +1279,7 @@ biolimit: max character length of bio; 0 means no limit; null means to use
|
|||
backup: whether users can backup their own profiles. Defaults to true.
|
||||
restore: whether users can restore their profiles from backup files. Defaults
|
||||
to true.
|
||||
delete: whether users can delete their own accounts. Defaults to true.
|
||||
delete: whether users can delete their own accounts. Defaults to false.
|
||||
move: whether users can move their accounts to another server. Defaults
|
||||
to true.
|
||||
|
||||
|
@ -1572,6 +1572,23 @@ proxy_user: Username to use for authenticating to the HTTP proxy. Default null.
|
|||
proxy_password: Password to use for authenticating to the HTTP proxy. Default null.
|
||||
proxy_auth_scheme: Scheme to use for authenticating to the HTTP proxy. Default null.
|
||||
|
||||
plugins
|
||||
-------
|
||||
|
||||
default: associative array mapping plugin name to array of arguments. To disable
|
||||
a default plugin, unset its value in this array.
|
||||
locale_path: path for finding plugin locale files. In the plugin's directory
|
||||
by default.
|
||||
server: Server to find static files for a plugin when the page is plain old HTTP.
|
||||
Defaults to site/server (same as pages). Use this to move plugin CSS and
|
||||
JS files to a CDN.
|
||||
sslserver: Server to find static files for a plugin when the page is HTTPS. Defaults
|
||||
to site/server (same as pages). Use this to move plugin CSS and JS files
|
||||
to a CDN.
|
||||
path: Path to the plugin files. defaults to site/path + '/plugins/'. Expects that
|
||||
each plugin will have a subdirectory at plugins/NameOfPlugin. Change this
|
||||
if you're using a CDN.
|
||||
|
||||
Plugins
|
||||
=======
|
||||
|
||||
|
|
|
@ -177,6 +177,8 @@ class EditgroupAction extends GroupDesignAction
|
|||
return;
|
||||
}
|
||||
|
||||
if (Event::handle('StartGroupSaveForm', array($this))) {
|
||||
|
||||
$nickname = Nickname::normalize($this->trimmed('nickname'));
|
||||
$fullname = $this->trimmed('fullname');
|
||||
$homepage = $this->trimmed('homepage');
|
||||
|
@ -287,6 +289,9 @@ class EditgroupAction extends GroupDesignAction
|
|||
|
||||
$this->group->query('COMMIT');
|
||||
|
||||
Event::handle('EndGroupSaveForm', array($this));
|
||||
}
|
||||
|
||||
if ($this->group->nickname != $orig->nickname) {
|
||||
common_redirect(common_local_url('editgroup',
|
||||
array('nickname' => $nickname)),
|
||||
|
|
|
@ -90,18 +90,9 @@ class InboxAction extends MailboxAction
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the profile we want to show with the message
|
||||
*
|
||||
* For inboxes, we show the sender; for outboxes, the recipient.
|
||||
*
|
||||
* @param Message $message The message to get the profile for
|
||||
*
|
||||
* @return Profile The profile that matches the message
|
||||
*/
|
||||
function getMessageProfile($message)
|
||||
function getMessageList($message)
|
||||
{
|
||||
return $message->getFrom();
|
||||
return new InboxMessageList($this, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -115,3 +106,24 @@ class InboxAction extends MailboxAction
|
|||
return _('This is your inbox, which lists your incoming private messages.');
|
||||
}
|
||||
}
|
||||
|
||||
class InboxMessageList extends MessageList
|
||||
{
|
||||
function newItem($message)
|
||||
{
|
||||
return new InboxMessageListItem($this->out, $message);
|
||||
}
|
||||
}
|
||||
|
||||
class InboxMessageListItem extends MessageListItem
|
||||
{
|
||||
/**
|
||||
* Returns the profile we want to show with the message
|
||||
*
|
||||
* @return Profile The profile that matches the message
|
||||
*/
|
||||
function getMessageProfile()
|
||||
{
|
||||
return $this->message->getFrom();
|
||||
}
|
||||
}
|
|
@ -120,6 +120,7 @@ class NewgroupAction extends Action
|
|||
|
||||
function trySave()
|
||||
{
|
||||
if (Event::handle('StartGroupSaveForm', array($this))) {
|
||||
try {
|
||||
$nickname = Nickname::normalize($this->trimmed('nickname'));
|
||||
} catch (NicknameException $e) {
|
||||
|
@ -216,8 +217,13 @@ class NewgroupAction extends Action
|
|||
'userid' => $cur->id,
|
||||
'local' => true));
|
||||
|
||||
$this->group = $group;
|
||||
|
||||
Event::handle('EndGroupSaveForm', array($this));
|
||||
|
||||
common_redirect($group->homeUrl(), 303);
|
||||
}
|
||||
}
|
||||
|
||||
function nicknameExists($nickname)
|
||||
{
|
||||
|
|
|
@ -88,21 +88,9 @@ class OutboxAction extends MailboxAction
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the profile we want to show with the message
|
||||
*
|
||||
* For outboxes, we show the recipient.
|
||||
*
|
||||
* @param Message $message The message to get the profile for
|
||||
*
|
||||
* @return Profile The profile of the message recipient
|
||||
*
|
||||
* @see MailboxAction::getMessageProfile()
|
||||
*/
|
||||
|
||||
function getMessageProfile($message)
|
||||
function getMessageList($message)
|
||||
{
|
||||
return $message->getTo();
|
||||
return new OutboxMessageList($this, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -116,3 +104,24 @@ class OutboxAction extends MailboxAction
|
|||
return _('This is your outbox, which lists private messages you have sent.');
|
||||
}
|
||||
}
|
||||
|
||||
class OutboxMessageList extends MessageList
|
||||
{
|
||||
function newItem($message)
|
||||
{
|
||||
return new OutboxMessageListItem($this->out, $message);
|
||||
}
|
||||
}
|
||||
|
||||
class OutboxMessageListItem extends MessageListItem
|
||||
{
|
||||
/**
|
||||
* Returns the profile we want to show with the message
|
||||
*
|
||||
* @return Profile The profile that matches the message
|
||||
*/
|
||||
function getMessageProfile()
|
||||
{
|
||||
return $this->message->getTo();
|
||||
}
|
||||
}
|
|
@ -181,6 +181,7 @@ class ShowgroupAction extends GroupDesignAction
|
|||
function showContent()
|
||||
{
|
||||
$this->showGroupProfile();
|
||||
$this->showGroupActions();
|
||||
$this->showGroupNotices();
|
||||
}
|
||||
|
||||
|
@ -216,6 +217,8 @@ class ShowgroupAction extends GroupDesignAction
|
|||
$this->elementStart('div', array('id' => 'i',
|
||||
'class' => 'entity_profile vcard author'));
|
||||
|
||||
if (Event::handle('StartGroupProfileElements', array($this, $this->group))) {
|
||||
|
||||
// TRANS: Group profile header (h2). Text hidden by default.
|
||||
$this->element('h2', null, _('Group profile'));
|
||||
|
||||
|
@ -296,13 +299,20 @@ class ShowgroupAction extends GroupDesignAction
|
|||
}
|
||||
}
|
||||
|
||||
$this->elementEnd('div');
|
||||
Event::handle('EndGroupProfileElements', array($this, $this->group));
|
||||
}
|
||||
|
||||
$this->elementEnd('div');
|
||||
}
|
||||
|
||||
function showGroupActions()
|
||||
{
|
||||
$cur = common_current_user();
|
||||
$this->elementStart('div', 'entity_actions');
|
||||
// TRANS: Group actions header (h2). Text hidden by default.
|
||||
$this->element('h2', null, _('Group actions'));
|
||||
$this->elementStart('ul');
|
||||
if (Event::handle('StartGroupActionsList', array($this, $this->group))) {
|
||||
$this->elementStart('li', 'entity_subscribe');
|
||||
if (Event::handle('StartGroupSubscribe', array($this, $this->group))) {
|
||||
if ($cur) {
|
||||
|
@ -323,6 +333,8 @@ class ShowgroupAction extends GroupDesignAction
|
|||
$df->show();
|
||||
$this->elementEnd('li');
|
||||
}
|
||||
Event::handle('EndGroupActionsList', array($this, $this->group));
|
||||
}
|
||||
$this->elementEnd('ul');
|
||||
$this->elementEnd('div');
|
||||
}
|
||||
|
|
|
@ -30,20 +30,17 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
|
|||
exit(1);
|
||||
}
|
||||
|
||||
require_once INSTALLDIR.'/lib/mailbox.php';
|
||||
|
||||
/**
|
||||
* Show a single message
|
||||
*
|
||||
* // XXX: It is totally weird how this works!
|
||||
*
|
||||
* @category Personal
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
class ShowmessageAction extends MailboxAction
|
||||
|
||||
class ShowmessageAction extends Action
|
||||
{
|
||||
/**
|
||||
* Message object to show
|
||||
|
@ -82,22 +79,20 @@ class ShowmessageAction extends MailboxAction
|
|||
|
||||
$this->user = common_current_user();
|
||||
|
||||
if (empty($this->user) ||
|
||||
($this->user->id != $this->message->from_profile &&
|
||||
$this->user->id != $this->message->to_profile)) {
|
||||
// TRANS: Client error displayed requesting a single direct message the requesting user was not a party in.
|
||||
throw new ClientException(_('Only the sender and recipient ' .
|
||||
'may read this message.'), 403);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function handle($args)
|
||||
{
|
||||
Action::handle($args);
|
||||
|
||||
if ($this->user && ($this->user->id == $this->message->from_profile ||
|
||||
$this->user->id == $this->message->to_profile)) {
|
||||
$this->showPage();
|
||||
} else {
|
||||
// TRANS: Client error displayed requesting a single direct message the requesting user was not a party in.
|
||||
$this->clientError(_('Only the sender and recipient ' .
|
||||
'may read this message.'), 403);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function title()
|
||||
|
@ -121,12 +116,38 @@ class ShowmessageAction extends MailboxAction
|
|||
}
|
||||
}
|
||||
|
||||
function getMessages()
|
||||
|
||||
function showContent()
|
||||
{
|
||||
$message = new Message();
|
||||
$message->id = $this->message->id;
|
||||
$message->find();
|
||||
return $message;
|
||||
$this->elementStart('ul', 'notices messages');
|
||||
$ml = new ShowMessageListItem($this, $this->message, $this->user);
|
||||
$ml->show();
|
||||
$this->elementEnd('ul');
|
||||
}
|
||||
|
||||
function isReadOnly($args)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't show aside
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
||||
function showAside() {
|
||||
}
|
||||
}
|
||||
|
||||
class ShowMessageListItem extends MessageListItem
|
||||
{
|
||||
var $user;
|
||||
|
||||
function __construct($out, $message, $user)
|
||||
{
|
||||
parent::__construct($out, $message);
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
function getMessageProfile()
|
||||
|
@ -140,46 +161,4 @@ class ShowmessageAction extends MailboxAction
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't show local navigation
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function showLocalNavBlock()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't show page notice
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function showPageNoticeBlock()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't show aside
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function showAside()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't show any instructions
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function getInstructions()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
function isReadOnly($args)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,14 +55,20 @@ class File extends Memcached_DataObject
|
|||
return 'http://www.facebook.com/login.php' === $url;
|
||||
}
|
||||
|
||||
function getAttachments($post_id) {
|
||||
$query = "select file.* from file join file_to_post on (file_id = file.id) join notice on (post_id = notice.id) where post_id = " . $this->escape($post_id);
|
||||
$this->query($query);
|
||||
/**
|
||||
* Get the attachments for a particlar notice.
|
||||
*
|
||||
* @param int $post_id
|
||||
* @return array of File objects
|
||||
*/
|
||||
static function getAttachments($post_id) {
|
||||
$file = new File();
|
||||
$query = "select file.* from file join file_to_post on (file_id = file.id) where post_id = " . $file->escape($post_id);
|
||||
$file = Memcached_DataObject::cachedQuery('File', $query);
|
||||
$att = array();
|
||||
while ($this->fetch()) {
|
||||
$att[] = clone($this);
|
||||
while ($file->fetch()) {
|
||||
$att[] = clone($file);
|
||||
}
|
||||
$this->free();
|
||||
return $att;
|
||||
}
|
||||
|
||||
|
|
|
@ -340,6 +340,7 @@ class Memcached_DataObject extends Safe_DataObject
|
|||
$start = microtime(true);
|
||||
$result = null;
|
||||
if (Event::handle('StartDBQuery', array($this, $string, &$result))) {
|
||||
common_perf_counter('query', $string);
|
||||
$result = parent::_query($string);
|
||||
Event::handle('EndDBQuery', array($this, $string, &$result));
|
||||
}
|
||||
|
|
|
@ -446,7 +446,10 @@ class Notice extends Memcached_DataObject
|
|||
function blowOnInsert($conversation = false)
|
||||
{
|
||||
self::blow('profile:notice_ids:%d', $this->profile_id);
|
||||
|
||||
if ($this->isPublic()) {
|
||||
self::blow('public');
|
||||
}
|
||||
|
||||
// XXX: Before we were blowing the casche only if the notice id
|
||||
// was not the root of the conversation. What to do now?
|
||||
|
@ -481,8 +484,11 @@ class Notice extends Memcached_DataObject
|
|||
$this->blowOnInsert();
|
||||
|
||||
self::blow('profile:notice_ids:%d;last', $this->profile_id);
|
||||
|
||||
if ($this->isPublic()) {
|
||||
self::blow('public;last');
|
||||
}
|
||||
}
|
||||
|
||||
/** save all urls in the notice to the db
|
||||
*
|
||||
|
@ -958,7 +964,7 @@ class Notice extends Memcached_DataObject
|
|||
$groups = array();
|
||||
|
||||
/* extract all !group */
|
||||
$count = preg_match_all('/(?:^|\s)!([A-Za-z0-9]{1,64})/',
|
||||
$count = preg_match_all('/(?:^|\s)!(' . Nickname::DISPLAY_FMT . ')/',
|
||||
strtolower($this->content),
|
||||
$match);
|
||||
if (!$count) {
|
||||
|
@ -2107,4 +2113,14 @@ class Notice extends Memcached_DataObject
|
|||
$obj->whereAdd($max);
|
||||
}
|
||||
}
|
||||
|
||||
function isPublic()
|
||||
{
|
||||
if (common_config('public', 'localonly')) {
|
||||
return ($this->is_local == Notice::LOCAL_PUBLIC);
|
||||
} else {
|
||||
return (($this->is_local != Notice::LOCAL_NONPUBLIC) &&
|
||||
($this->is_local != Notice::GATEWAY));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -918,6 +918,31 @@ class Profile extends Memcached_DataObject
|
|||
return $xs->getString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra profile info for atom entries
|
||||
*
|
||||
* Clients use some extra profile info in the atom stream.
|
||||
* This gives it to them.
|
||||
*
|
||||
* @param User $cur Current user
|
||||
*
|
||||
* @return array representation of <statusnet:profile_info> element
|
||||
*/
|
||||
|
||||
function profileInfo($cur)
|
||||
{
|
||||
$profileInfoAttr = array();
|
||||
|
||||
if ($cur != null) {
|
||||
// Whether the current user is a subscribed to this profile
|
||||
$profileInfoAttr['following'] = $cur->isSubscribed($this) ? 'true' : 'false';
|
||||
// Whether the current user is has blocked this profile
|
||||
$profileInfoAttr['blocking'] = $cur->hasBlocked($this) ? 'true' : 'false';
|
||||
}
|
||||
|
||||
return array('statusnet:profile_info', $profileInfoAttr, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an XML string fragment with profile information as an
|
||||
* Activity Streams <activity:actor> element.
|
||||
|
|
|
@ -156,6 +156,13 @@ class Session extends Memcached_DataObject
|
|||
$session->selectAdd();
|
||||
$session->selectAdd('id');
|
||||
|
||||
$limit = common_config('sessions', 'gc_limit');
|
||||
if ($limit > 0) {
|
||||
// On large sites, too many sessions to expire
|
||||
// at once will just result in failure.
|
||||
$session->limit($limit);
|
||||
}
|
||||
|
||||
$session->find();
|
||||
|
||||
while ($session->fetch()) {
|
||||
|
|
|
@ -512,6 +512,8 @@ class User_group extends Memcached_DataObject
|
|||
$group->mainpage = $mainpage;
|
||||
$group->created = common_sql_now();
|
||||
|
||||
if (Event::handle('StartGroupSave', array(&$group))) {
|
||||
|
||||
$result = $group->insert();
|
||||
|
||||
if (!$result) {
|
||||
|
@ -570,6 +572,10 @@ class User_group extends Memcached_DataObject
|
|||
}
|
||||
|
||||
$group->query('COMMIT');
|
||||
|
||||
Event::handle('EndGroupSave', array($group));
|
||||
}
|
||||
|
||||
return $group;
|
||||
}
|
||||
|
||||
|
|
104
extlib/Auth/SASL.php
Normal file
104
extlib/Auth/SASL.php
Normal file
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Copyright (c) 2002-2003 Richard Heyes |
|
||||
// | All rights reserved. |
|
||||
// | |
|
||||
// | Redistribution and use in source and binary forms, with or without |
|
||||
// | modification, are permitted provided that the following conditions |
|
||||
// | are met: |
|
||||
// | |
|
||||
// | o Redistributions of source code must retain the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer. |
|
||||
// | o Redistributions in binary form must reproduce the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer in the |
|
||||
// | documentation and/or other materials provided with the distribution.|
|
||||
// | o The names of the authors may not be used to endorse or promote |
|
||||
// | products derived from this software without specific prior written |
|
||||
// | permission. |
|
||||
// | |
|
||||
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
||||
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
||||
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
||||
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
||||
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
||||
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
||||
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
||||
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
||||
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
||||
// | |
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Author: Richard Heyes <richard@php.net> |
|
||||
// +-----------------------------------------------------------------------+
|
||||
//
|
||||
// $Id: SASL.php 286825 2009-08-05 06:23:42Z cweiske $
|
||||
|
||||
/**
|
||||
* Client implementation of various SASL mechanisms
|
||||
*
|
||||
* @author Richard Heyes <richard@php.net>
|
||||
* @access public
|
||||
* @version 1.0
|
||||
* @package Auth_SASL
|
||||
*/
|
||||
|
||||
require_once('PEAR.php');
|
||||
|
||||
class Auth_SASL
|
||||
{
|
||||
/**
|
||||
* Factory class. Returns an object of the request
|
||||
* type.
|
||||
*
|
||||
* @param string $type One of: Anonymous
|
||||
* Plain
|
||||
* CramMD5
|
||||
* DigestMD5
|
||||
* Types are not case sensitive
|
||||
*/
|
||||
function &factory($type)
|
||||
{
|
||||
switch (strtolower($type)) {
|
||||
case 'anonymous':
|
||||
$filename = 'Auth/SASL/Anonymous.php';
|
||||
$classname = 'Auth_SASL_Anonymous';
|
||||
break;
|
||||
|
||||
case 'login':
|
||||
$filename = 'Auth/SASL/Login.php';
|
||||
$classname = 'Auth_SASL_Login';
|
||||
break;
|
||||
|
||||
case 'plain':
|
||||
$filename = 'Auth/SASL/Plain.php';
|
||||
$classname = 'Auth_SASL_Plain';
|
||||
break;
|
||||
|
||||
case 'external':
|
||||
$filename = 'Auth/SASL/External.php';
|
||||
$classname = 'Auth_SASL_External';
|
||||
break;
|
||||
|
||||
case 'crammd5':
|
||||
$filename = 'Auth/SASL/CramMD5.php';
|
||||
$classname = 'Auth_SASL_CramMD5';
|
||||
break;
|
||||
|
||||
case 'digestmd5':
|
||||
$filename = 'Auth/SASL/DigestMD5.php';
|
||||
$classname = 'Auth_SASL_DigestMD5';
|
||||
break;
|
||||
|
||||
default:
|
||||
return PEAR::raiseError('Invalid SASL mechanism type');
|
||||
break;
|
||||
}
|
||||
|
||||
require_once($filename);
|
||||
$obj = new $classname();
|
||||
return $obj;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
71
extlib/Auth/SASL/Anonymous.php
Normal file
71
extlib/Auth/SASL/Anonymous.php
Normal file
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Copyright (c) 2002-2003 Richard Heyes |
|
||||
// | All rights reserved. |
|
||||
// | |
|
||||
// | Redistribution and use in source and binary forms, with or without |
|
||||
// | modification, are permitted provided that the following conditions |
|
||||
// | are met: |
|
||||
// | |
|
||||
// | o Redistributions of source code must retain the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer. |
|
||||
// | o Redistributions in binary form must reproduce the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer in the |
|
||||
// | documentation and/or other materials provided with the distribution.|
|
||||
// | o The names of the authors may not be used to endorse or promote |
|
||||
// | products derived from this software without specific prior written |
|
||||
// | permission. |
|
||||
// | |
|
||||
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
||||
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
||||
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
||||
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
||||
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
||||
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
||||
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
||||
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
||||
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
||||
// | |
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Author: Richard Heyes <richard@php.net> |
|
||||
// +-----------------------------------------------------------------------+
|
||||
//
|
||||
// $Id: Anonymous.php 286825 2009-08-05 06:23:42Z cweiske $
|
||||
|
||||
/**
|
||||
* Implmentation of ANONYMOUS SASL mechanism
|
||||
*
|
||||
* @author Richard Heyes <richard@php.net>
|
||||
* @access public
|
||||
* @version 1.0
|
||||
* @package Auth_SASL
|
||||
*/
|
||||
|
||||
require_once('Auth/SASL/Common.php');
|
||||
|
||||
class Auth_SASL_Anonymous extends Auth_SASL_Common
|
||||
{
|
||||
/**
|
||||
* Not much to do here except return the token supplied.
|
||||
* No encoding, hashing or encryption takes place for this
|
||||
* mechanism, simply one of:
|
||||
* o An email address
|
||||
* o An opaque string not containing "@" that can be interpreted
|
||||
* by the sysadmin
|
||||
* o Nothing
|
||||
*
|
||||
* We could have some logic here for the second option, but this
|
||||
* would by no means create something interpretable.
|
||||
*
|
||||
* @param string $token Optional email address or string to provide
|
||||
* as trace information.
|
||||
* @return string The unaltered input token
|
||||
*/
|
||||
function getResponse($token = '')
|
||||
{
|
||||
return $token;
|
||||
}
|
||||
}
|
||||
?>
|
74
extlib/Auth/SASL/Common.php
Normal file
74
extlib/Auth/SASL/Common.php
Normal file
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Copyright (c) 2002-2003 Richard Heyes |
|
||||
// | All rights reserved. |
|
||||
// | |
|
||||
// | Redistribution and use in source and binary forms, with or without |
|
||||
// | modification, are permitted provided that the following conditions |
|
||||
// | are met: |
|
||||
// | |
|
||||
// | o Redistributions of source code must retain the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer. |
|
||||
// | o Redistributions in binary form must reproduce the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer in the |
|
||||
// | documentation and/or other materials provided with the distribution.|
|
||||
// | o The names of the authors may not be used to endorse or promote |
|
||||
// | products derived from this software without specific prior written |
|
||||
// | permission. |
|
||||
// | |
|
||||
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
||||
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
||||
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
||||
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
||||
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
||||
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
||||
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
||||
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
||||
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
||||
// | |
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Author: Richard Heyes <richard@php.net> |
|
||||
// +-----------------------------------------------------------------------+
|
||||
//
|
||||
// $Id: Common.php 286825 2009-08-05 06:23:42Z cweiske $
|
||||
|
||||
/**
|
||||
* Common functionality to SASL mechanisms
|
||||
*
|
||||
* @author Richard Heyes <richard@php.net>
|
||||
* @access public
|
||||
* @version 1.0
|
||||
* @package Auth_SASL
|
||||
*/
|
||||
|
||||
class Auth_SASL_Common
|
||||
{
|
||||
/**
|
||||
* Function which implements HMAC MD5 digest
|
||||
*
|
||||
* @param string $key The secret key
|
||||
* @param string $data The data to protect
|
||||
* @return string The HMAC MD5 digest
|
||||
*/
|
||||
function _HMAC_MD5($key, $data)
|
||||
{
|
||||
if (strlen($key) > 64) {
|
||||
$key = pack('H32', md5($key));
|
||||
}
|
||||
|
||||
if (strlen($key) < 64) {
|
||||
$key = str_pad($key, 64, chr(0));
|
||||
}
|
||||
|
||||
$k_ipad = substr($key, 0, 64) ^ str_repeat(chr(0x36), 64);
|
||||
$k_opad = substr($key, 0, 64) ^ str_repeat(chr(0x5C), 64);
|
||||
|
||||
$inner = pack('H32', md5($k_ipad . $data));
|
||||
$digest = md5($k_opad . $inner);
|
||||
|
||||
return $digest;
|
||||
}
|
||||
}
|
||||
?>
|
68
extlib/Auth/SASL/CramMD5.php
Normal file
68
extlib/Auth/SASL/CramMD5.php
Normal file
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Copyright (c) 2002-2003 Richard Heyes |
|
||||
// | All rights reserved. |
|
||||
// | |
|
||||
// | Redistribution and use in source and binary forms, with or without |
|
||||
// | modification, are permitted provided that the following conditions |
|
||||
// | are met: |
|
||||
// | |
|
||||
// | o Redistributions of source code must retain the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer. |
|
||||
// | o Redistributions in binary form must reproduce the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer in the |
|
||||
// | documentation and/or other materials provided with the distribution.|
|
||||
// | o The names of the authors may not be used to endorse or promote |
|
||||
// | products derived from this software without specific prior written |
|
||||
// | permission. |
|
||||
// | |
|
||||
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
||||
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
||||
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
||||
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
||||
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
||||
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
||||
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
||||
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
||||
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
||||
// | |
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Author: Richard Heyes <richard@php.net> |
|
||||
// +-----------------------------------------------------------------------+
|
||||
//
|
||||
// $Id: CramMD5.php 286825 2009-08-05 06:23:42Z cweiske $
|
||||
|
||||
/**
|
||||
* Implmentation of CRAM-MD5 SASL mechanism
|
||||
*
|
||||
* @author Richard Heyes <richard@php.net>
|
||||
* @access public
|
||||
* @version 1.0
|
||||
* @package Auth_SASL
|
||||
*/
|
||||
|
||||
require_once('Auth/SASL/Common.php');
|
||||
|
||||
class Auth_SASL_CramMD5 extends Auth_SASL_Common
|
||||
{
|
||||
/**
|
||||
* Implements the CRAM-MD5 SASL mechanism
|
||||
* This DOES NOT base64 encode the return value,
|
||||
* you will need to do that yourself.
|
||||
*
|
||||
* @param string $user Username
|
||||
* @param string $pass Password
|
||||
* @param string $challenge The challenge supplied by the server.
|
||||
* this should be already base64_decoded.
|
||||
*
|
||||
* @return string The string to pass back to the server, of the form
|
||||
* "<user> <digest>". This is NOT base64_encoded.
|
||||
*/
|
||||
function getResponse($user, $pass, $challenge)
|
||||
{
|
||||
return $user . ' ' . $this->_HMAC_MD5($pass, $challenge);
|
||||
}
|
||||
}
|
||||
?>
|
197
extlib/Auth/SASL/DigestMD5.php
Normal file
197
extlib/Auth/SASL/DigestMD5.php
Normal file
|
@ -0,0 +1,197 @@
|
|||
<?php
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Copyright (c) 2002-2003 Richard Heyes |
|
||||
// | All rights reserved. |
|
||||
// | |
|
||||
// | Redistribution and use in source and binary forms, with or without |
|
||||
// | modification, are permitted provided that the following conditions |
|
||||
// | are met: |
|
||||
// | |
|
||||
// | o Redistributions of source code must retain the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer. |
|
||||
// | o Redistributions in binary form must reproduce the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer in the |
|
||||
// | documentation and/or other materials provided with the distribution.|
|
||||
// | o The names of the authors may not be used to endorse or promote |
|
||||
// | products derived from this software without specific prior written |
|
||||
// | permission. |
|
||||
// | |
|
||||
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
||||
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
||||
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
||||
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
||||
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
||||
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
||||
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
||||
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
||||
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
||||
// | |
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Author: Richard Heyes <richard@php.net> |
|
||||
// +-----------------------------------------------------------------------+
|
||||
//
|
||||
// $Id: DigestMD5.php 294702 2010-02-07 16:03:55Z cweiske $
|
||||
|
||||
/**
|
||||
* Implmentation of DIGEST-MD5 SASL mechanism
|
||||
*
|
||||
* @author Richard Heyes <richard@php.net>
|
||||
* @access public
|
||||
* @version 1.0
|
||||
* @package Auth_SASL
|
||||
*/
|
||||
|
||||
require_once('Auth/SASL/Common.php');
|
||||
|
||||
class Auth_SASL_DigestMD5 extends Auth_SASL_Common
|
||||
{
|
||||
/**
|
||||
* Provides the (main) client response for DIGEST-MD5
|
||||
* requires a few extra parameters than the other
|
||||
* mechanisms, which are unavoidable.
|
||||
*
|
||||
* @param string $authcid Authentication id (username)
|
||||
* @param string $pass Password
|
||||
* @param string $challenge The digest challenge sent by the server
|
||||
* @param string $hostname The hostname of the machine you're connecting to
|
||||
* @param string $service The servicename (eg. imap, pop, acap etc)
|
||||
* @param string $authzid Authorization id (username to proxy as)
|
||||
* @return string The digest response (NOT base64 encoded)
|
||||
* @access public
|
||||
*/
|
||||
function getResponse($authcid, $pass, $challenge, $hostname, $service, $authzid = '')
|
||||
{
|
||||
$challenge = $this->_parseChallenge($challenge);
|
||||
$authzid_string = '';
|
||||
if ($authzid != '') {
|
||||
$authzid_string = ',authzid="' . $authzid . '"';
|
||||
}
|
||||
|
||||
if (!empty($challenge)) {
|
||||
$cnonce = $this->_getCnonce();
|
||||
$digest_uri = sprintf('%s/%s', $service, $hostname);
|
||||
$response_value = $this->_getResponseValue($authcid, $pass, $challenge['realm'], $challenge['nonce'], $cnonce, $digest_uri, $authzid);
|
||||
|
||||
if ($challenge['realm']) {
|
||||
return sprintf('username="%s",realm="%s"' . $authzid_string .
|
||||
',nonce="%s",cnonce="%s",nc=00000001,qop=auth,digest-uri="%s",response=%s,maxbuf=%d', $authcid, $challenge['realm'], $challenge['nonce'], $cnonce, $digest_uri, $response_value, $challenge['maxbuf']);
|
||||
} else {
|
||||
return sprintf('username="%s"' . $authzid_string . ',nonce="%s",cnonce="%s",nc=00000001,qop=auth,digest-uri="%s",response=%s,maxbuf=%d', $authcid, $challenge['nonce'], $cnonce, $digest_uri, $response_value, $challenge['maxbuf']);
|
||||
}
|
||||
} else {
|
||||
return PEAR::raiseError('Invalid digest challenge');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses and verifies the digest challenge*
|
||||
*
|
||||
* @param string $challenge The digest challenge
|
||||
* @return array The parsed challenge as an assoc
|
||||
* array in the form "directive => value".
|
||||
* @access private
|
||||
*/
|
||||
function _parseChallenge($challenge)
|
||||
{
|
||||
$tokens = array();
|
||||
while (preg_match('/^([a-z-]+)=("[^"]+(?<!\\\)"|[^,]+)/i', $challenge, $matches)) {
|
||||
|
||||
// Ignore these as per rfc2831
|
||||
if ($matches[1] == 'opaque' OR $matches[1] == 'domain') {
|
||||
$challenge = substr($challenge, strlen($matches[0]) + 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Allowed multiple "realm" and "auth-param"
|
||||
if (!empty($tokens[$matches[1]]) AND ($matches[1] == 'realm' OR $matches[1] == 'auth-param')) {
|
||||
if (is_array($tokens[$matches[1]])) {
|
||||
$tokens[$matches[1]][] = preg_replace('/^"(.*)"$/', '\\1', $matches[2]);
|
||||
} else {
|
||||
$tokens[$matches[1]] = array($tokens[$matches[1]], preg_replace('/^"(.*)"$/', '\\1', $matches[2]));
|
||||
}
|
||||
|
||||
// Any other multiple instance = failure
|
||||
} elseif (!empty($tokens[$matches[1]])) {
|
||||
$tokens = array();
|
||||
break;
|
||||
|
||||
} else {
|
||||
$tokens[$matches[1]] = preg_replace('/^"(.*)"$/', '\\1', $matches[2]);
|
||||
}
|
||||
|
||||
// Remove the just parsed directive from the challenge
|
||||
$challenge = substr($challenge, strlen($matches[0]) + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defaults and required directives
|
||||
*/
|
||||
// Realm
|
||||
if (empty($tokens['realm'])) {
|
||||
$tokens['realm'] = "";
|
||||
}
|
||||
|
||||
// Maxbuf
|
||||
if (empty($tokens['maxbuf'])) {
|
||||
$tokens['maxbuf'] = 65536;
|
||||
}
|
||||
|
||||
// Required: nonce, algorithm
|
||||
if (empty($tokens['nonce']) OR empty($tokens['algorithm'])) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the response= part of the digest response
|
||||
*
|
||||
* @param string $authcid Authentication id (username)
|
||||
* @param string $pass Password
|
||||
* @param string $realm Realm as provided by the server
|
||||
* @param string $nonce Nonce as provided by the server
|
||||
* @param string $cnonce Client nonce
|
||||
* @param string $digest_uri The digest-uri= value part of the response
|
||||
* @param string $authzid Authorization id
|
||||
* @return string The response= part of the digest response
|
||||
* @access private
|
||||
*/
|
||||
function _getResponseValue($authcid, $pass, $realm, $nonce, $cnonce, $digest_uri, $authzid = '')
|
||||
{
|
||||
if ($authzid == '') {
|
||||
$A1 = sprintf('%s:%s:%s', pack('H32', md5(sprintf('%s:%s:%s', $authcid, $realm, $pass))), $nonce, $cnonce);
|
||||
} else {
|
||||
$A1 = sprintf('%s:%s:%s:%s', pack('H32', md5(sprintf('%s:%s:%s', $authcid, $realm, $pass))), $nonce, $cnonce, $authzid);
|
||||
}
|
||||
$A2 = 'AUTHENTICATE:' . $digest_uri;
|
||||
return md5(sprintf('%s:%s:00000001:%s:auth:%s', md5($A1), $nonce, $cnonce, md5($A2)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the client nonce for the response
|
||||
*
|
||||
* @return string The cnonce value
|
||||
* @access private
|
||||
*/
|
||||
function _getCnonce()
|
||||
{
|
||||
if (@file_exists('/dev/urandom') && $fd = @fopen('/dev/urandom', 'r')) {
|
||||
return base64_encode(fread($fd, 32));
|
||||
|
||||
} elseif (@file_exists('/dev/random') && $fd = @fopen('/dev/random', 'r')) {
|
||||
return base64_encode(fread($fd, 32));
|
||||
|
||||
} else {
|
||||
$str = '';
|
||||
for ($i=0; $i<32; $i++) {
|
||||
$str .= chr(mt_rand(0, 255));
|
||||
}
|
||||
|
||||
return base64_encode($str);
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
63
extlib/Auth/SASL/External.php
Normal file
63
extlib/Auth/SASL/External.php
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Copyright (c) 2008 Christoph Schulz |
|
||||
// | All rights reserved. |
|
||||
// | |
|
||||
// | Redistribution and use in source and binary forms, with or without |
|
||||
// | modification, are permitted provided that the following conditions |
|
||||
// | are met: |
|
||||
// | |
|
||||
// | o Redistributions of source code must retain the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer. |
|
||||
// | o Redistributions in binary form must reproduce the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer in the |
|
||||
// | documentation and/or other materials provided with the distribution.|
|
||||
// | o The names of the authors may not be used to endorse or promote |
|
||||
// | products derived from this software without specific prior written |
|
||||
// | permission. |
|
||||
// | |
|
||||
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
||||
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
||||
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
||||
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
||||
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
||||
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
||||
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
||||
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
||||
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
||||
// | |
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Author: Christoph Schulz <develop@kristov.de> |
|
||||
// +-----------------------------------------------------------------------+
|
||||
//
|
||||
// $Id: External.php 286825 2009-08-05 06:23:42Z cweiske $
|
||||
|
||||
/**
|
||||
* Implmentation of EXTERNAL SASL mechanism
|
||||
*
|
||||
* @author Christoph Schulz <develop@kristov.de>
|
||||
* @access public
|
||||
* @version 1.0.3
|
||||
* @package Auth_SASL
|
||||
*/
|
||||
|
||||
require_once('Auth/SASL/Common.php');
|
||||
|
||||
class Auth_SASL_External extends Auth_SASL_Common
|
||||
{
|
||||
/**
|
||||
* Returns EXTERNAL response
|
||||
*
|
||||
* @param string $authcid Authentication id (username)
|
||||
* @param string $pass Password
|
||||
* @param string $authzid Autorization id
|
||||
* @return string EXTERNAL Response
|
||||
*/
|
||||
function getResponse($authcid, $pass, $authzid = '')
|
||||
{
|
||||
return $authzid;
|
||||
}
|
||||
}
|
||||
?>
|
65
extlib/Auth/SASL/Login.php
Normal file
65
extlib/Auth/SASL/Login.php
Normal file
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Copyright (c) 2002-2003 Richard Heyes |
|
||||
// | All rights reserved. |
|
||||
// | |
|
||||
// | Redistribution and use in source and binary forms, with or without |
|
||||
// | modification, are permitted provided that the following conditions |
|
||||
// | are met: |
|
||||
// | |
|
||||
// | o Redistributions of source code must retain the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer. |
|
||||
// | o Redistributions in binary form must reproduce the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer in the |
|
||||
// | documentation and/or other materials provided with the distribution.|
|
||||
// | o The names of the authors may not be used to endorse or promote |
|
||||
// | products derived from this software without specific prior written |
|
||||
// | permission. |
|
||||
// | |
|
||||
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
||||
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
||||
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
||||
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
||||
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
||||
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
||||
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
||||
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
||||
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
||||
// | |
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Author: Richard Heyes <richard@php.net> |
|
||||
// +-----------------------------------------------------------------------+
|
||||
//
|
||||
// $Id: Login.php 286825 2009-08-05 06:23:42Z cweiske $
|
||||
|
||||
/**
|
||||
* This is technically not a SASL mechanism, however
|
||||
* it's used by Net_Sieve, Net_Cyrus and potentially
|
||||
* other protocols , so here is a good place to abstract
|
||||
* it.
|
||||
*
|
||||
* @author Richard Heyes <richard@php.net>
|
||||
* @access public
|
||||
* @version 1.0
|
||||
* @package Auth_SASL
|
||||
*/
|
||||
|
||||
require_once('Auth/SASL/Common.php');
|
||||
|
||||
class Auth_SASL_Login extends Auth_SASL_Common
|
||||
{
|
||||
/**
|
||||
* Pseudo SASL LOGIN mechanism
|
||||
*
|
||||
* @param string $user Username
|
||||
* @param string $pass Password
|
||||
* @return string LOGIN string
|
||||
*/
|
||||
function getResponse($user, $pass)
|
||||
{
|
||||
return sprintf('LOGIN %s %s', $user, $pass);
|
||||
}
|
||||
}
|
||||
?>
|
63
extlib/Auth/SASL/Plain.php
Normal file
63
extlib/Auth/SASL/Plain.php
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Copyright (c) 2002-2003 Richard Heyes |
|
||||
// | All rights reserved. |
|
||||
// | |
|
||||
// | Redistribution and use in source and binary forms, with or without |
|
||||
// | modification, are permitted provided that the following conditions |
|
||||
// | are met: |
|
||||
// | |
|
||||
// | o Redistributions of source code must retain the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer. |
|
||||
// | o Redistributions in binary form must reproduce the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer in the |
|
||||
// | documentation and/or other materials provided with the distribution.|
|
||||
// | o The names of the authors may not be used to endorse or promote |
|
||||
// | products derived from this software without specific prior written |
|
||||
// | permission. |
|
||||
// | |
|
||||
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
||||
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
||||
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
||||
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
||||
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
||||
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
||||
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
||||
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
||||
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
||||
// | |
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Author: Richard Heyes <richard@php.net> |
|
||||
// +-----------------------------------------------------------------------+
|
||||
//
|
||||
// $Id: Plain.php 286825 2009-08-05 06:23:42Z cweiske $
|
||||
|
||||
/**
|
||||
* Implmentation of PLAIN SASL mechanism
|
||||
*
|
||||
* @author Richard Heyes <richard@php.net>
|
||||
* @access public
|
||||
* @version 1.0
|
||||
* @package Auth_SASL
|
||||
*/
|
||||
|
||||
require_once('Auth/SASL/Common.php');
|
||||
|
||||
class Auth_SASL_Plain extends Auth_SASL_Common
|
||||
{
|
||||
/**
|
||||
* Returns PLAIN response
|
||||
*
|
||||
* @param string $authcid Authentication id (username)
|
||||
* @param string $pass Password
|
||||
* @param string $authzid Autorization id
|
||||
* @return string PLAIN Response
|
||||
*/
|
||||
function getResponse($authcid, $pass, $authzid = '')
|
||||
{
|
||||
return $authzid . chr(0) . $authcid . chr(0) . $pass;
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -38,6 +38,7 @@
|
|||
*/
|
||||
|
||||
$_startTime = microtime(true);
|
||||
$_perfCounters = array();
|
||||
|
||||
define('INSTALLDIR', dirname(__FILE__));
|
||||
define('STATUSNET', true);
|
||||
|
@ -45,6 +46,8 @@ define('LACONICA', true); // compatibility
|
|||
|
||||
require_once INSTALLDIR . '/lib/common.php';
|
||||
|
||||
register_shutdown_function('common_log_perf_counters');
|
||||
|
||||
$user = null;
|
||||
$action = null;
|
||||
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
/*!
|
||||
/*
|
||||
* jQuery Form Plugin
|
||||
* version: 2.63 (29-JAN-2011)
|
||||
* @requires jQuery v1.3.2 or later
|
||||
* version: 2.17 (06-NOV-2008)
|
||||
* @requires jQuery v1.2.2 or later
|
||||
*
|
||||
* Examples and documentation at: http://malsup.com/jquery/form/
|
||||
* Dual licensed under the MIT and GPL licenses:
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*
|
||||
* Revision: $Id$
|
||||
*/
|
||||
;(function($) {
|
||||
|
||||
|
@ -18,11 +20,11 @@
|
|||
to bind your own submit handler to the form. For example,
|
||||
|
||||
$(document).ready(function() {
|
||||
$('#myForm').bind('submit', function(e) {
|
||||
e.preventDefault(); // <-- important
|
||||
$('#myForm').bind('submit', function() {
|
||||
$(this).ajaxSubmit({
|
||||
target: '#output'
|
||||
});
|
||||
return false; // <-- important!
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -50,23 +52,13 @@ $.fn.ajaxSubmit = function(options) {
|
|||
return this;
|
||||
}
|
||||
|
||||
if (typeof options == 'function') {
|
||||
if (typeof options == 'function')
|
||||
options = { success: options };
|
||||
}
|
||||
|
||||
var action = this.attr('action');
|
||||
var url = (typeof action === 'string') ? $.trim(action) : '';
|
||||
if (url) {
|
||||
// clean url (don't include hash vaue)
|
||||
url = (url.match(/^([^#]+)/)||[])[1];
|
||||
}
|
||||
url = url || window.location.href || '';
|
||||
|
||||
options = $.extend(true, {
|
||||
url: url,
|
||||
type: this[0].getAttribute('method') || 'GET', // IE7 massage (see issue 57)
|
||||
iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
|
||||
}, options);
|
||||
options = $.extend({
|
||||
url: this.attr('action') || window.location.toString(),
|
||||
type: this.attr('method') || 'GET'
|
||||
}, options || {});
|
||||
|
||||
// hook for manipulating the form data before it is extracted;
|
||||
// convenient for use with rich editors like tinyMCE or FCKEditor
|
||||
|
@ -83,20 +75,16 @@ $.fn.ajaxSubmit = function(options) {
|
|||
return this;
|
||||
}
|
||||
|
||||
var n,v,a = this.formToArray(options.semantic);
|
||||
var a = this.formToArray(options.semantic);
|
||||
if (options.data) {
|
||||
options.extraData = options.data;
|
||||
for (n in options.data) {
|
||||
for (var n in options.data) {
|
||||
if(options.data[n] instanceof Array) {
|
||||
for (var k in options.data[n]) {
|
||||
a.push( { name: n, value: options.data[n][k] } );
|
||||
}
|
||||
}
|
||||
else {
|
||||
v = options.data[n];
|
||||
v = $.isFunction(v) ? v() : v; // if value is fn, invoke it
|
||||
a.push( { name: n, value: v } );
|
||||
for (var k in options.data[n])
|
||||
a.push( { name: n, value: options.data[n][k] } )
|
||||
}
|
||||
else
|
||||
a.push( { name: n, value: options.data[n] } );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,57 +107,46 @@ $.fn.ajaxSubmit = function(options) {
|
|||
options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
|
||||
options.data = null; // data is null for 'get'
|
||||
}
|
||||
else {
|
||||
else
|
||||
options.data = q; // data is the query string for 'post'
|
||||
}
|
||||
|
||||
var $form = this, callbacks = [];
|
||||
if (options.resetForm) {
|
||||
callbacks.push(function() { $form.resetForm(); });
|
||||
}
|
||||
if (options.clearForm) {
|
||||
callbacks.push(function() { $form.clearForm(); });
|
||||
}
|
||||
if (options.resetForm) callbacks.push(function() { $form.resetForm(); });
|
||||
if (options.clearForm) callbacks.push(function() { $form.clearForm(); });
|
||||
|
||||
// perform a load on the target only if dataType is not provided
|
||||
if (!options.dataType && options.target) {
|
||||
var oldSuccess = options.success || function(){};
|
||||
callbacks.push(function(data) {
|
||||
var fn = options.replaceTarget ? 'replaceWith' : 'html';
|
||||
$(options.target)[fn](data).each(oldSuccess, arguments);
|
||||
$(options.target).html(data).each(oldSuccess, arguments);
|
||||
});
|
||||
}
|
||||
else if (options.success) {
|
||||
else if (options.success)
|
||||
callbacks.push(options.success);
|
||||
}
|
||||
|
||||
options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
|
||||
var context = options.context || options; // jQuery 1.4+ supports scope context
|
||||
for (var i=0, max=callbacks.length; i < max; i++) {
|
||||
callbacks[i].apply(context, [data, status, xhr || $form, $form]);
|
||||
}
|
||||
options.success = function(data, status) {
|
||||
for (var i=0, max=callbacks.length; i < max; i++)
|
||||
callbacks[i].apply(options, [data, status, $form]);
|
||||
};
|
||||
|
||||
// are there files to upload?
|
||||
var fileInputs = $('input:file', this).length > 0;
|
||||
var mp = 'multipart/form-data';
|
||||
var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
|
||||
var files = $('input:file', this).fieldValue();
|
||||
var found = false;
|
||||
for (var j=0; j < files.length; j++)
|
||||
if (files[j])
|
||||
found = true;
|
||||
|
||||
// options.iframe allows user to force iframe mode
|
||||
// 06-NOV-09: now defaulting to iframe mode if file input is detected
|
||||
if (options.iframe !== false && (fileInputs || options.iframe || multipart)) {
|
||||
if (options.iframe || found) {
|
||||
// hack to fix Safari hang (thanks to Tim Molendijk for this)
|
||||
// see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
|
||||
if (options.closeKeepAlive) {
|
||||
if ($.browser.safari && options.closeKeepAlive)
|
||||
$.get(options.closeKeepAlive, fileUpload);
|
||||
}
|
||||
else {
|
||||
else
|
||||
fileUpload();
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
$.ajax(options);
|
||||
}
|
||||
|
||||
// fire 'notify' event
|
||||
this.trigger('form-submit-notify', [this, options]);
|
||||
|
@ -180,19 +157,20 @@ $.fn.ajaxSubmit = function(options) {
|
|||
function fileUpload() {
|
||||
var form = $form[0];
|
||||
|
||||
if ($(':input[name=submit],:input[id=submit]', form).length) {
|
||||
// if there is an input with a name or id of 'submit' then we won't be
|
||||
// able to invoke the submit fn on the form (at least not x-browser)
|
||||
alert('Error: Form elements must not have name or id of "submit".');
|
||||
if ($(':input[name=submit]', form).length) {
|
||||
alert('Error: Form elements must not be named "submit".');
|
||||
return;
|
||||
}
|
||||
|
||||
var s = $.extend(true, {}, $.ajaxSettings, options);
|
||||
s.context = s.context || s;
|
||||
var id = 'jqFormIO' + (new Date().getTime()), fn = '_'+id;
|
||||
var $io = $('<iframe id="' + id + '" name="' + id + '" src="'+ s.iframeSrc +'" />');
|
||||
var opts = $.extend({}, $.ajaxSettings, options);
|
||||
var s = jQuery.extend(true, {}, $.extend(true, {}, $.ajaxSettings), opts);
|
||||
|
||||
var id = 'jqFormIO' + (new Date().getTime());
|
||||
var $io = $('<iframe id="' + id + '" name="' + id + '" />');
|
||||
var io = $io[0];
|
||||
|
||||
if ($.browser.msie || $.browser.opera)
|
||||
io.src = 'javascript:false;document.write("");';
|
||||
$io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
|
||||
|
||||
var xhr = { // mock object
|
||||
|
@ -206,29 +184,23 @@ $.fn.ajaxSubmit = function(options) {
|
|||
setRequestHeader: function() {},
|
||||
abort: function() {
|
||||
this.aborted = 1;
|
||||
$io.attr('src', s.iframeSrc); // abort op in progress
|
||||
$io.attr('src','about:blank'); // abort op in progress
|
||||
}
|
||||
};
|
||||
|
||||
var g = s.global;
|
||||
var g = opts.global;
|
||||
// trigger ajax global events so that activity/block indicators work like normal
|
||||
if (g && ! $.active++) {
|
||||
$.event.trigger("ajaxStart");
|
||||
}
|
||||
if (g) {
|
||||
$.event.trigger("ajaxSend", [xhr, s]);
|
||||
}
|
||||
if (g && ! $.active++) $.event.trigger("ajaxStart");
|
||||
if (g) $.event.trigger("ajaxSend", [xhr, opts]);
|
||||
|
||||
if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
|
||||
if (s.global) {
|
||||
$.active--;
|
||||
}
|
||||
if (s.beforeSend && s.beforeSend(xhr, s) === false) {
|
||||
s.global && jQuery.active--;
|
||||
return;
|
||||
}
|
||||
if (xhr.aborted) {
|
||||
if (xhr.aborted)
|
||||
return;
|
||||
}
|
||||
|
||||
var cbInvoked = 0;
|
||||
var timedOut = 0;
|
||||
|
||||
// add submitting element to data if we know it
|
||||
|
@ -236,31 +208,27 @@ $.fn.ajaxSubmit = function(options) {
|
|||
if (sub) {
|
||||
var n = sub.name;
|
||||
if (n && !sub.disabled) {
|
||||
s.extraData = s.extraData || {};
|
||||
s.extraData[n] = sub.value;
|
||||
options.extraData = options.extraData || {};
|
||||
options.extraData[n] = sub.value;
|
||||
if (sub.type == "image") {
|
||||
s.extraData[n+'.x'] = form.clk_x;
|
||||
s.extraData[n+'.y'] = form.clk_y;
|
||||
options.extraData[name+'.x'] = form.clk_x;
|
||||
options.extraData[name+'.y'] = form.clk_y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// take a breath so that pending repaints get some cpu time before the upload starts
|
||||
function doSubmit() {
|
||||
setTimeout(function() {
|
||||
// make sure form attrs are set
|
||||
var t = $form.attr('target'), a = $form.attr('action');
|
||||
|
||||
// update form attrs in IE friendly way
|
||||
form.setAttribute('target',id);
|
||||
if (form.getAttribute('method') != 'POST') {
|
||||
form.setAttribute('method', 'POST');
|
||||
}
|
||||
if (form.getAttribute('action') != s.url) {
|
||||
form.setAttribute('action', s.url);
|
||||
}
|
||||
$form.attr({
|
||||
target: id,
|
||||
method: 'POST',
|
||||
action: opts.url
|
||||
});
|
||||
|
||||
// ie borks in some cases when setting encoding
|
||||
if (! s.skipEncodingOverride) {
|
||||
if (! options.skipEncodingOverride) {
|
||||
$form.attr({
|
||||
encoding: 'multipart/form-data',
|
||||
enctype: 'multipart/form-data'
|
||||
|
@ -268,20 +236,17 @@ $.fn.ajaxSubmit = function(options) {
|
|||
}
|
||||
|
||||
// support timout
|
||||
if (s.timeout) {
|
||||
setTimeout(function() { timedOut = true; cb(); }, s.timeout);
|
||||
}
|
||||
if (opts.timeout)
|
||||
setTimeout(function() { timedOut = true; cb(); }, opts.timeout);
|
||||
|
||||
// add "extra" data to form if provided in options
|
||||
var extraInputs = [];
|
||||
try {
|
||||
if (s.extraData) {
|
||||
for (var n in s.extraData) {
|
||||
if (options.extraData)
|
||||
for (var n in options.extraData)
|
||||
extraInputs.push(
|
||||
$('<input type="hidden" name="'+n+'" value="'+s.extraData[n]+'" />')
|
||||
$('<input type="hidden" name="'+n+'" value="'+options.extraData[n]+'" />')
|
||||
.appendTo(form)[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// add iframe to doc and submit the form
|
||||
$io.appendTo('body');
|
||||
|
@ -290,158 +255,83 @@ $.fn.ajaxSubmit = function(options) {
|
|||
}
|
||||
finally {
|
||||
// reset attrs and remove "extra" input elements
|
||||
form.setAttribute('action',a);
|
||||
if(t) {
|
||||
form.setAttribute('target', t);
|
||||
} else {
|
||||
$form.removeAttr('target');
|
||||
}
|
||||
$form.attr('action', a);
|
||||
t ? $form.attr('target', t) : $form.removeAttr('target');
|
||||
$(extraInputs).remove();
|
||||
}
|
||||
}
|
||||
|
||||
if (s.forceSync) {
|
||||
doSubmit();
|
||||
}
|
||||
else {
|
||||
setTimeout(doSubmit, 10); // this lets dom updates render
|
||||
}
|
||||
|
||||
var data, doc, domCheckCount = 50;
|
||||
}, 10);
|
||||
|
||||
function cb() {
|
||||
doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;
|
||||
if (!doc || doc.location.href == s.iframeSrc) {
|
||||
// response not received yet
|
||||
return;
|
||||
}
|
||||
if (cbInvoked++) return;
|
||||
|
||||
io.detachEvent ? io.detachEvent('onload', cb) : io.removeEventListener('load', cb, false);
|
||||
|
||||
var operaHack = 0;
|
||||
var ok = true;
|
||||
try {
|
||||
if (timedOut) {
|
||||
throw 'timeout';
|
||||
}
|
||||
if (timedOut) throw 'timeout';
|
||||
// extract the server response from the iframe
|
||||
var data, doc;
|
||||
|
||||
var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
|
||||
log('isXml='+isXml);
|
||||
if (!isXml && window.opera && (doc.body == null || doc.body.innerHTML == '')) {
|
||||
if (--domCheckCount) {
|
||||
// in some browsers (Opera) the iframe DOM is not always traversable when
|
||||
// the onload callback fires, so we loop a bit to accommodate
|
||||
log('requeing onLoad callback, DOM not available');
|
||||
setTimeout(cb, 250);
|
||||
doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;
|
||||
|
||||
if (doc.body == null && !operaHack && $.browser.opera) {
|
||||
// In Opera 9.2.x the iframe DOM is not always traversable when
|
||||
// the onload callback fires so we give Opera 100ms to right itself
|
||||
operaHack = 1;
|
||||
cbInvoked--;
|
||||
setTimeout(cb, 100);
|
||||
return;
|
||||
}
|
||||
// let this fall through because server response could be an empty document
|
||||
//log('Could not access iframe DOM after mutiple tries.');
|
||||
//throw 'DOMException: not available';
|
||||
}
|
||||
|
||||
//log('response detected');
|
||||
xhr.responseText = doc.body ? doc.body.innerHTML : doc.documentElement ? doc.documentElement.innerHTML : null;
|
||||
xhr.responseText = doc.body ? doc.body.innerHTML : null;
|
||||
xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
|
||||
xhr.getResponseHeader = function(header){
|
||||
var headers = {'content-type': s.dataType};
|
||||
var headers = {'content-type': opts.dataType};
|
||||
return headers[header];
|
||||
};
|
||||
|
||||
var scr = /(json|script)/.test(s.dataType);
|
||||
if (scr || s.textarea) {
|
||||
// see if user embedded response in textarea
|
||||
if (opts.dataType == 'json' || opts.dataType == 'script') {
|
||||
var ta = doc.getElementsByTagName('textarea')[0];
|
||||
if (ta) {
|
||||
xhr.responseText = ta.value;
|
||||
xhr.responseText = ta ? ta.value : xhr.responseText;
|
||||
}
|
||||
else if (scr) {
|
||||
// account for browsers injecting pre around json response
|
||||
var pre = doc.getElementsByTagName('pre')[0];
|
||||
var b = doc.getElementsByTagName('body')[0];
|
||||
if (pre) {
|
||||
xhr.responseText = pre.textContent;
|
||||
}
|
||||
else if (b) {
|
||||
xhr.responseText = b.innerHTML;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (s.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) {
|
||||
else if (opts.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) {
|
||||
xhr.responseXML = toXml(xhr.responseText);
|
||||
}
|
||||
|
||||
data = httpData(xhr, s.dataType, s);
|
||||
data = $.httpData(xhr, opts.dataType);
|
||||
}
|
||||
catch(e){
|
||||
log('error caught:',e);
|
||||
ok = false;
|
||||
xhr.error = e;
|
||||
s.error.call(s.context, xhr, 'error', e);
|
||||
g && $.event.trigger("ajaxError", [xhr, s, e]);
|
||||
}
|
||||
|
||||
if (xhr.aborted) {
|
||||
log('upload aborted');
|
||||
ok = false;
|
||||
$.handleError(opts, xhr, 'error', e);
|
||||
}
|
||||
|
||||
// ordering of these callbacks/triggers is odd, but that's how $.ajax does it
|
||||
if (ok) {
|
||||
s.success.call(s.context, data, 'success', xhr);
|
||||
g && $.event.trigger("ajaxSuccess", [xhr, s]);
|
||||
opts.success(data, 'success');
|
||||
if (g) $.event.trigger("ajaxSuccess", [xhr, opts]);
|
||||
}
|
||||
|
||||
g && $.event.trigger("ajaxComplete", [xhr, s]);
|
||||
|
||||
if (g && ! --$.active) {
|
||||
$.event.trigger("ajaxStop");
|
||||
}
|
||||
|
||||
s.complete && s.complete.call(s.context, xhr, ok ? 'success' : 'error');
|
||||
if (g) $.event.trigger("ajaxComplete", [xhr, opts]);
|
||||
if (g && ! --$.active) $.event.trigger("ajaxStop");
|
||||
if (opts.complete) opts.complete(xhr, ok ? 'success' : 'error');
|
||||
|
||||
// clean up
|
||||
setTimeout(function() {
|
||||
$io.removeData('form-plugin-onload');
|
||||
$io.remove();
|
||||
xhr.responseXML = null;
|
||||
}, 100);
|
||||
}
|
||||
};
|
||||
|
||||
var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
|
||||
function toXml(s, doc) {
|
||||
if (window.ActiveXObject) {
|
||||
doc = new ActiveXObject('Microsoft.XMLDOM');
|
||||
doc.async = 'false';
|
||||
doc.loadXML(s);
|
||||
}
|
||||
else {
|
||||
else
|
||||
doc = (new DOMParser()).parseFromString(s, 'text/xml');
|
||||
}
|
||||
return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
|
||||
return (doc && doc.documentElement && doc.documentElement.tagName != 'parsererror') ? doc : null;
|
||||
};
|
||||
var parseJSON = $.parseJSON || function(s) {
|
||||
return window['eval']('(' + s + ')');
|
||||
};
|
||||
|
||||
var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
|
||||
var ct = xhr.getResponseHeader('content-type') || '',
|
||||
xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
|
||||
data = xml ? xhr.responseXML : xhr.responseText;
|
||||
|
||||
if (xml && data.documentElement.nodeName === 'parsererror') {
|
||||
$.error && $.error('parsererror');
|
||||
}
|
||||
if (s && s.dataFilter) {
|
||||
data = s.dataFilter(data, type);
|
||||
}
|
||||
if (typeof data === 'string') {
|
||||
if (type === 'json' || !type && ct.indexOf('json') >= 0) {
|
||||
data = parseJSON(data);
|
||||
} else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
|
||||
$.globalEval(data);
|
||||
}
|
||||
}
|
||||
return data;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -460,60 +350,40 @@ $.fn.ajaxSubmit = function(options) {
|
|||
* the form itself.
|
||||
*/
|
||||
$.fn.ajaxForm = function(options) {
|
||||
// in jQuery 1.3+ we can fix mistakes with the ready state
|
||||
if (this.length === 0) {
|
||||
var o = { s: this.selector, c: this.context };
|
||||
if (!$.isReady && o.s) {
|
||||
log('DOM not ready, queuing ajaxForm');
|
||||
$(function() {
|
||||
$(o.s,o.c).ajaxForm(options);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
// is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
|
||||
log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
|
||||
return this;
|
||||
}
|
||||
|
||||
return this.ajaxFormUnbind().bind('submit.form-plugin', function(e) {
|
||||
if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
|
||||
e.preventDefault();
|
||||
return this.ajaxFormUnbind().bind('submit.form-plugin',function() {
|
||||
$(this).ajaxSubmit(options);
|
||||
}
|
||||
}).bind('click.form-plugin', function(e) {
|
||||
var target = e.target;
|
||||
var $el = $(target);
|
||||
if (!($el.is(":submit,input:image"))) {
|
||||
// is this a child element of the submit el? (ex: a span within a button)
|
||||
var t = $el.closest(':submit');
|
||||
if (t.length == 0) {
|
||||
return;
|
||||
}
|
||||
target = t[0];
|
||||
}
|
||||
var form = this;
|
||||
form.clk = target;
|
||||
if (target.type == 'image') {
|
||||
return false;
|
||||
}).each(function() {
|
||||
// store options in hash
|
||||
$(":submit,input:image", this).bind('click.form-plugin',function(e) {
|
||||
var form = this.form;
|
||||
form.clk = this;
|
||||
if (this.type == 'image') {
|
||||
if (e.offsetX != undefined) {
|
||||
form.clk_x = e.offsetX;
|
||||
form.clk_y = e.offsetY;
|
||||
} else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
|
||||
var offset = $el.offset();
|
||||
var offset = $(this).offset();
|
||||
form.clk_x = e.pageX - offset.left;
|
||||
form.clk_y = e.pageY - offset.top;
|
||||
} else {
|
||||
form.clk_x = e.pageX - target.offsetLeft;
|
||||
form.clk_y = e.pageY - target.offsetTop;
|
||||
form.clk_x = e.pageX - this.offsetLeft;
|
||||
form.clk_y = e.pageY - this.offsetTop;
|
||||
}
|
||||
}
|
||||
// clear form vars
|
||||
setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
|
||||
setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 10);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
|
||||
$.fn.ajaxFormUnbind = function() {
|
||||
return this.unbind('submit.form-plugin click.form-plugin');
|
||||
this.unbind('submit.form-plugin');
|
||||
return this.each(function() {
|
||||
$(":submit,input:image", this).unbind('click.form-plugin');
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -529,50 +399,39 @@ $.fn.ajaxFormUnbind = function() {
|
|||
*/
|
||||
$.fn.formToArray = function(semantic) {
|
||||
var a = [];
|
||||
if (this.length === 0) {
|
||||
return a;
|
||||
}
|
||||
if (this.length == 0) return a;
|
||||
|
||||
var form = this[0];
|
||||
var els = semantic ? form.getElementsByTagName('*') : form.elements;
|
||||
if (!els) {
|
||||
return a;
|
||||
}
|
||||
|
||||
var i,j,n,v,el,max,jmax;
|
||||
for(i=0, max=els.length; i < max; i++) {
|
||||
el = els[i];
|
||||
n = el.name;
|
||||
if (!n) {
|
||||
continue;
|
||||
}
|
||||
if (!els) return a;
|
||||
for(var i=0, max=els.length; i < max; i++) {
|
||||
var el = els[i];
|
||||
var n = el.name;
|
||||
if (!n) continue;
|
||||
|
||||
if (semantic && form.clk && el.type == "image") {
|
||||
// handle image inputs on the fly when semantic == true
|
||||
if(!el.disabled && form.clk == el) {
|
||||
a.push({name: n, value: $(el).val()});
|
||||
if(!el.disabled && form.clk == el)
|
||||
a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
v = $.fieldValue(el, true);
|
||||
var v = $.fieldValue(el, true);
|
||||
if (v && v.constructor == Array) {
|
||||
for(j=0, jmax=v.length; j < jmax; j++) {
|
||||
for(var j=0, jmax=v.length; j < jmax; j++)
|
||||
a.push({name: n, value: v[j]});
|
||||
}
|
||||
}
|
||||
else if (v !== null && typeof v != 'undefined') {
|
||||
else if (v !== null && typeof v != 'undefined')
|
||||
a.push({name: n, value: v});
|
||||
}
|
||||
}
|
||||
|
||||
if (!semantic && form.clk) {
|
||||
// input type=='image' are not found in elements array! handle it here
|
||||
var $input = $(form.clk), input = $input[0];
|
||||
n = input.name;
|
||||
if (n && !input.disabled && input.type == 'image') {
|
||||
a.push({name: n, value: $input.val()});
|
||||
// input type=='image' are not found in elements array! handle them here
|
||||
var inputs = form.getElementsByTagName("input");
|
||||
for(var i=0, max=inputs.length; i < max; i++) {
|
||||
var input = inputs[i];
|
||||
var n = input.name;
|
||||
if(n && !input.disabled && input.type == "image" && form.clk == input)
|
||||
a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
|
||||
}
|
||||
}
|
||||
|
@ -596,18 +455,14 @@ $.fn.fieldSerialize = function(successful) {
|
|||
var a = [];
|
||||
this.each(function() {
|
||||
var n = this.name;
|
||||
if (!n) {
|
||||
return;
|
||||
}
|
||||
if (!n) return;
|
||||
var v = $.fieldValue(this, successful);
|
||||
if (v && v.constructor == Array) {
|
||||
for (var i=0,max=v.length; i < max; i++) {
|
||||
for (var i=0,max=v.length; i < max; i++)
|
||||
a.push({name: n, value: v[i]});
|
||||
}
|
||||
}
|
||||
else if (v !== null && typeof v != 'undefined') {
|
||||
else if (v !== null && typeof v != 'undefined')
|
||||
a.push({name: this.name, value: v});
|
||||
}
|
||||
});
|
||||
//hand off to jQuery.param for proper encoding
|
||||
return $.param(a);
|
||||
|
@ -655,9 +510,8 @@ $.fn.fieldValue = function(successful) {
|
|||
for (var val=[], i=0, max=this.length; i < max; i++) {
|
||||
var el = this[i];
|
||||
var v = $.fieldValue(el, successful);
|
||||
if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
|
||||
if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length))
|
||||
continue;
|
||||
}
|
||||
v.constructor == Array ? $.merge(val, v) : val.push(v);
|
||||
}
|
||||
return val;
|
||||
|
@ -668,41 +522,32 @@ $.fn.fieldValue = function(successful) {
|
|||
*/
|
||||
$.fieldValue = function(el, successful) {
|
||||
var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
|
||||
if (successful === undefined) {
|
||||
successful = true;
|
||||
}
|
||||
if (typeof successful == 'undefined') successful = true;
|
||||
|
||||
if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
|
||||
(t == 'checkbox' || t == 'radio') && !el.checked ||
|
||||
(t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
|
||||
tag == 'select' && el.selectedIndex == -1)) {
|
||||
tag == 'select' && el.selectedIndex == -1))
|
||||
return null;
|
||||
}
|
||||
|
||||
if (tag == 'select') {
|
||||
var index = el.selectedIndex;
|
||||
if (index < 0) {
|
||||
return null;
|
||||
}
|
||||
if (index < 0) return null;
|
||||
var a = [], ops = el.options;
|
||||
var one = (t == 'select-one');
|
||||
var max = (one ? index+1 : ops.length);
|
||||
for(var i=(one ? index : 0); i < max; i++) {
|
||||
var op = ops[i];
|
||||
if (op.selected) {
|
||||
var v = op.value;
|
||||
if (!v) { // extra pain for IE...
|
||||
v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
|
||||
}
|
||||
if (one) {
|
||||
return v;
|
||||
}
|
||||
// extra pain for IE...
|
||||
var v = $.browser.msie && !(op.attributes['value'].specified) ? op.text : op.value;
|
||||
if (one) return v;
|
||||
a.push(v);
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}
|
||||
return $(el).val();
|
||||
return el.value;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -725,15 +570,12 @@ $.fn.clearForm = function() {
|
|||
$.fn.clearFields = $.fn.clearInputs = function() {
|
||||
return this.each(function() {
|
||||
var t = this.type, tag = this.tagName.toLowerCase();
|
||||
if (t == 'text' || t == 'password' || tag == 'textarea') {
|
||||
if (t == 'file' || t == 'text' || t == 'password' || tag == 'textarea')
|
||||
this.value = '';
|
||||
}
|
||||
else if (t == 'checkbox' || t == 'radio') {
|
||||
else if (t == 'checkbox' || t == 'radio')
|
||||
this.checked = false;
|
||||
}
|
||||
else if (tag == 'select') {
|
||||
else if (tag == 'select')
|
||||
this.selectedIndex = -1;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -744,9 +586,8 @@ $.fn.resetForm = function() {
|
|||
return this.each(function() {
|
||||
// guard against an input with the name of 'reset'
|
||||
// note that IE reports the reset function as an 'object'
|
||||
if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
|
||||
if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType))
|
||||
this.reset();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -754,11 +595,9 @@ $.fn.resetForm = function() {
|
|||
* Enables or disables any matching elements.
|
||||
*/
|
||||
$.fn.enable = function(b) {
|
||||
if (b === undefined) {
|
||||
b = true;
|
||||
}
|
||||
if (b == undefined) b = true;
|
||||
return this.each(function() {
|
||||
this.disabled = !b;
|
||||
this.disabled = !b
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -767,14 +606,11 @@ $.fn.enable = function(b) {
|
|||
* selects/deselects and matching option elements.
|
||||
*/
|
||||
$.fn.selected = function(select) {
|
||||
if (select === undefined) {
|
||||
select = true;
|
||||
}
|
||||
if (select == undefined) select = true;
|
||||
return this.each(function() {
|
||||
var t = this.type;
|
||||
if (t == 'checkbox' || t == 'radio') {
|
||||
if (t == 'checkbox' || t == 'radio')
|
||||
this.checked = select;
|
||||
}
|
||||
else if (this.tagName.toLowerCase() == 'option') {
|
||||
var $sel = $(this).parent('select');
|
||||
if (select && $sel[0] && $sel[0].type == 'select-one') {
|
||||
|
@ -789,15 +625,8 @@ $.fn.selected = function(select) {
|
|||
// helper fn for console logging
|
||||
// set $.fn.ajaxSubmit.debug to true to enable debug logging
|
||||
function log() {
|
||||
if ($.fn.ajaxSubmit.debug) {
|
||||
var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
|
||||
if (window.console && window.console.log) {
|
||||
window.console.log(msg);
|
||||
}
|
||||
else if (window.opera && window.opera.postError) {
|
||||
window.opera.postError(msg);
|
||||
}
|
||||
}
|
||||
if ($.fn.ajaxSubmit.debug && window.console && window.console.log)
|
||||
window.console.log('[jquery.form] ' + Array.prototype.join.call(arguments,''));
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
|
|
12
js/jquery.form.min.js
vendored
12
js/jquery.form.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -392,6 +392,18 @@ class Activity
|
|||
|
||||
if ($author) {
|
||||
$this->actor->outputTo($xs, 'author');
|
||||
|
||||
// XXX: Remove <activity:actor> ASAP! Author information
|
||||
// has been moved to the author element in the Activity
|
||||
// Streams spec. We're outputting actor only for backward
|
||||
// compatibility with clients that can only parse
|
||||
// activities based on older versions of the spec.
|
||||
|
||||
$depMsg = 'Deprecation warning: activity:actor is present '
|
||||
. 'only for backward compatibility. It will be '
|
||||
. 'removed in the next version of StatusNet.';
|
||||
$xs->comment($depMsg);
|
||||
$this->actor->outputTo($xs, 'activity:actor');
|
||||
}
|
||||
|
||||
if ($this->verb != ActivityVerb::POST || count($this->objects) != 1) {
|
||||
|
|
|
@ -263,8 +263,7 @@ class ApiAction extends Action
|
|||
? Design::url($design->backgroundimage) : '';
|
||||
|
||||
$twitter_user['profile_background_tile']
|
||||
= empty($design->disposition)
|
||||
? '' : ($design->disposition & BACKGROUND_TILE) ? 'true' : 'false';
|
||||
= (bool)($design->disposition & BACKGROUND_TILE);
|
||||
|
||||
$twitter_user['statuses_count'] = $profile->noticeCount();
|
||||
|
||||
|
@ -1236,9 +1235,12 @@ class ApiAction extends Action
|
|||
return;
|
||||
}
|
||||
|
||||
function clientError($msg, $code = 400, $format = 'xml')
|
||||
function clientError($msg, $code = 400, $format = null)
|
||||
{
|
||||
$action = $this->trimmed('action');
|
||||
if ($format === null) {
|
||||
$format = $this->format;
|
||||
}
|
||||
|
||||
common_debug("User error '$code' on '$action': $msg", __FILE__);
|
||||
|
||||
|
@ -1278,9 +1280,12 @@ class ApiAction extends Action
|
|||
}
|
||||
}
|
||||
|
||||
function serverError($msg, $code = 500, $content_type = 'xml')
|
||||
function serverError($msg, $code = 500, $content_type = null)
|
||||
{
|
||||
$action = $this->trimmed('action');
|
||||
if ($content_type === null) {
|
||||
$content_type = $this->format;
|
||||
}
|
||||
|
||||
common_debug("Server error '$code' on '$action': $msg", __FILE__);
|
||||
|
||||
|
|
|
@ -91,8 +91,16 @@ class AtomGroupNoticeFeed extends AtomNoticeFeed
|
|||
|
||||
$ao = ActivityObject::fromGroup($group);
|
||||
|
||||
$this->addAuthorRaw($ao->asString('author').
|
||||
$ao->asString('activity:subject'));
|
||||
$this->addAuthorRaw($ao->asString('author'));
|
||||
|
||||
$depMsg = 'Deprecation warning: activity:subject is present '
|
||||
. 'only for backward compatibility. It will be '
|
||||
. 'removed in the next version of StatusNet.';
|
||||
|
||||
$this->addAuthorRaw(
|
||||
"<!--$depMsg-->\n"
|
||||
. $ao->asString('activity:subject')
|
||||
);
|
||||
|
||||
$this->addLink($group->homeUrl());
|
||||
}
|
||||
|
|
|
@ -59,9 +59,29 @@ class AtomUserNoticeFeed extends AtomNoticeFeed
|
|||
parent::__construct($cur, $indent);
|
||||
$this->user = $user;
|
||||
if (!empty($user)) {
|
||||
|
||||
$profile = $user->getProfile();
|
||||
|
||||
$ao = ActivityObject::fromProfile($profile);
|
||||
|
||||
$ao->extra[] = $profile->profileInfo($cur);
|
||||
|
||||
// XXX: For users, we generate an author _AND_ an <activity:subject>
|
||||
// This is for backward compatibility with clients (especially
|
||||
// StatusNet's clients) that assume the Atom will conform to an
|
||||
// older version of the Activity Streams API. Subject should be
|
||||
// removed in future versions of StatusNet.
|
||||
|
||||
$this->addAuthorRaw($ao->asString('author'));
|
||||
|
||||
$depMsg = 'Deprecation warning: activity:subject is present '
|
||||
. 'only for backward compatibility. It will be '
|
||||
. 'removed in the next version of StatusNet.';
|
||||
|
||||
$this->addAuthorRaw(
|
||||
"<!--$depMsg-->\n"
|
||||
. $ao->asString('activity:subject')
|
||||
);
|
||||
}
|
||||
|
||||
// TRANS: Title in atom user notice feed. %s is a user name.
|
||||
|
|
|
@ -76,8 +76,7 @@ class AttachmentList extends Widget
|
|||
*/
|
||||
function show()
|
||||
{
|
||||
$atts = new File;
|
||||
$att = $atts->getAttachments($this->notice->id);
|
||||
$att = File::getAttachments($this->notice->id);
|
||||
if (empty($att)) return 0;
|
||||
$this->showListStart();
|
||||
|
||||
|
|
|
@ -164,6 +164,7 @@ class Cache
|
|||
{
|
||||
$value = false;
|
||||
|
||||
common_perf_counter('Cache::get', $key);
|
||||
if (Event::handle('StartCacheGet', array(&$key, &$value))) {
|
||||
if (array_key_exists($key, $this->_items)) {
|
||||
$value = unserialize($this->_items[$key]);
|
||||
|
@ -188,6 +189,7 @@ class Cache
|
|||
{
|
||||
$success = false;
|
||||
|
||||
common_perf_counter('Cache::set', $key);
|
||||
if (Event::handle('StartCacheSet', array(&$key, &$value, &$flag,
|
||||
&$expiry, &$success))) {
|
||||
|
||||
|
@ -214,6 +216,7 @@ class Cache
|
|||
function increment($key, $step=1)
|
||||
{
|
||||
$value = false;
|
||||
common_perf_counter('Cache::increment', $key);
|
||||
if (Event::handle('StartCacheIncrement', array(&$key, &$step, &$value))) {
|
||||
// Fallback is not guaranteed to be atomic,
|
||||
// and may original expiry value.
|
||||
|
@ -239,6 +242,7 @@ class Cache
|
|||
{
|
||||
$success = false;
|
||||
|
||||
common_perf_counter('Cache::delete', $key);
|
||||
if (Event::handle('StartCacheDelete', array(&$key, &$success))) {
|
||||
if (array_key_exists($key, $this->_items)) {
|
||||
unset($this->_items[$key]);
|
||||
|
|
|
@ -25,252 +25,287 @@ class CommandInterpreter
|
|||
{
|
||||
function handle_command($user, $text)
|
||||
{
|
||||
# XXX: localise
|
||||
// XXX: localise
|
||||
|
||||
$text = preg_replace('/\s+/', ' ', trim($text));
|
||||
list($cmd, $arg) = $this->split_arg($text);
|
||||
|
||||
# We try to support all the same commands as Twitter, see
|
||||
# http://getsatisfaction.com/twitter/topics/what_are_the_twitter_commands
|
||||
# There are a few compatibility commands from earlier versions of
|
||||
# StatusNet
|
||||
// We try to support all the same commands as Twitter, see
|
||||
// http://getsatisfaction.com/twitter/topics/what_are_the_twitter_commands
|
||||
// There are a few compatibility commands from earlier versions of
|
||||
// StatusNet
|
||||
|
||||
switch(strtolower($cmd)) {
|
||||
$cmd = strtolower($cmd);
|
||||
|
||||
if (Event::handle('StartIntepretCommand', array($cmd, $arg, $user, &$result))) {
|
||||
switch($cmd) {
|
||||
case 'help':
|
||||
if ($arg) {
|
||||
return null;
|
||||
$result = null;
|
||||
}
|
||||
return new HelpCommand($user);
|
||||
$result = new HelpCommand($user);
|
||||
break;
|
||||
case 'login':
|
||||
if ($arg) {
|
||||
return null;
|
||||
$result = null;
|
||||
} else {
|
||||
return new LoginCommand($user);
|
||||
$result = new LoginCommand($user);
|
||||
}
|
||||
break;
|
||||
case 'lose':
|
||||
if ($arg) {
|
||||
list($other, $extra) = $this->split_arg($arg);
|
||||
if ($extra) {
|
||||
return null;
|
||||
$result = null;
|
||||
} else {
|
||||
return new LoseCommand($user, $other);
|
||||
$result = new LoseCommand($user, $other);
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
$result = null;
|
||||
}
|
||||
break;
|
||||
case 'subscribers':
|
||||
if ($arg) {
|
||||
return null;
|
||||
$result = null;
|
||||
} else {
|
||||
return new SubscribersCommand($user);
|
||||
$result = new SubscribersCommand($user);
|
||||
}
|
||||
break;
|
||||
case 'subscriptions':
|
||||
if ($arg) {
|
||||
return null;
|
||||
$result = null;
|
||||
} else {
|
||||
return new SubscriptionsCommand($user);
|
||||
$result = new SubscriptionsCommand($user);
|
||||
}
|
||||
break;
|
||||
case 'groups':
|
||||
if ($arg) {
|
||||
return null;
|
||||
$result = null;
|
||||
} else {
|
||||
return new GroupsCommand($user);
|
||||
$result = new GroupsCommand($user);
|
||||
}
|
||||
break;
|
||||
case 'on':
|
||||
if ($arg) {
|
||||
list($other, $extra) = $this->split_arg($arg);
|
||||
if ($extra) {
|
||||
return null;
|
||||
$result = null;
|
||||
} else {
|
||||
return new OnCommand($user, $other);
|
||||
$result = new OnCommand($user, $other);
|
||||
}
|
||||
} else {
|
||||
return new OnCommand($user);
|
||||
$result = new OnCommand($user);
|
||||
}
|
||||
break;
|
||||
case 'off':
|
||||
if ($arg) {
|
||||
list($other, $extra) = $this->split_arg($arg);
|
||||
if ($extra) {
|
||||
return null;
|
||||
$result = null;
|
||||
} else {
|
||||
return new OffCommand($user, $other);
|
||||
$result = new OffCommand($user, $other);
|
||||
}
|
||||
} else {
|
||||
return new OffCommand($user);
|
||||
$result = new OffCommand($user);
|
||||
}
|
||||
break;
|
||||
case 'stop':
|
||||
case 'quit':
|
||||
if ($arg) {
|
||||
return null;
|
||||
$result = null;
|
||||
} else {
|
||||
return new OffCommand($user);
|
||||
$result = new OffCommand($user);
|
||||
}
|
||||
break;
|
||||
case 'join':
|
||||
if (!$arg) {
|
||||
return null;
|
||||
$result = null;
|
||||
}
|
||||
list($other, $extra) = $this->split_arg($arg);
|
||||
if ($extra) {
|
||||
return null;
|
||||
$result = null;
|
||||
} else {
|
||||
return new JoinCommand($user, $other);
|
||||
$result = new JoinCommand($user, $other);
|
||||
}
|
||||
break;
|
||||
case 'drop':
|
||||
if (!$arg) {
|
||||
return null;
|
||||
$result = null;
|
||||
}
|
||||
list($other, $extra) = $this->split_arg($arg);
|
||||
if ($extra) {
|
||||
return null;
|
||||
$result = null;
|
||||
} else {
|
||||
return new DropCommand($user, $other);
|
||||
$result = new DropCommand($user, $other);
|
||||
}
|
||||
break;
|
||||
case 'follow':
|
||||
case 'sub':
|
||||
if (!$arg) {
|
||||
return null;
|
||||
$result = null;
|
||||
}
|
||||
|
||||
list($other, $extra) = $this->split_arg($arg);
|
||||
if ($extra) {
|
||||
return null;
|
||||
$result = null;
|
||||
} else {
|
||||
return new SubCommand($user, $other);
|
||||
$result = new SubCommand($user, $other);
|
||||
}
|
||||
break;
|
||||
case 'leave':
|
||||
case 'unsub':
|
||||
if (!$arg) {
|
||||
return null;
|
||||
$result = null;
|
||||
}
|
||||
|
||||
list($other, $extra) = $this->split_arg($arg);
|
||||
if ($extra) {
|
||||
return null;
|
||||
$result = null;
|
||||
} else {
|
||||
return new UnsubCommand($user, $other);
|
||||
$result = new UnsubCommand($user, $other);
|
||||
}
|
||||
break;
|
||||
case 'get':
|
||||
case 'last':
|
||||
if (!$arg) {
|
||||
return null;
|
||||
$result = null;
|
||||
}
|
||||
list($other, $extra) = $this->split_arg($arg);
|
||||
if ($extra) {
|
||||
return null;
|
||||
$result = null;
|
||||
} else {
|
||||
return new GetCommand($user, $other);
|
||||
$result = new GetCommand($user, $other);
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
case 'dm':
|
||||
if (!$arg) {
|
||||
return null;
|
||||
$result = null;
|
||||
}
|
||||
list($other, $extra) = $this->split_arg($arg);
|
||||
if (!$extra) {
|
||||
return null;
|
||||
$result = null;
|
||||
} else {
|
||||
return new MessageCommand($user, $other, $extra);
|
||||
$result = new MessageCommand($user, $other, $extra);
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
case 'reply':
|
||||
if (!$arg) {
|
||||
return null;
|
||||
$result = null;
|
||||
}
|
||||
list($other, $extra) = $this->split_arg($arg);
|
||||
if (!$extra) {
|
||||
return null;
|
||||
$result = null;
|
||||
} else {
|
||||
return new ReplyCommand($user, $other, $extra);
|
||||
$result = new ReplyCommand($user, $other, $extra);
|
||||
}
|
||||
break;
|
||||
case 'repeat':
|
||||
case 'rp':
|
||||
case 'rt':
|
||||
case 'rd':
|
||||
if (!$arg) {
|
||||
return null;
|
||||
$result = null;
|
||||
}
|
||||
list($other, $extra) = $this->split_arg($arg);
|
||||
if ($extra) {
|
||||
return null;
|
||||
$result = null;
|
||||
} else {
|
||||
return new RepeatCommand($user, $other);
|
||||
$result = new RepeatCommand($user, $other);
|
||||
}
|
||||
break;
|
||||
case 'whois':
|
||||
if (!$arg) {
|
||||
return null;
|
||||
$result = null;
|
||||
}
|
||||
list($other, $extra) = $this->split_arg($arg);
|
||||
if ($extra) {
|
||||
return null;
|
||||
$result = null;
|
||||
} else {
|
||||
return new WhoisCommand($user, $other);
|
||||
$result = new WhoisCommand($user, $other);
|
||||
}
|
||||
break;
|
||||
case 'fav':
|
||||
if (!$arg) {
|
||||
return null;
|
||||
$result = null;
|
||||
}
|
||||
list($other, $extra) = $this->split_arg($arg);
|
||||
if ($extra) {
|
||||
return null;
|
||||
$result = null;
|
||||
} else {
|
||||
return new FavCommand($user, $other);
|
||||
$result = new FavCommand($user, $other);
|
||||
}
|
||||
break;
|
||||
case 'nudge':
|
||||
if (!$arg) {
|
||||
return null;
|
||||
$result = null;
|
||||
}
|
||||
list($other, $extra) = $this->split_arg($arg);
|
||||
if ($extra) {
|
||||
return null;
|
||||
$result = null;
|
||||
} else {
|
||||
return new NudgeCommand($user, $other);
|
||||
$result = new NudgeCommand($user, $other);
|
||||
}
|
||||
break;
|
||||
case 'stats':
|
||||
if ($arg) {
|
||||
return null;
|
||||
$result = null;
|
||||
}
|
||||
return new StatsCommand($user);
|
||||
$result = new StatsCommand($user);
|
||||
break;
|
||||
case 'invite':
|
||||
if (!$arg) {
|
||||
return null;
|
||||
$result = null;
|
||||
}
|
||||
list($other, $extra) = $this->split_arg($arg);
|
||||
if ($extra) {
|
||||
return null;
|
||||
$result = null;
|
||||
} else {
|
||||
return new InviteCommand($user, $other);
|
||||
$result = new InviteCommand($user, $other);
|
||||
}
|
||||
break;
|
||||
case 'track':
|
||||
if (!$arg) {
|
||||
return null;
|
||||
$result = null;
|
||||
}
|
||||
list($word, $extra) = $this->split_arg($arg);
|
||||
if ($extra) {
|
||||
return null;
|
||||
$result = null;
|
||||
} else if ($word == 'off') {
|
||||
return new TrackOffCommand($user);
|
||||
$result = new TrackOffCommand($user);
|
||||
} else {
|
||||
return new TrackCommand($user, $word);
|
||||
$result = new TrackCommand($user, $word);
|
||||
}
|
||||
break;
|
||||
case 'untrack':
|
||||
if (!$arg) {
|
||||
return null;
|
||||
$result = null;
|
||||
}
|
||||
list($word, $extra) = $this->split_arg($arg);
|
||||
if ($extra) {
|
||||
return null;
|
||||
$result = null;
|
||||
} else if ($word == 'all') {
|
||||
return new TrackOffCommand($user);
|
||||
$result = new TrackOffCommand($user);
|
||||
} else {
|
||||
return new UntrackCommand($user, $word);
|
||||
$result = new UntrackCommand($user, $word);
|
||||
}
|
||||
break;
|
||||
case 'tracks':
|
||||
case 'tracking':
|
||||
if ($arg) {
|
||||
return null;
|
||||
$result = null;
|
||||
}
|
||||
return new TrackingCommand($user);
|
||||
$result = new TrackingCommand($user);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
$result = false;
|
||||
}
|
||||
|
||||
Event::handle('EndInterpretCommand', array($cmd, $arg, $user, $result));
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -23,7 +23,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
|
|||
if (isset($_REQUEST['p']) && $_REQUEST['p'] == 'check-fancy') { exit; }
|
||||
|
||||
define('STATUSNET_BASE_VERSION', '0.9.7');
|
||||
define('STATUSNET_LIFECYCLE', 'alpha1'); // 'dev', 'alpha[0-9]+', 'beta[0-9]+', 'rc[0-9]+', 'release'
|
||||
define('STATUSNET_LIFECYCLE', 'beta2'); // 'dev', 'alpha[0-9]+', 'beta[0-9]+', 'rc[0-9]+', 'release'
|
||||
define('STATUSNET_VERSION', STATUSNET_BASE_VERSION . STATUSNET_LIFECYCLE);
|
||||
|
||||
define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility
|
||||
|
@ -36,6 +36,7 @@ define('AVATAR_MINI_SIZE', 24);
|
|||
|
||||
define('NOTICES_PER_PAGE', 20);
|
||||
define('PROFILES_PER_PAGE', 20);
|
||||
define('MESSAGES_PER_PAGE', 20);
|
||||
|
||||
define('FOREIGN_NOTICE_SEND', 1);
|
||||
define('FOREIGN_NOTICE_RECV', 2);
|
||||
|
|
|
@ -39,6 +39,8 @@ $default =
|
|||
'logo' => null,
|
||||
'ssllogo' => null,
|
||||
'logdebug' => false,
|
||||
'logperf' => false, // Enable to dump performance counters to syslog
|
||||
'logperf_detail' => false, // Enable to dump every counter hit
|
||||
'fancy' => false,
|
||||
'locale_path' => INSTALLDIR.'/locale',
|
||||
'language' => 'en',
|
||||
|
@ -270,7 +272,8 @@ $default =
|
|||
array('type' => 'fulltext'),
|
||||
'sessions' =>
|
||||
array('handle' => false, // whether to handle sessions ourselves
|
||||
'debug' => false), // debugging output for sessions
|
||||
'debug' => false, // debugging output for sessions
|
||||
'gc_limit' => 1000), // max sessions to expire at a time
|
||||
'design' =>
|
||||
array('backgroundcolor' => null, // null -> 'use theme default'
|
||||
'contentcolor' => null,
|
||||
|
@ -311,6 +314,9 @@ $default =
|
|||
'RSSCloud' => null,
|
||||
'OpenID' => null),
|
||||
'locale_path' => false, // Set to a path to use *instead of* each plugin's own locale subdirectories
|
||||
'server' => null,
|
||||
'sslserver' => null,
|
||||
'path' => null,
|
||||
),
|
||||
'admin' =>
|
||||
array('panels' => array('design', 'site', 'user', 'paths', 'access', 'sessions', 'sitenotice', 'license')),
|
||||
|
|
|
@ -139,11 +139,12 @@ class GroupEditForm extends Form
|
|||
}
|
||||
|
||||
$this->out->elementStart('ul', 'form_data');
|
||||
if (Event::handle('StartGroupEditFormData', array($this))) {
|
||||
$this->out->elementStart('li');
|
||||
$this->out->hidden('groupid', $id);
|
||||
$this->out->input('nickname', _('Nickname'),
|
||||
($this->out->arg('nickname')) ? $this->out->arg('nickname') : $nickname,
|
||||
_('1-64 lowercase letters or numbers, no punctuation or spaces.'));
|
||||
_('1-64 lowercase letters or numbers, no punctuation or spaces'));
|
||||
$this->out->elementEnd('li');
|
||||
$this->out->elementStart('li');
|
||||
$this->out->input('fullname', _('Full name'),
|
||||
|
@ -159,8 +160,8 @@ class GroupEditForm extends Form
|
|||
if ($desclimit == 0) {
|
||||
$descinstr = _('Describe the group or topic');
|
||||
} else {
|
||||
$descinstr = sprintf(_m('Describe the group or topic in %d character or less.',
|
||||
'Describe the group or topic in %d characters or less.',
|
||||
$descinstr = sprintf(_m('Describe the group or topic in %d character or less',
|
||||
'Describe the group or topic in %d characters or less',
|
||||
$desclimit),
|
||||
$desclimit);
|
||||
}
|
||||
|
@ -185,6 +186,8 @@ class GroupEditForm extends Form
|
|||
common_config('group', 'maxaliases')));;
|
||||
$this->out->elementEnd('li');
|
||||
}
|
||||
Event::handle('EndGroupEditFormData', array($this));
|
||||
}
|
||||
$this->out->elementEnd('ul');
|
||||
}
|
||||
|
||||
|
|
161
lib/mailbox.php
161
lib/mailbox.php
|
@ -31,8 +31,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
|
|||
exit(1);
|
||||
}
|
||||
|
||||
define('MESSAGES_PER_PAGE', 20);
|
||||
|
||||
/**
|
||||
* common superclass for direct messages inbox and outbox
|
||||
*
|
||||
|
@ -111,32 +109,22 @@ class MailboxAction extends CurrentUserDesignAction
|
|||
$message = $this->getMessages();
|
||||
|
||||
if ($message) {
|
||||
$cnt = 0;
|
||||
$this->elementStart('div', array('id' =>'notices_primary'));
|
||||
$this->element('h2', null, _('Notices'));
|
||||
$this->elementStart('ul', 'notices');
|
||||
|
||||
while ($message->fetch() && $cnt <= MESSAGES_PER_PAGE) {
|
||||
$cnt++;
|
||||
$ml = $this->getMessageList($message);
|
||||
|
||||
if ($cnt > MESSAGES_PER_PAGE) {
|
||||
break;
|
||||
}
|
||||
$cnt = $ml->show();
|
||||
|
||||
$this->showMessage($message);
|
||||
}
|
||||
|
||||
$this->elementEnd('ul');
|
||||
|
||||
$this->pagination($this->page > 1, $cnt > MESSAGES_PER_PAGE,
|
||||
$this->page, $this->trimmed('action'),
|
||||
$this->pagination($this->page > 1,
|
||||
$cnt > MESSAGES_PER_PAGE,
|
||||
$this->page,
|
||||
$this->trimmed('action'),
|
||||
array('nickname' => $this->user->nickname));
|
||||
$this->elementEnd('div');
|
||||
$message->free();
|
||||
unset($message);
|
||||
}
|
||||
else {
|
||||
$this->element('p', 'guide', _('You have no private messages. You can send private message to engage other users in conversation. People can send you messages for your eyes only.'));
|
||||
} else {
|
||||
$this->element('p',
|
||||
'guide',
|
||||
_('You have no private messages. '.
|
||||
'You can send private message to engage other users in conversation. '.
|
||||
'People can send you messages for your eyes only.'));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,95 +133,11 @@ class MailboxAction extends CurrentUserDesignAction
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the profile we want to show with the message
|
||||
*
|
||||
* For inboxes, we show the sender; for outboxes, the recipient.
|
||||
*
|
||||
* @param Message $message The message to get the profile for
|
||||
*
|
||||
* @return Profile The profile that matches the message
|
||||
*/
|
||||
|
||||
function getMessageProfile($message)
|
||||
function getMessageList($message)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* show a single message in the list format
|
||||
*
|
||||
* XXX: This needs to be extracted out into a MessageList similar
|
||||
* to NoticeList.
|
||||
*
|
||||
* @param Message $message the message to show
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
||||
function showMessage($message)
|
||||
{
|
||||
$this->elementStart('li', array('class' => 'hentry notice',
|
||||
'id' => 'message-' . $message->id));
|
||||
|
||||
$profile = $this->getMessageProfile($message);
|
||||
|
||||
$this->elementStart('div', 'entry-title');
|
||||
$this->elementStart('span', 'vcard author');
|
||||
$this->elementStart('a', array('href' => $profile->profileurl,
|
||||
'class' => 'url'));
|
||||
$avatar = $profile->getAvatar(AVATAR_STREAM_SIZE);
|
||||
$this->element('img', array('src' => ($avatar) ?
|
||||
$avatar->displayUrl() :
|
||||
Avatar::defaultImage(AVATAR_STREAM_SIZE),
|
||||
'class' => 'photo avatar',
|
||||
'width' => AVATAR_STREAM_SIZE,
|
||||
'height' => AVATAR_STREAM_SIZE,
|
||||
'alt' =>
|
||||
($profile->fullname) ? $profile->fullname :
|
||||
$profile->nickname));
|
||||
$this->element('span', array('class' => 'nickname fn'),
|
||||
$profile->nickname);
|
||||
$this->elementEnd('a');
|
||||
$this->elementEnd('span');
|
||||
|
||||
// FIXME: URL, image, video, audio
|
||||
$this->elementStart('p', array('class' => 'entry-content'));
|
||||
$this->raw($message->rendered);
|
||||
$this->elementEnd('p');
|
||||
$this->elementEnd('div');
|
||||
|
||||
$messageurl = common_local_url('showmessage',
|
||||
array('message' => $message->id));
|
||||
|
||||
// XXX: we need to figure this out better. Is this right?
|
||||
if (strcmp($message->uri, $messageurl) != 0 &&
|
||||
preg_match('/^http/', $message->uri)) {
|
||||
$messageurl = $message->uri;
|
||||
}
|
||||
|
||||
$this->elementStart('div', 'entry-content');
|
||||
$this->elementStart('a', array('rel' => 'bookmark',
|
||||
'class' => 'timestamp',
|
||||
'href' => $messageurl));
|
||||
$dt = common_date_iso8601($message->created);
|
||||
$this->element('abbr', array('class' => 'published',
|
||||
'title' => $dt),
|
||||
common_date_string($message->created));
|
||||
$this->elementEnd('a');
|
||||
|
||||
if ($message->source) {
|
||||
$this->elementStart('span', 'source');
|
||||
// FIXME: bad i18n. Device should be a parameter (from %s).
|
||||
$this->text(_('from'));
|
||||
$this->element('span', 'device', $this->showSource($message->source));
|
||||
$this->elementEnd('span');
|
||||
}
|
||||
$this->elementEnd('div');
|
||||
|
||||
$this->elementEnd('li');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the page notice
|
||||
*
|
||||
|
@ -252,44 +156,6 @@ class MailboxAction extends CurrentUserDesignAction
|
|||
$this->elementEnd('div');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the source of the message
|
||||
*
|
||||
* Returns either the name (and link) of the API client that posted the notice,
|
||||
* or one of other other channels.
|
||||
*
|
||||
* @param string $source the source of the message
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
||||
function showSource($source)
|
||||
{
|
||||
$source_name = _($source);
|
||||
switch ($source) {
|
||||
case 'web':
|
||||
case 'xmpp':
|
||||
case 'mail':
|
||||
case 'omb':
|
||||
case 'api':
|
||||
$this->element('span', 'device', $source_name);
|
||||
break;
|
||||
default:
|
||||
$ns = Notice_source::staticGet($source);
|
||||
if ($ns) {
|
||||
$this->elementStart('span', 'device');
|
||||
$this->element('a', array('href' => $ns->url,
|
||||
'rel' => 'external'),
|
||||
$ns->name);
|
||||
$this->elementEnd('span');
|
||||
} else {
|
||||
$this->element('span', 'device', $source_name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mailbox actions are read only
|
||||
*
|
||||
|
@ -302,5 +168,4 @@ class MailboxAction extends CurrentUserDesignAction
|
|||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
107
lib/messagelist.php
Normal file
107
lib/messagelist.php
Normal file
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
/**
|
||||
* StatusNet - the distributed open-source microblogging tool
|
||||
* Copyright (C) 2011, StatusNet, Inc.
|
||||
*
|
||||
* The message list widget
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* 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 StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @copyright 2011 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET')) {
|
||||
// This check helps protect against security problems;
|
||||
// your code file can't be executed directly from the web.
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Message list widget
|
||||
*
|
||||
* @category Widget
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @copyright 2011 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
abstract class MessageList extends Widget
|
||||
{
|
||||
var $message;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param HTMLOutputter $out Output context
|
||||
* @param Message $message Stream of messages to show
|
||||
*/
|
||||
function __construct($out, $message)
|
||||
{
|
||||
parent::__construct($out);
|
||||
$this->message = $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the widget
|
||||
*
|
||||
* Uses newItem() to create each new item.
|
||||
*
|
||||
* @return integer count of messages seen.
|
||||
*/
|
||||
function show()
|
||||
{
|
||||
$cnt = 0;
|
||||
|
||||
$this->out->elementStart('div', array('id' =>'notices_primary'));
|
||||
|
||||
$this->out->element('h2', null, _('Messages'));
|
||||
|
||||
$this->out->elementStart('ul', 'notices messages');
|
||||
|
||||
while ($this->message->fetch() && $cnt <= MESSAGES_PER_PAGE) {
|
||||
|
||||
$cnt++;
|
||||
|
||||
if ($cnt > MESSAGES_PER_PAGE) {
|
||||
break;
|
||||
}
|
||||
|
||||
$mli = $this->newItem($this->message);
|
||||
|
||||
$mli->show();
|
||||
}
|
||||
|
||||
$this->out->elementEnd('ul');
|
||||
|
||||
$this->out->elementEnd('div');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new message item for a message
|
||||
*
|
||||
* @param Message $message The message to show
|
||||
*
|
||||
* @return MessageListItem an item to show
|
||||
*/
|
||||
abstract function newItem($message);
|
||||
}
|
178
lib/messagelistitem.php
Normal file
178
lib/messagelistitem.php
Normal file
|
@ -0,0 +1,178 @@
|
|||
<?php
|
||||
/**
|
||||
* StatusNet - the distributed open-source microblogging tool
|
||||
* Copyright (C) 2011, StatusNet, Inc.
|
||||
*
|
||||
* A single list item for showing in a message list
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* 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 StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @copyright 2011 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET')) {
|
||||
// This check helps protect against security problems;
|
||||
// your code file can't be executed directly from the web.
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* A single item in a message list
|
||||
*
|
||||
* @category Widget
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @copyright 2011 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
abstract class MessageListItem extends Widget
|
||||
{
|
||||
var $message;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param HTMLOutputter $out Output context
|
||||
* @param Message $message Message to show
|
||||
*/
|
||||
function __construct($out, $message)
|
||||
{
|
||||
parent::__construct($out);
|
||||
$this->message = $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the widget
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
||||
function show()
|
||||
{
|
||||
$this->out->elementStart('li', array('class' => 'hentry notice',
|
||||
'id' => 'message-' . $this->message->id));
|
||||
|
||||
$profile = $this->getMessageProfile();
|
||||
|
||||
$this->out->elementStart('div', 'entry-title');
|
||||
$this->out->elementStart('span', 'vcard author');
|
||||
$this->out->elementStart('a', array('href' => $profile->profileurl,
|
||||
'class' => 'url'));
|
||||
$avatar = $profile->getAvatar(AVATAR_STREAM_SIZE);
|
||||
$this->out->element('img', array('src' => ($avatar) ?
|
||||
$avatar->displayUrl() :
|
||||
Avatar::defaultImage(AVATAR_STREAM_SIZE),
|
||||
'class' => 'photo avatar',
|
||||
'width' => AVATAR_STREAM_SIZE,
|
||||
'height' => AVATAR_STREAM_SIZE,
|
||||
'alt' =>
|
||||
($profile->fullname) ? $profile->fullname :
|
||||
$profile->nickname));
|
||||
$this->out->element('span', array('class' => 'nickname fn'),
|
||||
$profile->nickname);
|
||||
$this->out->elementEnd('a');
|
||||
$this->out->elementEnd('span');
|
||||
|
||||
// FIXME: URL, image, video, audio
|
||||
$this->out->elementStart('p', array('class' => 'entry-content'));
|
||||
$this->out->raw($this->message->rendered);
|
||||
$this->out->elementEnd('p');
|
||||
$this->out->elementEnd('div');
|
||||
|
||||
$messageurl = common_local_url('showmessage',
|
||||
array('message' => $this->message->id));
|
||||
|
||||
// XXX: we need to figure this out better. Is this right?
|
||||
if (strcmp($this->message->uri, $messageurl) != 0 &&
|
||||
preg_match('/^http/', $this->message->uri)) {
|
||||
$messageurl = $this->message->uri;
|
||||
}
|
||||
|
||||
$this->out->elementStart('div', 'entry-content');
|
||||
$this->out->elementStart('a', array('rel' => 'bookmark',
|
||||
'class' => 'timestamp',
|
||||
'href' => $messageurl));
|
||||
$dt = common_date_iso8601($this->message->created);
|
||||
$this->out->element('abbr', array('class' => 'published',
|
||||
'title' => $dt),
|
||||
common_date_string($this->message->created));
|
||||
$this->out->elementEnd('a');
|
||||
|
||||
if ($this->message->source) {
|
||||
$this->out->elementStart('span', 'source');
|
||||
// FIXME: bad i18n. Device should be a parameter (from %s).
|
||||
$this->out->text(_('from'));
|
||||
$this->showSource($this->message->source);
|
||||
$this->out->elementEnd('span');
|
||||
}
|
||||
$this->out->elementEnd('div');
|
||||
|
||||
$this->out->elementEnd('li');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Show the source of the message
|
||||
*
|
||||
* Returns either the name (and link) of the API client that posted the notice,
|
||||
* or one of other other channels.
|
||||
*
|
||||
* @param string $source the source of the message
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function showSource($source)
|
||||
{
|
||||
$source_name = _($source);
|
||||
switch ($source) {
|
||||
case 'web':
|
||||
case 'xmpp':
|
||||
case 'mail':
|
||||
case 'omb':
|
||||
case 'api':
|
||||
$this->out->element('span', 'device', $source_name);
|
||||
break;
|
||||
default:
|
||||
$ns = Notice_source::staticGet($source);
|
||||
if ($ns) {
|
||||
$this->out->elementStart('span', 'device');
|
||||
$this->out->element('a', array('href' => $ns->url,
|
||||
'rel' => 'external'),
|
||||
$ns->name);
|
||||
$this->out->elementEnd('span');
|
||||
} else {
|
||||
$this->out->element('span', 'device', $source_name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the profile to show in the message item
|
||||
*
|
||||
* Overridden in sub-classes to show sender, receiver, or whatever
|
||||
*
|
||||
* @return Profile profile to show avatar and name of
|
||||
*/
|
||||
abstract function getMessageProfile();
|
||||
}
|
|
@ -111,10 +111,15 @@ class Plugin
|
|||
$this->log(LOG_DEBUG, $msg);
|
||||
}
|
||||
|
||||
function onPluginVersion(&$versions)
|
||||
function name()
|
||||
{
|
||||
$cls = get_class($this);
|
||||
$name = mb_substr($cls, 0, -6);
|
||||
return mb_substr($cls, 0, -6);
|
||||
}
|
||||
|
||||
function onPluginVersion(&$versions)
|
||||
{
|
||||
$name = $this->name();
|
||||
|
||||
$versions[] = array('name' => $name,
|
||||
// TRANS: Displayed as version information for a plugin if no version information was found.
|
||||
|
@ -122,4 +127,39 @@ class Plugin
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
function path($relative)
|
||||
{
|
||||
return self::staticPath($this->name(), $relative);
|
||||
}
|
||||
|
||||
static function staticPath($plugin, $relative)
|
||||
{
|
||||
$isHTTPS = StatusNet::isHTTPS();
|
||||
|
||||
if ($isHTTPS) {
|
||||
$server = common_config('plugins', 'sslserver');
|
||||
} else {
|
||||
$server = common_config('plugins', 'server');
|
||||
}
|
||||
|
||||
if (empty($server)) {
|
||||
if ($isHTTPS) {
|
||||
$server = common_config('site', 'sslserver');
|
||||
}
|
||||
if (empty($server)) {
|
||||
$server = common_config('site', 'server');
|
||||
}
|
||||
}
|
||||
|
||||
$path = common_config('plugins', 'path');
|
||||
|
||||
if (empty($path)) {
|
||||
$path = common_config('site', 'path') . '/plugins/';
|
||||
}
|
||||
|
||||
$protocol = ($isHTTPS) ? 'https' : 'http';
|
||||
|
||||
return $protocol.'://'.$server.$path.$plugin.'/'.$relative;
|
||||
}
|
||||
}
|
||||
|
|
37
lib/util.php
37
lib/util.php
|
@ -2184,3 +2184,40 @@ function common_nicknamize($str)
|
|||
$str = preg_replace('/\W/', '', $str);
|
||||
return strtolower($str);
|
||||
}
|
||||
|
||||
function common_perf_counter($key, $val=null)
|
||||
{
|
||||
global $_perfCounters;
|
||||
if (isset($_perfCounters)) {
|
||||
if (common_config('site', 'logperf')) {
|
||||
if (array_key_exists($key, $_perfCounters)) {
|
||||
$_perfCounters[$key][] = $val;
|
||||
} else {
|
||||
$_perfCounters[$key] = array($val);
|
||||
}
|
||||
if (common_config('site', 'logperf_detail')) {
|
||||
common_log(LOG_DEBUG, "PERF COUNTER HIT: $key $val");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function common_log_perf_counters()
|
||||
{
|
||||
if (common_config('site', 'logperf')) {
|
||||
global $_startTime, $_perfCounters;
|
||||
|
||||
if (isset($_startTime)) {
|
||||
$endTime = microtime(true);
|
||||
$diff = round(($endTime - $_startTime) * 1000);
|
||||
common_log(LOG_DEBUG, "PERF runtime: ${diff}ms");
|
||||
}
|
||||
$counters = $_perfCounters;
|
||||
ksort($counters);
|
||||
foreach ($counters as $key => $values) {
|
||||
$count = count($values);
|
||||
$unique = count(array_unique($values));
|
||||
common_log(LOG_DEBUG, "PERF COUNTER: $key $count ($unique unique)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,15 +51,15 @@ class AutocompletePlugin extends Plugin
|
|||
|
||||
function onEndShowScripts($action){
|
||||
if (common_logged_in()) {
|
||||
$action->script('plugins/Autocomplete/jquery-autocomplete/jquery.autocomplete.pack.js');
|
||||
$action->script('plugins/Autocomplete/Autocomplete.js');
|
||||
$action->script($this->path('jquery-autocomplete/jquery.autocomplete.pack.js'));
|
||||
$action->script($this->path('Autocomplete.js'));
|
||||
}
|
||||
}
|
||||
|
||||
function onEndShowStatusNetStyles($action)
|
||||
{
|
||||
if (common_logged_in()) {
|
||||
$action->cssLink('plugins/Autocomplete/jquery-autocomplete/jquery.autocomplete.css');
|
||||
$action->cssLink($this->path('jquery-autocomplete/jquery.autocomplete.css'));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ class BlankAdPlugin extends UAPPlugin
|
|||
$action->element('img',
|
||||
array('width' => 300,
|
||||
'height' => 250,
|
||||
'src' => common_path('plugins/BlankAd/redpixel.png')),
|
||||
'src' => $this->path('redpixel.png')),
|
||||
'');
|
||||
}
|
||||
|
||||
|
@ -81,7 +81,7 @@ class BlankAdPlugin extends UAPPlugin
|
|||
$action->element('img',
|
||||
array('width' => 180,
|
||||
'height' => 150,
|
||||
'src' => common_path('plugins/BlankAd/redpixel.png')),
|
||||
'src' => $this->path('redpixel.png')),
|
||||
'');
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,7 @@ class BlankAdPlugin extends UAPPlugin
|
|||
$action->element('img',
|
||||
array('width' => 160,
|
||||
'height' => 600,
|
||||
'src' => common_path('plugins/BlankAd/redpixel.png')),
|
||||
'src' => $this->path('redpixel.png')),
|
||||
'');
|
||||
}
|
||||
|
||||
|
@ -113,7 +113,7 @@ class BlankAdPlugin extends UAPPlugin
|
|||
$action->element('img',
|
||||
array('width' => 728,
|
||||
'height' => 90,
|
||||
'src' => common_path('plugins/BlankAd/redpixel.png')),
|
||||
'src' => $this->path('redpixel.png')),
|
||||
'');
|
||||
}
|
||||
|
||||
|
|
|
@ -149,7 +149,7 @@ class BookmarkPlugin extends Plugin
|
|||
|
||||
function onEndShowStyles($action)
|
||||
{
|
||||
$action->cssLink('plugins/Bookmark/bookmark.css');
|
||||
$action->cssLink($this->path('bookmark.css'));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -107,6 +107,6 @@ class BookmarkpopupAction extends NewbookmarkAction
|
|||
function showScripts()
|
||||
{
|
||||
parent::showScripts();
|
||||
$this->script(common_path('plugins/Bookmark/bookmarkpopup.js'));
|
||||
$this->script(Plugin::staticPath('Bookmark', 'bookmarkpopup.js'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ class ClientSideShortenPlugin extends Plugin
|
|||
function onEndShowScripts($action){
|
||||
$action->inlineScript('var Notice_maxContent = ' . Notice::maxContent());
|
||||
if (common_logged_in()) {
|
||||
$action->script('plugins/ClientSideShorten/shorten.js');
|
||||
$action->script($this->path('shorten.js'));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -129,7 +129,7 @@ class DirectionDetectorPlugin extends Plugin {
|
|||
*/
|
||||
function onEndShowScripts($action){
|
||||
if (common_logged_in()) {
|
||||
$action->script('plugins/DirectionDetector/jquery.DirectionDetector.js');
|
||||
$action->script($this->path('jquery.DirectionDetector.js'));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
* @category Pugin
|
||||
* @package StatusNet
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2010 StatusNet, Inc.
|
||||
* @copyright 2011 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
@ -47,8 +47,9 @@ define("FACEBOOK_SERVICE", 2);
|
|||
*/
|
||||
class FacebookBridgePlugin extends Plugin
|
||||
{
|
||||
public $appId = null; // Facebook application ID
|
||||
public $secret = null; // Facebook application secret
|
||||
public $appId; // Facebook application ID
|
||||
public $secret; // Facebook application secret
|
||||
|
||||
public $facebook = null; // Facebook application instance
|
||||
public $dir = null; // Facebook plugin dir
|
||||
|
||||
|
@ -61,6 +62,28 @@ class FacebookBridgePlugin extends Plugin
|
|||
*/
|
||||
function initialize()
|
||||
{
|
||||
|
||||
// Allow the id and key to be passed in
|
||||
// Control panel will override
|
||||
|
||||
if (isset($this->appId)) {
|
||||
$appId = common_config('facebook', 'appid');
|
||||
if (empty($appId)) {
|
||||
Config::save(
|
||||
'facebook',
|
||||
'appid',
|
||||
$this->appId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->secret)) {
|
||||
$secret = common_config('facebook', 'secret');
|
||||
if (empty($secret)) {
|
||||
Config::save('facebook', 'secret', $this->secret);
|
||||
}
|
||||
}
|
||||
|
||||
$this->facebook = Facebookclient::getFacebook(
|
||||
$this->appId,
|
||||
$this->secret
|
||||
|
|
|
@ -89,10 +89,7 @@ class FacebookloginAction extends Action
|
|||
);
|
||||
|
||||
$attrs = array(
|
||||
'src' => common_path(
|
||||
'plugins/FacebookBridge/images/login-button.png',
|
||||
true
|
||||
),
|
||||
'src' => Plugin::staticPath('FacebookBridge', 'images/login-button.png'),
|
||||
'alt' => 'Login with Facebook',
|
||||
'title' => 'Login with Facebook'
|
||||
);
|
||||
|
|
|
@ -115,14 +115,7 @@ class Facebookclient
|
|||
function isFacebookBound() {
|
||||
|
||||
if (empty($this->flink)) {
|
||||
common_log(
|
||||
LOG_WARN,
|
||||
sprintf(
|
||||
"No Foreign_link to Facebook for the author of notice %d.",
|
||||
$this->notice->id
|
||||
),
|
||||
__FILE__
|
||||
);
|
||||
// User hasn't setup bridging
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -180,15 +173,6 @@ class Facebookclient
|
|||
// Otherwise we most likely have an access token
|
||||
return $this->sendGraph();
|
||||
}
|
||||
|
||||
} else {
|
||||
common_debug(
|
||||
sprintf(
|
||||
"Skipping notice %d - not bound for Facebook",
|
||||
$this->notice->id,
|
||||
__FILE__
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
505
plugins/GroupPrivateMessage/GroupPrivateMessagePlugin.php
Normal file
505
plugins/GroupPrivateMessage/GroupPrivateMessagePlugin.php
Normal file
|
@ -0,0 +1,505 @@
|
|||
<?php
|
||||
/**
|
||||
* StatusNet - the distributed open-source microblogging tool
|
||||
* Copyright (C) 2011, StatusNet, Inc.
|
||||
*
|
||||
* Private groups for StatusNet 0.9.x
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* 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 Privacy
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @copyright 2011 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET')) {
|
||||
// This check helps protect against security problems;
|
||||
// your code file can't be executed directly from the web.
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Private groups
|
||||
*
|
||||
* This plugin allows users to send private messages to a group.
|
||||
*
|
||||
* @category Privacy
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @copyright 2011 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
class GroupPrivateMessagePlugin extends Plugin
|
||||
{
|
||||
/**
|
||||
* Database schema setup
|
||||
*
|
||||
* @see Schema
|
||||
* @see ColumnDef
|
||||
*
|
||||
* @return boolean hook value
|
||||
*/
|
||||
|
||||
function onCheckSchema()
|
||||
{
|
||||
$schema = Schema::get();
|
||||
|
||||
// For storing user-submitted flags on profiles
|
||||
|
||||
$schema->ensureTable('group_privacy_settings',
|
||||
array(new ColumnDef('group_id',
|
||||
'integer',
|
||||
null,
|
||||
false,
|
||||
'PRI'),
|
||||
new ColumnDef('allow_privacy',
|
||||
'integer'),
|
||||
new ColumnDef('allow_sender',
|
||||
'integer'),
|
||||
new ColumnDef('created',
|
||||
'datetime'),
|
||||
new ColumnDef('modified',
|
||||
'timestamp')));
|
||||
|
||||
$schema->ensureTable('group_message',
|
||||
array(new ColumnDef('id',
|
||||
'char',
|
||||
36,
|
||||
false,
|
||||
'PRI'),
|
||||
new ColumnDef('uri',
|
||||
'varchar',
|
||||
255,
|
||||
false,
|
||||
'UNI'),
|
||||
new ColumnDef('from_profile',
|
||||
'integer',
|
||||
null,
|
||||
false,
|
||||
'MUL'),
|
||||
new ColumnDef('to_group',
|
||||
'integer',
|
||||
null,
|
||||
false,
|
||||
'MUL'),
|
||||
new ColumnDef('content',
|
||||
'text'),
|
||||
new ColumnDef('rendered',
|
||||
'text'),
|
||||
new ColumnDef('url',
|
||||
'varchar',
|
||||
255,
|
||||
false,
|
||||
'UNI'),
|
||||
new ColumnDef('created',
|
||||
'datetime')));
|
||||
|
||||
$schema->ensureTable('group_message_profile',
|
||||
array(new ColumnDef('to_profile',
|
||||
'integer',
|
||||
null,
|
||||
false,
|
||||
'PRI'),
|
||||
new ColumnDef('group_message_id',
|
||||
'char',
|
||||
36,
|
||||
false,
|
||||
'PRI'),
|
||||
new ColumnDef('created',
|
||||
'datetime')));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load related modules when needed
|
||||
*
|
||||
* @param string $cls Name of the class to be loaded
|
||||
*
|
||||
* @return boolean hook value
|
||||
*/
|
||||
|
||||
function onAutoload($cls)
|
||||
{
|
||||
$dir = dirname(__FILE__);
|
||||
|
||||
switch ($cls)
|
||||
{
|
||||
case 'GroupinboxAction':
|
||||
case 'ShowgroupmessageAction':
|
||||
case 'NewgroupmessageAction':
|
||||
include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
|
||||
return false;
|
||||
case 'Group_privacy_settings':
|
||||
case 'Group_message':
|
||||
case 'Group_message_profile':
|
||||
include_once $dir . '/'.$cls.'.php';
|
||||
return false;
|
||||
case 'GroupMessageCommand':
|
||||
case 'GroupMessageList':
|
||||
case 'GroupMessageListItem':
|
||||
case 'GroupMessageForm':
|
||||
include_once $dir . '/'.strtolower($cls).'.php';
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map URLs to actions
|
||||
*
|
||||
* @param Net_URL_Mapper $m path-to-action mapper
|
||||
*
|
||||
* @return boolean hook value
|
||||
*/
|
||||
|
||||
function onRouterInitialized($m)
|
||||
{
|
||||
$m->connect('group/:nickname/inbox',
|
||||
array('action' => 'groupinbox'),
|
||||
array('nickname' => Nickname::DISPLAY_FMT));
|
||||
|
||||
$m->connect('group/message/:id',
|
||||
array('action' => 'showgroupmessage'),
|
||||
array('id' => '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'));
|
||||
|
||||
$m->connect('group/:nickname/message/new',
|
||||
array('action' => 'newgroupmessage'),
|
||||
array('nickname' => Nickname::DISPLAY_FMT));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add group inbox to the menu
|
||||
*
|
||||
* @param Action $action The current action handler. Use this to
|
||||
* do any output.
|
||||
*
|
||||
* @return boolean hook value; true means continue processing, false means stop.
|
||||
*
|
||||
* @see Action
|
||||
*/
|
||||
|
||||
function onEndGroupGroupNav($groupnav)
|
||||
{
|
||||
$action = $groupnav->action;
|
||||
$group = $groupnav->group;
|
||||
|
||||
$action->menuItem(common_local_url('groupinbox',
|
||||
array('nickname' => $group->nickname)),
|
||||
_m('Inbox'),
|
||||
_m('Private messages for this group'),
|
||||
$action->trimmed('action') == 'groupinbox',
|
||||
'nav_group_inbox');
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create default group privacy settings at group create time
|
||||
*
|
||||
* @param User_group $group Group that was just created
|
||||
*
|
||||
* @result boolean hook value
|
||||
*/
|
||||
|
||||
function onEndGroupSave($group)
|
||||
{
|
||||
$gps = new Group_privacy_settings();
|
||||
|
||||
$gps->group_id = $group->id;
|
||||
$gps->allow_privacy = Group_privacy_settings::SOMETIMES;
|
||||
$gps->allow_sender = Group_privacy_settings::MEMBER;
|
||||
$gps->created = common_sql_now();
|
||||
$gps->modified = $gps->created;
|
||||
|
||||
// This will throw an exception on error
|
||||
|
||||
$gps->insert();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show group privacy controls on group edit form
|
||||
*
|
||||
* @param GroupEditForm $form form being shown
|
||||
*/
|
||||
|
||||
function onEndGroupEditFormData($form)
|
||||
{
|
||||
$gps = null;
|
||||
|
||||
if (!empty($form->group)) {
|
||||
$gps = Group_privacy_settings::staticGet('group_id', $form->group->id);
|
||||
}
|
||||
|
||||
$form->out->elementStart('li');
|
||||
$form->out->dropdown('allow_privacy',
|
||||
_('Private messages'),
|
||||
array(Group_privacy_settings::SOMETIMES => _('Sometimes'),
|
||||
Group_privacy_settings::ALWAYS => _('Always'),
|
||||
Group_privacy_settings::NEVER => _('Never')),
|
||||
_('Whether to allow private messages to this group'),
|
||||
false,
|
||||
(empty($gps)) ? Group_privacy_settings::SOMETIMES : $gps->allow_privacy);
|
||||
$form->out->elementEnd('li');
|
||||
$form->out->elementStart('li');
|
||||
$form->out->dropdown('allow_sender',
|
||||
_('Private sender'),
|
||||
array(Group_privacy_settings::EVERYONE => _('Everyone'),
|
||||
Group_privacy_settings::MEMBER => _('Member'),
|
||||
Group_privacy_settings::ADMIN => _('Admin')),
|
||||
_('Who can send private messages to the group'),
|
||||
false,
|
||||
(empty($gps)) ? Group_privacy_settings::MEMBER : $gps->allow_sender);
|
||||
$form->out->elementEnd('li');
|
||||
return true;
|
||||
}
|
||||
|
||||
function onEndGroupSaveForm($action)
|
||||
{
|
||||
$gps = null;
|
||||
|
||||
if (!empty($action->group)) {
|
||||
$gps = Group_privacy_settings::staticGet('group_id', $action->group->id);
|
||||
}
|
||||
|
||||
$orig = null;
|
||||
|
||||
if (empty($gps)) {
|
||||
$gps = new Group_privacy_settings();
|
||||
$gps->group_id = $action->group->id;
|
||||
} else {
|
||||
$orig = clone($gps);
|
||||
}
|
||||
|
||||
$gps->allow_privacy = $action->trimmed('allow_privacy');
|
||||
$gps->allow_sender = $action->trimmed('allow_sender');
|
||||
|
||||
if (empty($orig)) {
|
||||
$gps->created = common_sql_now();
|
||||
$gps->insert();
|
||||
} else {
|
||||
$gps->update($orig);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overload 'd' command to send private messages to groups.
|
||||
*
|
||||
* 'd !group word word word' will send the private message
|
||||
* 'word word word' to the group 'group'.
|
||||
*
|
||||
* @param string $cmd Command being run
|
||||
* @param string $arg Rest of the message (including address)
|
||||
* @param User $user User sending the message
|
||||
* @param Command &$result The resulting command object to be run.
|
||||
*
|
||||
* @return boolean hook value
|
||||
*/
|
||||
function onStartIntepretCommand($cmd, $arg, $user, &$result)
|
||||
{
|
||||
if ($cmd == 'd' || $cmd == 'dm') {
|
||||
|
||||
$this->debug('Got a d command');
|
||||
|
||||
// Break off the first word as the address
|
||||
|
||||
$pieces = explode(' ', $arg, 2);
|
||||
|
||||
if (count($pieces) == 1) {
|
||||
$pieces[] = null;
|
||||
}
|
||||
|
||||
list($addr, $msg) = $pieces;
|
||||
|
||||
if (!empty($addr) && $addr[0] == '!') {
|
||||
$result = new GroupMessageCommand($user, substr($addr, 1), $msg);
|
||||
Event::handle('EndInterpretCommand', array($cmd, $arg, $user, $result));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* To add a "Message" button to the group profile page
|
||||
*
|
||||
* @param Action $action The showgroup action being shown
|
||||
* @param User_group $group The current group
|
||||
*
|
||||
* @return boolean hook value
|
||||
*/
|
||||
function onEndGroupActionsList($action, $group)
|
||||
{
|
||||
$cur = common_current_user();
|
||||
|
||||
if (empty($cur)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
Group_privacy_settings::ensurePost($cur, $group);
|
||||
} catch (Exception $e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$action->elementStart('li', 'entity_send-a-message');
|
||||
$action->element('a', array('href' => common_local_url('newgroupmessage', array('nickname' => $group->nickname)),
|
||||
'title' => _('Send a direct message to this group')),
|
||||
_('Message'));
|
||||
// $form = new GroupMessageForm($action, $group);
|
||||
// $form->hidden = true;
|
||||
// $form->show();
|
||||
$action->elementEnd('li');
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* When saving a notice, check its groups. If any of them has
|
||||
* privacy == always, force a group private message to all mentioned groups.
|
||||
* If any of the groups disallows private messages, skip it.
|
||||
*
|
||||
* @param
|
||||
*
|
||||
*/
|
||||
|
||||
function onStartNoticeSave(&$notice) {
|
||||
|
||||
// Look for group tags
|
||||
// FIXME: won't work for remote groups
|
||||
// @fixme if Notice::saveNew is refactored so we can just pull its list
|
||||
// of groups between processing and saving, make use of it
|
||||
|
||||
$count = preg_match_all('/(?:^|\s)!(' . Nickname::DISPLAY_FMT . ')/',
|
||||
strtolower($notice->content),
|
||||
$match);
|
||||
|
||||
$groups = array();
|
||||
$ignored = array();
|
||||
|
||||
$forcePrivate = false;
|
||||
|
||||
if ($count > 0) {
|
||||
|
||||
/* Add them to the database */
|
||||
|
||||
foreach (array_unique($match[1]) as $nickname) {
|
||||
|
||||
$group = User_group::getForNickname($nickname, $profile);
|
||||
|
||||
if (empty($group)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$gps = Group_privacy_settings::forGroup($group);
|
||||
|
||||
switch ($gps->allow_privacy) {
|
||||
case Group_privacy_settings::ALWAYS:
|
||||
$forcePrivate = true;
|
||||
// fall through
|
||||
case Group_privacy_settings::SOMETIMES:
|
||||
$groups[] = $group;
|
||||
break;
|
||||
case Group_privacy_settings::NEVER:
|
||||
$ignored[] = $group;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($forcePrivate) {
|
||||
|
||||
foreach ($ignored as $group) {
|
||||
common_log(LOG_NOTICE,
|
||||
"Notice forced to group direct message ".
|
||||
"but group ".$group->nickname." does not allow them.");
|
||||
}
|
||||
|
||||
$user = User::staticGet('id', $notice->profile_id);
|
||||
|
||||
if (empty($user)) {
|
||||
common_log(LOG_WARNING,
|
||||
"Notice forced to group direct message ".
|
||||
"but profile ".$notice->profile_id." is not a local user.");
|
||||
} else {
|
||||
foreach ($groups as $group) {
|
||||
Group_message::send($user, $group, $notice->content);
|
||||
}
|
||||
}
|
||||
|
||||
// Don't save the notice!
|
||||
// FIXME: this is probably cheating.
|
||||
throw new ClientException(sprintf(_('Forced notice to private group message.')),
|
||||
200);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show an indicator that the group is (essentially) private on the group page
|
||||
*
|
||||
* @param Action $action The action being shown
|
||||
* @param User_group $group The group being shown
|
||||
*
|
||||
* @return boolean hook value
|
||||
*/
|
||||
|
||||
function onEndGroupProfileElements($action, $group)
|
||||
{
|
||||
$gps = Group_privacy_settings::forGroup($group);
|
||||
|
||||
if ($gps->allow_privacy == Group_privacy_settings::ALWAYS) {
|
||||
$action->element('p', 'privategroupindicator', _('Private'));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function onStartShowExportData($action)
|
||||
{
|
||||
if ($action instanceof ShowgroupAction) {
|
||||
$gps = Group_privacy_settings::forGroup($action->group);
|
||||
|
||||
if ($gps->allow_privacy == Group_privacy_settings::ALWAYS) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function onPluginVersion(&$versions)
|
||||
{
|
||||
$versions[] = array('name' => 'GroupPrivateMessage',
|
||||
'version' => STATUSNET_VERSION,
|
||||
'author' => 'Evan Prodromou',
|
||||
'homepage' => 'http://status.net/wiki/Plugin:GroupPrivateMessage',
|
||||
'rawdescription' =>
|
||||
_m('Allow posting DMs to a group.'));
|
||||
return true;
|
||||
}
|
||||
}
|
208
plugins/GroupPrivateMessage/Group_message.php
Normal file
208
plugins/GroupPrivateMessage/Group_message.php
Normal file
|
@ -0,0 +1,208 @@
|
|||
<?php
|
||||
/**
|
||||
* Data class for group direct messages
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Data
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
||||
* @link http://status.net/
|
||||
*
|
||||
* StatusNet - the distributed open-source microblogging tool
|
||||
* Copyright (C) 2009, StatusNet, 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/>.
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
|
||||
|
||||
/**
|
||||
* Data class for group direct messages
|
||||
*
|
||||
* @category GroupPrivateMessage
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
||||
* @link http://status.net/
|
||||
*
|
||||
* @see DB_DataObject
|
||||
*/
|
||||
|
||||
class Group_message extends Memcached_DataObject
|
||||
{
|
||||
public $__table = 'group_message'; // table name
|
||||
public $id; // char(36) primary_key not_null
|
||||
public $uri; // varchar(255)
|
||||
public $from_profile; // int
|
||||
public $to_group; // int
|
||||
public $content;
|
||||
public $rendered;
|
||||
public $url;
|
||||
public $created;
|
||||
|
||||
/**
|
||||
* Get an instance by key
|
||||
*
|
||||
* This is a utility method to get a single instance with a given key value.
|
||||
*
|
||||
* @param string $k Key to use to lookup (usually 'user_id' for this class)
|
||||
* @param mixed $v Value to lookup
|
||||
*
|
||||
* @return Group_message object found, or null for no hits
|
||||
*
|
||||
*/
|
||||
function staticGet($k, $v=null)
|
||||
{
|
||||
return Memcached_DataObject::staticGet('Group_message', $k, $v);
|
||||
}
|
||||
|
||||
/**
|
||||
* return table definition for DB_DataObject
|
||||
*
|
||||
* DB_DataObject needs to know something about the table to manipulate
|
||||
* instances. This method provides all the DB_DataObject needs to know.
|
||||
*
|
||||
* @return array array of column definitions
|
||||
*/
|
||||
function table()
|
||||
{
|
||||
return array('id' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL,
|
||||
'uri' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL,
|
||||
'from_profile' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
|
||||
'to_group' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
|
||||
'content' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL,
|
||||
'rendered' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL,
|
||||
'url' => DB_DATAOBJECT_STR,
|
||||
'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* return key definitions for DB_DataObject
|
||||
*
|
||||
* DB_DataObject needs to know about keys that the table has, since it
|
||||
* won't appear in StatusNet's own keys list. In most cases, this will
|
||||
* simply reference your keyTypes() function.
|
||||
*
|
||||
* @return array list of key field names
|
||||
*/
|
||||
function keys()
|
||||
{
|
||||
return array_keys($this->keyTypes());
|
||||
}
|
||||
|
||||
/**
|
||||
* return key definitions for Memcached_DataObject
|
||||
*
|
||||
* @return array associative array of key definitions, field name to type:
|
||||
* 'K' for primary key: for compound keys, add an entry for each component;
|
||||
* 'U' for unique keys: compound keys are not well supported here.
|
||||
*/
|
||||
function keyTypes()
|
||||
{
|
||||
return array('id' => 'K', 'uri' => 'U');
|
||||
}
|
||||
|
||||
static function send($user, $group, $text)
|
||||
{
|
||||
if (!$user->hasRight(Right::NEWMESSAGE)) {
|
||||
// XXX: maybe break this out into a separate right
|
||||
throw new Exception(sprintf(_('User %s not allowed to send private messages.'),
|
||||
$user->nickname));
|
||||
}
|
||||
|
||||
Group_privacy_settings::ensurePost($user, $group);
|
||||
|
||||
$text = $user->shortenLinks($text);
|
||||
|
||||
// We use the same limits as for 'regular' private messages.
|
||||
|
||||
if (Message::contentTooLong($text)) {
|
||||
throw new Exception(sprintf(_m('That\'s too long. Maximum message size is %d character.',
|
||||
'That\'s too long. Maximum message size is %d characters.',
|
||||
Message::maxContent()),
|
||||
Message::maxContent()));
|
||||
}
|
||||
|
||||
// Valid! Let's do this thing!
|
||||
|
||||
$gm = new Group_message();
|
||||
|
||||
$gm->id = UUID::gen();
|
||||
$gm->uri = common_local_url('showgroupmessage', array('id' => $gm->id));
|
||||
$gm->from_profile = $user->id;
|
||||
$gm->to_group = $group->id;
|
||||
$gm->content = $text; // XXX: is this cool?!
|
||||
$gm->rendered = common_render_text($text);
|
||||
$gm->url = $gm->uri;
|
||||
$gm->created = common_sql_now();
|
||||
|
||||
// This throws a conniption if there's a problem
|
||||
|
||||
$gm->insert();
|
||||
|
||||
$gm->distribute();
|
||||
|
||||
return $gm;
|
||||
}
|
||||
|
||||
function distribute()
|
||||
{
|
||||
$group = User_group::staticGet('id', $this->to_group);
|
||||
|
||||
$member = $group->getMembers();
|
||||
|
||||
while ($member->fetch()) {
|
||||
Group_message_profile::send($this, $member);
|
||||
}
|
||||
}
|
||||
|
||||
function getGroup()
|
||||
{
|
||||
$group = User_group::staticGet('id', $this->to_group);
|
||||
if (empty($group)) {
|
||||
throw new ServerException(_('No group for group message'));
|
||||
}
|
||||
return $group;
|
||||
}
|
||||
|
||||
function getSender()
|
||||
{
|
||||
$sender = Profile::staticGet('id', $this->from_profile);
|
||||
if (empty($sender)) {
|
||||
throw new ServerException(_('No sender for group message'));
|
||||
}
|
||||
return $sender;
|
||||
}
|
||||
|
||||
static function forGroup($group, $offset, $limit)
|
||||
{
|
||||
// XXX: cache
|
||||
$gm = new Group_message();
|
||||
|
||||
$gm->to_group = $group->id;
|
||||
$gm->orderBy('created DESC');
|
||||
$gm->limit($offset, $limit);
|
||||
|
||||
$gm->find();
|
||||
|
||||
return $gm;
|
||||
}
|
||||
|
||||
}
|
189
plugins/GroupPrivateMessage/Group_message_profile.php
Normal file
189
plugins/GroupPrivateMessage/Group_message_profile.php
Normal file
|
@ -0,0 +1,189 @@
|
|||
<?php
|
||||
/**
|
||||
* Who received a group message
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Data
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
||||
* @link http://status.net/
|
||||
*
|
||||
* StatusNet - the distributed open-source microblogging tool
|
||||
* Copyright (C) 2011, StatusNet, 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/>.
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
|
||||
|
||||
/**
|
||||
* Data class for group direct messages for users
|
||||
*
|
||||
* @category GroupPrivateMessage
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
||||
* @link http://status.net/
|
||||
*
|
||||
* @see DB_DataObject
|
||||
*/
|
||||
|
||||
class Group_message_profile extends Memcached_DataObject
|
||||
{
|
||||
public $__table = 'group_message_profile'; // table name
|
||||
public $to_profile; // int
|
||||
public $group_message_id; // char(36) primary_key not_null
|
||||
public $created;
|
||||
|
||||
/**
|
||||
* Get an instance by key
|
||||
*
|
||||
* This is a utility method to get a single instance with a given key value.
|
||||
*
|
||||
* @param string $k Key to use to lookup (usually 'user_id' for this class)
|
||||
* @param mixed $v Value to lookup
|
||||
*
|
||||
* @return Group_message object found, or null for no hits
|
||||
*
|
||||
*/
|
||||
function staticGet($k, $v=null)
|
||||
{
|
||||
return Memcached_DataObject::staticGet('Group_message_profile', $k, $v);
|
||||
}
|
||||
|
||||
/**
|
||||
* return table definition for DB_DataObject
|
||||
*
|
||||
* DB_DataObject needs to know something about the table to manipulate
|
||||
* instances. This method provides all the DB_DataObject needs to know.
|
||||
*
|
||||
* @return array array of column definitions
|
||||
*/
|
||||
function table()
|
||||
{
|
||||
return array('to_profile' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
|
||||
'group_message_id' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL,
|
||||
'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* return key definitions for DB_DataObject
|
||||
*
|
||||
* DB_DataObject needs to know about keys that the table has, since it
|
||||
* won't appear in StatusNet's own keys list. In most cases, this will
|
||||
* simply reference your keyTypes() function.
|
||||
*
|
||||
* @return array list of key field names
|
||||
*/
|
||||
function keys()
|
||||
{
|
||||
return array_keys($this->keyTypes());
|
||||
}
|
||||
|
||||
/**
|
||||
* return key definitions for Memcached_DataObject
|
||||
*
|
||||
* @return array associative array of key definitions, field name to type:
|
||||
* 'K' for primary key: for compound keys, add an entry for each component;
|
||||
* 'U' for unique keys: compound keys are not well supported here.
|
||||
*/
|
||||
function keyTypes()
|
||||
{
|
||||
return array('to_profile' => 'K', 'group_message_id' => 'K');
|
||||
}
|
||||
|
||||
/**
|
||||
* No sequence keys in this table.
|
||||
*/
|
||||
function sequenceKey()
|
||||
{
|
||||
return array(false, false, false);
|
||||
}
|
||||
|
||||
function send($gm, $profile)
|
||||
{
|
||||
$gmp = new Group_message_profile();
|
||||
|
||||
$gmp->group_message_id = $gm->id;
|
||||
$gmp->to_profile = $profile->id;
|
||||
$gmp->created = common_sql_now();
|
||||
|
||||
$gmp->insert();
|
||||
|
||||
$gmp->notify();
|
||||
|
||||
return $gmp;
|
||||
}
|
||||
|
||||
function notify()
|
||||
{
|
||||
// XXX: add more here
|
||||
$this->notifyByMail();
|
||||
}
|
||||
|
||||
function notifyByMail()
|
||||
{
|
||||
$to = User::staticGet('id', $this->to_profile);
|
||||
|
||||
if (empty($to) || is_null($to->email) || !$to->emailnotifymsg) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$gm = Group_message::staticGet('id', $this->group_message_id);
|
||||
|
||||
$from_profile = Profile::staticGet('id', $gm->from_profile);
|
||||
|
||||
$group = $gm->getGroup();
|
||||
|
||||
common_switch_locale($to->language);
|
||||
|
||||
// TRANS: Subject for direct-message notification email.
|
||||
// TRANS: %s is the sending user's nickname.
|
||||
$subject = sprintf(_('New private message from %s to group %s'), $from->nickname, $group->nickname);
|
||||
|
||||
$from_profile = $from->getProfile();
|
||||
|
||||
// TRANS: Body for direct-message notification email.
|
||||
// TRANS: %1$s is the sending user's long name, %2$s is the sending user's nickname,
|
||||
// TRANS: %3$s is the message content, %4$s a URL to the message,
|
||||
// TRANS: %5$s is the StatusNet sitename.
|
||||
$body = sprintf(_("%1\$s (%2\$s) sent a private message to group %3\$s:\n\n".
|
||||
"------------------------------------------------------\n".
|
||||
"%4\$s\n".
|
||||
"------------------------------------------------------\n\n".
|
||||
"You can reply to their message here:\n\n".
|
||||
"%5\$s\n\n".
|
||||
"Don't reply to this email; it won't get to them.\n\n".
|
||||
"With kind regards,\n".
|
||||
"%6\$s\n"),
|
||||
$from_profile->getBestName(),
|
||||
$from->nickname,
|
||||
$group->nickname,
|
||||
$this->content,
|
||||
common_local_url('newmessage', array('to' => $from->id)),
|
||||
common_config('site', 'name'));
|
||||
|
||||
$headers = _mail_prepare_headers('message', $to->nickname, $from->nickname);
|
||||
|
||||
common_switch_locale();
|
||||
|
||||
return mail_to_user($to, $subject, $body, $headers);
|
||||
}
|
||||
}
|
201
plugins/GroupPrivateMessage/Group_privacy_settings.php
Normal file
201
plugins/GroupPrivateMessage/Group_privacy_settings.php
Normal file
|
@ -0,0 +1,201 @@
|
|||
<?php
|
||||
/**
|
||||
* Data class for group privacy settings
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Data
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
||||
* @link http://status.net/
|
||||
*
|
||||
* StatusNet - the distributed open-source microblogging tool
|
||||
* Copyright (C) 2011, StatusNet, 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/>.
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data class for group privacy
|
||||
*
|
||||
* Stores admin preferences about the group.
|
||||
*
|
||||
* @category Action
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
||||
* @link http://status.net/
|
||||
*
|
||||
* @see DB_DataObject
|
||||
*/
|
||||
|
||||
class Group_privacy_settings extends Memcached_DataObject
|
||||
{
|
||||
public $__table = 'group_privacy_settings';
|
||||
/** ID of the group. */
|
||||
public $group_id;
|
||||
/** When to allow privacy: always, sometimes, or never. */
|
||||
public $allow_privacy;
|
||||
/** Who can send private messages: everyone, member, admin */
|
||||
public $allow_sender;
|
||||
/** row creation timestamp */
|
||||
public $created;
|
||||
/** Last-modified timestamp */
|
||||
public $modified;
|
||||
|
||||
/** NEVER is */
|
||||
|
||||
const SOMETIMES = -1;
|
||||
const NEVER = 0;
|
||||
const ALWAYS = 1;
|
||||
|
||||
/** These are bit-mappy, as a hedge against the future. */
|
||||
|
||||
const EVERYONE = 1;
|
||||
const MEMBER = 2;
|
||||
const ADMIN = 4;
|
||||
|
||||
/**
|
||||
* Get an instance by key
|
||||
*
|
||||
* This is a utility method to get a single instance with a given key value.
|
||||
*
|
||||
* @param string $k Key to use to lookup (usually 'user_id' for this class)
|
||||
* @param mixed $v Value to lookup
|
||||
*
|
||||
* @return User_greeting_count object found, or null for no hits
|
||||
*/
|
||||
|
||||
function staticGet($k, $v=null)
|
||||
{
|
||||
return Memcached_DataObject::staticGet('Group_privacy_settings', $k, $v);
|
||||
}
|
||||
|
||||
/**
|
||||
* return table definition for DB_DataObject
|
||||
*
|
||||
* DB_DataObject needs to know something about the table to manipulate
|
||||
* instances. This method provides all the DB_DataObject needs to know.
|
||||
*
|
||||
* @return array array of column definitions
|
||||
*/
|
||||
|
||||
function table()
|
||||
{
|
||||
return array('group_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
|
||||
'allow_privacy' => DB_DATAOBJECT_INT,
|
||||
'allow_sender' => DB_DATAOBJECT_INT,
|
||||
'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL,
|
||||
'modified' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* return key definitions for DB_DataObject
|
||||
*
|
||||
* DB_DataObject needs to know about keys that the table has, since it
|
||||
* won't appear in StatusNet's own keys list. In most cases, this will
|
||||
* simply reference your keyTypes() function.
|
||||
*
|
||||
* @return array list of key field names
|
||||
*/
|
||||
|
||||
function keys()
|
||||
{
|
||||
return array_keys($this->keyTypes());
|
||||
}
|
||||
|
||||
/**
|
||||
* return key definitions for Memcached_DataObject
|
||||
*
|
||||
* @return array associative array of key definitions, field name to type:
|
||||
* 'K' for primary key: for compound keys, add an entry for each component;
|
||||
* 'U' for unique keys: compound keys are not well supported here.
|
||||
*/
|
||||
|
||||
function keyTypes()
|
||||
{
|
||||
return array('group_id' => 'K');
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic formula for non-autoincrementing integer primary keys
|
||||
*
|
||||
* @return array magic three-false array that stops auto-incrementing.
|
||||
*/
|
||||
|
||||
function sequenceKey()
|
||||
{
|
||||
return array(false, false, false);
|
||||
}
|
||||
|
||||
function forGroup($group)
|
||||
{
|
||||
$gps = Group_privacy_settings::staticGet('group_id', $group->id);
|
||||
|
||||
if (empty($gps)) {
|
||||
// make a fake one with defaults
|
||||
$gps = new Group_privacy_settings();
|
||||
$gps->allow_privacy = Group_privacy_settings::SOMETIMES;
|
||||
$gps->allow_sender = Group_privacy_settings::MEMBER;
|
||||
}
|
||||
|
||||
return $gps;
|
||||
}
|
||||
|
||||
function ensurePost($user, $group)
|
||||
{
|
||||
$gps = self::forGroup($group);
|
||||
|
||||
if ($gps->allow_privacy == Group_privacy_settings::NEVER) {
|
||||
throw new Exception(sprintf(_('Group %s does not allow private messages.'),
|
||||
$group->nickname));
|
||||
}
|
||||
|
||||
switch ($gps->allow_sender) {
|
||||
case Group_privacy_settings::EVERYONE:
|
||||
$profile = $user->getProfile();
|
||||
if (Group_block::isBlocked($group, $profile)) {
|
||||
throw new Exception(sprintf(_('User %s is blocked from group %s.'),
|
||||
$user->nickname,
|
||||
$group->nickname));
|
||||
}
|
||||
break;
|
||||
case Group_privacy_settings::MEMBER:
|
||||
if (!$user->isMember($group)) {
|
||||
throw new Exception(sprintf(_('User %s is not a member of group %s.'),
|
||||
$user->nickname,
|
||||
$group->nickname));
|
||||
}
|
||||
break;
|
||||
case Group_privacy_settings::ADMIN:
|
||||
if (!$user->isAdmin($group)) {
|
||||
throw new Exception(sprintf(_('User %s is not an administrator of group %s.'),
|
||||
$user->nickname,
|
||||
$group->nickname));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Exception(sprintf(_('Unknown privacy settings for group %s.'),
|
||||
$group->nickname));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
208
plugins/GroupPrivateMessage/groupinbox.php
Normal file
208
plugins/GroupPrivateMessage/groupinbox.php
Normal file
|
@ -0,0 +1,208 @@
|
|||
<?php
|
||||
/**
|
||||
* StatusNet - the distributed open-source microblogging tool
|
||||
* Copyright (C) 2011, StatusNet, Inc.
|
||||
*
|
||||
* List of private messages to this group
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* 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 GroupPrivateMessage
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @copyright 2011 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET')) {
|
||||
// This check helps protect against security problems;
|
||||
// your code file can't be executed directly from the web.
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a list of private messages to this group
|
||||
*
|
||||
* @category GroupPrivateMessage
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @copyright 2011 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
class GroupinboxAction extends GroupDesignAction
|
||||
{
|
||||
var $gm;
|
||||
|
||||
/**
|
||||
* For initializing members of the class.
|
||||
*
|
||||
* @param array $argarray misc. arguments
|
||||
*
|
||||
* @return boolean true
|
||||
*/
|
||||
function prepare($argarray)
|
||||
{
|
||||
parent::prepare($argarray);
|
||||
|
||||
$cur = common_current_user();
|
||||
|
||||
if (empty($cur)) {
|
||||
throw new ClientException(_('Only for logged-in users'), 403);
|
||||
}
|
||||
|
||||
$nicknameArg = $this->trimmed('nickname');
|
||||
|
||||
$nickname = common_canonical_nickname($nicknameArg);
|
||||
|
||||
if ($nickname != $nicknameArg) {
|
||||
$url = common_local_url('groupinbox', array('nickname' => $nickname));
|
||||
common_redirect($url);
|
||||
return false;
|
||||
}
|
||||
|
||||
$localGroup = Local_group::staticGet('nickname', $nickname);
|
||||
|
||||
if (empty($localGroup)) {
|
||||
throw new ClientException(_('No such group'), 404);
|
||||
}
|
||||
|
||||
$this->group = User_group::staticGet('id', $localGroup->group_id);
|
||||
|
||||
if (empty($this->group)) {
|
||||
throw new ClientException(_('No such group'), 404);
|
||||
}
|
||||
|
||||
if (!$cur->isMember($this->group)) {
|
||||
throw new ClientException(_('Only for members'), 403);
|
||||
}
|
||||
|
||||
$this->page = $this->trimmed('page');
|
||||
|
||||
if (!$this->page) {
|
||||
$this->page = 1;
|
||||
}
|
||||
|
||||
$this->gm = Group_message::forGroup($this->group,
|
||||
($this->page - 1) * MESSAGES_PER_PAGE,
|
||||
MESSAGES_PER_PAGE + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
function showLocalNav()
|
||||
{
|
||||
$nav = new GroupNav($this, $this->group);
|
||||
$nav->show();
|
||||
}
|
||||
|
||||
function showNoticeForm()
|
||||
{
|
||||
$form = new GroupMessageForm($this, $this->group);
|
||||
$form->show();
|
||||
}
|
||||
|
||||
function showContent()
|
||||
{
|
||||
$gml = new GroupMessageList($this, $this->gm);
|
||||
$cnt = $gml->show();
|
||||
|
||||
if ($cnt == 0) {
|
||||
$this->element('p', 'guide', _m('This group has not received any private messages.'));
|
||||
}
|
||||
$this->pagination($this->page > 1,
|
||||
$cnt > MESSAGES_PER_PAGE,
|
||||
$this->page,
|
||||
'groupinbox',
|
||||
array('nickname' => $this->group->nickname));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler method
|
||||
*
|
||||
* @param array $argarray is ignored since it's now passed in in prepare()
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function handle($argarray=null)
|
||||
{
|
||||
$this->showPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if read only.
|
||||
*
|
||||
* MAY override
|
||||
*
|
||||
* @param array $args other arguments
|
||||
*
|
||||
* @return boolean is read only action?
|
||||
*/
|
||||
function isReadOnly($args)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Title of the page
|
||||
*
|
||||
* @return string page title, with page number
|
||||
*/
|
||||
function title()
|
||||
{
|
||||
$base = $this->group->getFancyName();
|
||||
|
||||
if ($this->page == 1) {
|
||||
return sprintf(_('%s group inbox'), $base);
|
||||
} else {
|
||||
// TRANS: Page title for any but first group page.
|
||||
// TRANS: %1$s is a group name, $2$s is a page number.
|
||||
return sprintf(_('%1$s group inbox, page %2$d'),
|
||||
$base,
|
||||
$this->page);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the page notice
|
||||
*
|
||||
* Shows instructions for the page
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
||||
function showPageNotice()
|
||||
{
|
||||
$instr = $this->getInstructions();
|
||||
$output = common_markup_to_html($instr);
|
||||
|
||||
$this->elementStart('div', 'instructions');
|
||||
$this->raw($output);
|
||||
$this->elementEnd('div');
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructions for using this page
|
||||
*
|
||||
* @return string localised instructions for using the page
|
||||
*/
|
||||
function getInstructions()
|
||||
{
|
||||
// TRANS: Instructions for user inbox page.
|
||||
return _m('This is the group inbox, which lists all incoming private messages for this group.');
|
||||
}
|
||||
}
|
85
plugins/GroupPrivateMessage/groupmessagecommand.php
Normal file
85
plugins/GroupPrivateMessage/groupmessagecommand.php
Normal file
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
/**
|
||||
* StatusNet - the distributed open-source microblogging tool
|
||||
* Copyright (C) 2011, StatusNet, Inc.
|
||||
*
|
||||
* Command object for messages to groups
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* 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 Command
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @copyright 2011 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET')) {
|
||||
// This check helps protect against security problems;
|
||||
// your code file can't be executed directly from the web.
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Command object for messages to groups
|
||||
*
|
||||
* @category General
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @copyright 2011 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
class GroupMessageCommand extends Command
|
||||
{
|
||||
/** User sending the message. */
|
||||
var $user;
|
||||
/** Nickname of the group they're sending to. */
|
||||
var $nickname;
|
||||
/** Text of the message. */
|
||||
var $text;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param User $user User sending the message
|
||||
* @param string $nickname Nickname of the group
|
||||
* @param string $text Text of message
|
||||
*/
|
||||
|
||||
function __construct($user, $nickname, $text)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->nickname = $nickname;
|
||||
$this->text = $text;
|
||||
}
|
||||
|
||||
function handle($channel)
|
||||
{
|
||||
// Throws a command exception if group not found
|
||||
$group = $this->getGroup($this->nickname);
|
||||
|
||||
$gm = Group_message::send($this->user, $group, $this->text);
|
||||
|
||||
$channel->output($this->user,
|
||||
sprintf(_('Direct message to group %s sent.'),
|
||||
$group->nickname));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
166
plugins/GroupPrivateMessage/groupmessageform.php
Normal file
166
plugins/GroupPrivateMessage/groupmessageform.php
Normal file
|
@ -0,0 +1,166 @@
|
|||
<?php
|
||||
/**
|
||||
* StatusNet - the distributed open-source microblogging tool
|
||||
* Copyright (C) 2011, StatusNet, Inc.
|
||||
*
|
||||
* Form for posting a group message
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* 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 GroupPrivateMessage
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @copyright 2011 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET')) {
|
||||
// This check helps protect against security problems;
|
||||
// your code file can't be executed directly from the web.
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Form for posting a group message
|
||||
*
|
||||
* @category GroupPrivateMessage
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @copyright 2011 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
class GroupMessageForm extends Form
|
||||
{
|
||||
var $group;
|
||||
var $content;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param HTMLOutputter $out Output context
|
||||
* @param User_group $group Group to post to
|
||||
*
|
||||
* @todo add a drop-down list to post to any group
|
||||
*/
|
||||
|
||||
function __construct($out, $group, $content=null)
|
||||
{
|
||||
parent::__construct($out);
|
||||
|
||||
$this->group = $group;
|
||||
$this->content = $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Action for the form
|
||||
*/
|
||||
function action()
|
||||
{
|
||||
return common_local_url('newgroupmessage',
|
||||
array('nickname' => $this->group->nickname));
|
||||
}
|
||||
|
||||
/**
|
||||
* Legend for the form
|
||||
*
|
||||
* @param
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
function formLegend()
|
||||
{
|
||||
$this->out->element('legend',
|
||||
null,
|
||||
sprintf(_('Message to %s'), $this->group->nickname));
|
||||
}
|
||||
|
||||
/**
|
||||
* id for the form
|
||||
*
|
||||
* @param
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
|
||||
function id()
|
||||
{
|
||||
return 'form_notice-group-message';
|
||||
}
|
||||
|
||||
/**
|
||||
* class for the form
|
||||
*
|
||||
* @param
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
|
||||
function formClass()
|
||||
{
|
||||
return 'form_notice';
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry data
|
||||
*
|
||||
* @param
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
|
||||
function formData()
|
||||
{
|
||||
$this->out->element('label', array('for' => 'notice_data-text',
|
||||
'id' => 'notice_data-text-label'),
|
||||
sprintf(_('Direct message to %s'), $this->group->nickname));
|
||||
|
||||
$this->out->element('textarea', array('id' => 'notice_data-text',
|
||||
'cols' => 35,
|
||||
'rows' => 4,
|
||||
'name' => 'content'),
|
||||
($this->content) ? $this->content : '');
|
||||
|
||||
$contentLimit = Message::maxContent();
|
||||
|
||||
if ($contentLimit > 0) {
|
||||
$this->out->elementStart('dl', 'form_note');
|
||||
$this->out->element('dt', null, _('Available characters'));
|
||||
$this->out->element('dd', array('id' => 'notice_text-count'),
|
||||
$contentLimit);
|
||||
$this->out->elementEnd('dl');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Legend for the form
|
||||
*
|
||||
* @param
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
|
||||
function formActions()
|
||||
{
|
||||
$this->out->element('input', array('id' => 'notice_action-submit',
|
||||
'class' => 'submit',
|
||||
'name' => 'message_send',
|
||||
'type' => 'submit',
|
||||
'value' => _m('Send button for sending notice', 'Send')));
|
||||
}
|
||||
}
|
90
plugins/GroupPrivateMessage/groupmessagelist.php
Normal file
90
plugins/GroupPrivateMessage/groupmessagelist.php
Normal file
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
/**
|
||||
* StatusNet - the distributed open-source microblogging tool
|
||||
* Copyright (C) 2011, StatusNet, Inc.
|
||||
*
|
||||
* Widget for showing list of group messages
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* 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 GroupPrivateMessage
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @copyright 2011 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET')) {
|
||||
// This check helps protect against security problems;
|
||||
// your code file can't be executed directly from the web.
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Widget for showing list of group messages
|
||||
*
|
||||
* @category GroupPrivateMessage
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @copyright 2011 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
class GroupMessageList extends Widget
|
||||
{
|
||||
var $gm;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param HTMLOutputter $out output context
|
||||
* @param Group_message $gm Group message stream
|
||||
*/
|
||||
function __construct($out, $gm)
|
||||
{
|
||||
parent::__construct($out);
|
||||
$this->gm = $gm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function show()
|
||||
{
|
||||
$this->out->elementStart('ul', 'notices messages group-messages');
|
||||
|
||||
$cnt = 0;
|
||||
|
||||
while ($this->gm->fetch() && $cnt <= MESSAGES_PER_PAGE) {
|
||||
|
||||
$cnt++;
|
||||
|
||||
if ($cnt > MESSAGES_PER_PAGE) {
|
||||
break;
|
||||
}
|
||||
|
||||
$gmli = new GroupMessageListItem($this->out, $this->gm);
|
||||
$gmli->show();
|
||||
}
|
||||
|
||||
$this->out->elementEnd('ul');
|
||||
|
||||
return $cnt;
|
||||
}
|
||||
}
|
113
plugins/GroupPrivateMessage/groupmessagelistitem.php
Normal file
113
plugins/GroupPrivateMessage/groupmessagelistitem.php
Normal file
|
@ -0,0 +1,113 @@
|
|||
<?php
|
||||
/**
|
||||
* StatusNet - the distributed open-source microblogging tool
|
||||
* Copyright (C) 2011, StatusNet, Inc.
|
||||
*
|
||||
* Widget for showing an individual group message
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* 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 GroupPrivateMessage
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @copyright 2011 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET')) {
|
||||
// This check helps protect against security problems;
|
||||
// your code file can't be executed directly from the web.
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Widget for showing a single group message
|
||||
*
|
||||
* @category GroupPrivateMessage
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @copyright 2011 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
class GroupMessageListItem extends Widget
|
||||
{
|
||||
var $gm;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param HTMLOutputter $out output context
|
||||
* @param Group_message $gm Group message
|
||||
*/
|
||||
function __construct($out, $gm)
|
||||
{
|
||||
parent::__construct($out);
|
||||
$this->gm = $gm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the item
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function show()
|
||||
{
|
||||
$group = $this->gm->getGroup();
|
||||
$sender = $this->gm->getSender();
|
||||
|
||||
$this->out->elementStart('li', array('class' => 'hentry notice message group-message',
|
||||
'id' => 'message-' . $this->gm->id));
|
||||
|
||||
$this->out->elementStart('div', 'entry-title');
|
||||
$this->out->elementStart('span', 'vcard author');
|
||||
$this->out->elementStart('a',
|
||||
array('href' => $sender->profileurl,
|
||||
'class' => 'url'));
|
||||
$avatar = $sender->getAvatar(AVATAR_STREAM_SIZE);
|
||||
$this->out->element('img', array('src' => ($avatar) ?
|
||||
$avatar->displayUrl() :
|
||||
Avatar::defaultImage(AVATAR_STREAM_SIZE),
|
||||
'width' => AVATAR_STREAM_SIZE,
|
||||
'height' => AVATAR_STREAM_SIZE,
|
||||
'class' => 'photo avatar',
|
||||
'alt' => $sender->getBestName()));
|
||||
$this->out->element('span',
|
||||
array('class' => 'nickname fn'),
|
||||
$sender->nickname);
|
||||
$this->out->elementEnd('a');
|
||||
$this->out->elementEnd('span');
|
||||
|
||||
$this->out->elementStart('p', array('class' => 'entry-content message-content'));
|
||||
$this->out->raw($this->gm->rendered);
|
||||
$this->out->elementEnd('p');
|
||||
$this->out->elementEnd('div');
|
||||
|
||||
$this->out->elementStart('div', 'entry-content');
|
||||
$this->out->elementStart('a', array('rel' => 'bookmark',
|
||||
'class' => 'timestamp',
|
||||
'href' => $this->gm->url));
|
||||
$dt = common_date_iso8601($this->gm->created);
|
||||
$this->out->element('abbr', array('class' => 'published',
|
||||
'title' => $dt),
|
||||
common_date_string($this->gm->created));
|
||||
$this->out->elementEnd('a');
|
||||
$this->out->elementEnd('div');
|
||||
|
||||
$this->out->elementEnd('li');
|
||||
}
|
||||
}
|
161
plugins/GroupPrivateMessage/newgroupmessage.php
Normal file
161
plugins/GroupPrivateMessage/newgroupmessage.php
Normal file
|
@ -0,0 +1,161 @@
|
|||
<?php
|
||||
/**
|
||||
* StatusNet - the distributed open-source microblogging tool
|
||||
* Copyright (C) 2011, StatusNet, Inc.
|
||||
*
|
||||
* Action for adding a new group message
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* 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 Cache
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @copyright 2011 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET')) {
|
||||
// This check helps protect against security problems;
|
||||
// your code file can't be executed directly from the web.
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Action for adding a new group message
|
||||
*
|
||||
* @category Action
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @copyright 2011 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
class NewgroupmessageAction extends Action
|
||||
{
|
||||
var $group;
|
||||
var $user;
|
||||
var $text;
|
||||
|
||||
/**
|
||||
* For initializing members of the class.
|
||||
*
|
||||
* @param array $argarray misc. arguments
|
||||
*
|
||||
* @return boolean true
|
||||
*/
|
||||
|
||||
function prepare($argarray)
|
||||
{
|
||||
parent::prepare($argarray);
|
||||
|
||||
$this->user = common_current_user();
|
||||
|
||||
if (empty($this->user)) {
|
||||
throw new ClientException(_('Must be logged in.'), 403);
|
||||
}
|
||||
|
||||
if (!$this->user->hasRight(Right::NEWMESSAGE)) {
|
||||
throw new Exception(sprintf(_('User %s not allowed to send private messages.'),
|
||||
$this->user->nickname));
|
||||
}
|
||||
|
||||
$nicknameArg = $this->trimmed('nickname');
|
||||
|
||||
$nickname = common_canonical_nickname($nicknameArg);
|
||||
|
||||
if ($nickname != $nicknameArg) {
|
||||
$url = common_local_url('newgroupmessage', array('nickname' => $nickname));
|
||||
common_redirect($url, 301);
|
||||
return false;
|
||||
}
|
||||
|
||||
$localGroup = Local_group::staticGet('nickname', $nickname);
|
||||
|
||||
if (empty($localGroup)) {
|
||||
throw new ClientException(_('No such group'), 404);
|
||||
}
|
||||
|
||||
$this->group = User_group::staticGet('id', $localGroup->group_id);
|
||||
|
||||
if (empty($this->group)) {
|
||||
throw new ClientException(_('No such group'), 404);
|
||||
}
|
||||
|
||||
// This throws an exception on error
|
||||
|
||||
Group_privacy_settings::ensurePost($this->user, $this->group);
|
||||
|
||||
// If we're posted to, check session token and get text
|
||||
|
||||
if ($this->isPost()) {
|
||||
$this->checkSessionToken();
|
||||
$this->text = $this->trimmed('content');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler method
|
||||
*
|
||||
* @param array $argarray is ignored since it's now passed in in prepare()
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
||||
function handle($argarray=null)
|
||||
{
|
||||
if ($this->isPost()) {
|
||||
$this->sendNewMessage();
|
||||
} else {
|
||||
$this->showPage();
|
||||
}
|
||||
}
|
||||
|
||||
function showNoticeForm()
|
||||
{
|
||||
$form = new GroupMessageForm($this, $this->group);
|
||||
$form->show();
|
||||
}
|
||||
|
||||
function sendNewMessage()
|
||||
{
|
||||
$gm = Group_message::send($this->user, $this->group, $this->text);
|
||||
|
||||
if ($this->boolean('ajax')) {
|
||||
$this->startHTML('text/xml;charset=utf-8');
|
||||
$this->elementStart('head');
|
||||
$this->element('title', null, _('Message sent'));
|
||||
$this->elementEnd('head');
|
||||
$this->elementStart('body');
|
||||
$this->element('p',
|
||||
array('id' => 'command_result'),
|
||||
sprintf(_('Direct message to %s sent.'),
|
||||
$this->group->nickname));
|
||||
$this->elementEnd('body');
|
||||
$this->elementEnd('html');
|
||||
} else {
|
||||
common_redirect($gm->url, 303);
|
||||
}
|
||||
}
|
||||
|
||||
function title()
|
||||
{
|
||||
return sprintf(_('New message to group %s'), $this->group->nickname);
|
||||
}
|
||||
}
|
188
plugins/GroupPrivateMessage/showgroupmessage.php
Normal file
188
plugins/GroupPrivateMessage/showgroupmessage.php
Normal file
|
@ -0,0 +1,188 @@
|
|||
<?php
|
||||
/**
|
||||
* StatusNet - the distributed open-source microblogging tool
|
||||
* Copyright (C) 2011, StatusNet, Inc.
|
||||
*
|
||||
* Show a single group message
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* 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 GroupPrivateMessage
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @copyright 2011 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET')) {
|
||||
// This check helps protect against security problems;
|
||||
// your code file can't be executed directly from the web.
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a single private group message
|
||||
*
|
||||
* @category GroupPrivateMessage
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @copyright 2011 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
class ShowgroupmessageAction extends Action
|
||||
{
|
||||
var $gm;
|
||||
var $group;
|
||||
var $sender;
|
||||
var $user;
|
||||
|
||||
/**
|
||||
* For initializing members of the class.
|
||||
*
|
||||
* @param array $argarray misc. arguments
|
||||
*
|
||||
* @return boolean true
|
||||
*/
|
||||
|
||||
function prepare($argarray)
|
||||
{
|
||||
parent::prepare($argarray);
|
||||
|
||||
$this->user = common_current_user();
|
||||
|
||||
if (empty($this->user)) {
|
||||
throw new ClientException(_('Only logged-in users can view private messages.'),
|
||||
403);
|
||||
}
|
||||
|
||||
$id = $this->trimmed('id');
|
||||
|
||||
$this->gm = Group_message::staticGet('id', $id);
|
||||
|
||||
if (empty($this->gm)) {
|
||||
throw new ClientException(_('No such message'), 404);
|
||||
}
|
||||
|
||||
$this->group = User_group::staticGet('id', $this->gm->to_group);
|
||||
|
||||
if (empty($this->group)) {
|
||||
throw new ServerException(_('Group not found.'));
|
||||
}
|
||||
|
||||
if (!$this->user->isMember($this->group)) {
|
||||
throw new ClientException(_('Cannot read message.'), 403);
|
||||
}
|
||||
|
||||
$this->sender = Profile::staticGet('id', $this->gm->from_profile);
|
||||
|
||||
if (empty($this->sender)) {
|
||||
throw new ServerException(_('No sender found.'));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler method
|
||||
*
|
||||
* @param array $argarray is ignored since it's now passed in in prepare()
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
||||
function handle($argarray=null)
|
||||
{
|
||||
$this->showPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Title of the page
|
||||
*/
|
||||
|
||||
function title()
|
||||
{
|
||||
return sprintf(_('Message from %1$s to group %2$s on %3$s'),
|
||||
$this->sender->nickname,
|
||||
$this->group->nickname,
|
||||
common_exact_date($this->gm->created));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the content area.
|
||||
*/
|
||||
|
||||
function showContent()
|
||||
{
|
||||
$this->elementStart('ul', 'notices messages');
|
||||
$gmli = new GroupMessageListItem($this, $this->gm);
|
||||
$gmli->show();
|
||||
$this->elementEnd('ul');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if read only.
|
||||
*
|
||||
* MAY override
|
||||
*
|
||||
* @param array $args other arguments
|
||||
*
|
||||
* @return boolean is read only action?
|
||||
*/
|
||||
|
||||
function isReadOnly($args)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return last modified, if applicable.
|
||||
*
|
||||
* MAY override
|
||||
*
|
||||
* @return string last modified http header
|
||||
*/
|
||||
function lastModified()
|
||||
{
|
||||
return max(strtotime($this->group->modified),
|
||||
strtotime($this->sender->modified),
|
||||
strtotime($this->gm->modified));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return etag, if applicable.
|
||||
*
|
||||
* MAY override
|
||||
*
|
||||
* @return string etag http header
|
||||
*/
|
||||
function etag()
|
||||
{
|
||||
$avatar = $this->sender->getAvatar(AVATAR_STREAM_SIZE);
|
||||
|
||||
$avtime = ($avatar) ? strtotime($avatar->modified) : 0;
|
||||
|
||||
return 'W/"' . implode(':', array($this->arg('action'),
|
||||
common_user_cache_hash(),
|
||||
common_language(),
|
||||
$this->gm->id,
|
||||
strtotime($this->sender->modified),
|
||||
strtotime($this->group->modified),
|
||||
$avtime)) . '"';
|
||||
}
|
||||
}
|
|
@ -40,8 +40,8 @@ class InfiniteScrollPlugin extends Plugin
|
|||
|
||||
function onEndShowScripts($action)
|
||||
{
|
||||
$action->script('plugins/InfiniteScroll/jquery.infinitescroll.js');
|
||||
$action->script('plugins/InfiniteScroll/infinitescroll.js');
|
||||
$action->script($this->path('jquery.infinitescroll.js'));
|
||||
$action->script($this->path('infinitescroll.js'));
|
||||
}
|
||||
|
||||
function onPluginVersion(&$versions)
|
||||
|
|
|
@ -51,7 +51,7 @@ class LinkPreviewPlugin extends Plugin
|
|||
{
|
||||
$user = common_current_user();
|
||||
if ($user && common_config('attachments', 'process_links')) {
|
||||
$action->script('plugins/LinkPreview/linkpreview.min.js');
|
||||
$action->script($this->path('linkpreview.min.js'));
|
||||
$data = json_encode(array(
|
||||
'api' => common_local_url('oembedproxy'),
|
||||
'width' => common_config('attachments', 'thumbwidth'),
|
||||
|
|
|
@ -129,7 +129,7 @@ class MapstractionPlugin extends Plugin
|
|||
break;
|
||||
case 'openlayers':
|
||||
// Use our included stripped & minified OpenLayers.
|
||||
$action->script(common_path('plugins/Mapstraction/OpenLayers/OpenLayers.js'));
|
||||
$action->script($this->path('OpenLayers/OpenLayers.js'));
|
||||
break;
|
||||
case 'yahoo':
|
||||
$action->script(sprintf('http://api.maps.yahoo.com/ajaxymap?v=3.8&appid=%s',
|
||||
|
@ -145,13 +145,13 @@ class MapstractionPlugin extends Plugin
|
|||
//
|
||||
// Note that OpenLayers.js needs to be separate, or it won't
|
||||
// be able to find its UI images and styles.
|
||||
$action->script(common_path('plugins/Mapstraction/usermap-mxn-openlayers.min.js'));
|
||||
$action->script($this->path('usermap-mxn-openlayers.min.js'));
|
||||
} else {
|
||||
$action->script(sprintf('%s?(%s)',
|
||||
common_path('plugins/Mapstraction/js/mxn.js'),
|
||||
$this->path('js/mxn.js'),
|
||||
$this->provider));
|
||||
|
||||
$action->script(common_path('plugins/Mapstraction/usermap.js'));
|
||||
$action->script($this->path('usermap.js'));
|
||||
}
|
||||
|
||||
$action->inlineScript(sprintf('var _provider = "%s";', $this->provider));
|
||||
|
|
|
@ -89,7 +89,7 @@ class MeteorPlugin extends RealtimePlugin
|
|||
{
|
||||
$scripts = parent::_getScripts();
|
||||
$scripts[] = 'http://'.$this->webserver.(($this->webport == 80) ? '':':'.$this->webport).'/meteor.js';
|
||||
$scripts[] = common_path('plugins/Meteor/meteorupdater.min.js');
|
||||
$scripts[] = $this->path('meteorupdater.min.js');
|
||||
return $scripts;
|
||||
}
|
||||
|
||||
|
|
|
@ -241,13 +241,13 @@ class MobileProfilePlugin extends WAP20Plugin
|
|||
if (file_exists(Theme::file('css/mp-screen.css'))) {
|
||||
$action->cssLink('css/mp-screen.css', null, 'screen');
|
||||
} else {
|
||||
$action->cssLink('plugins/MobileProfile/mp-screen.css',null,'screen');
|
||||
$action->cssLink($this->path('mp-screen.css'),null,'screen');
|
||||
}
|
||||
|
||||
if (file_exists(Theme::file('css/mp-handheld.css'))) {
|
||||
$action->cssLink('css/mp-handheld.css', null, 'handheld');
|
||||
} else {
|
||||
$action->cssLink('plugins/MobileProfile/mp-handheld.css',null,'handheld');
|
||||
$action->cssLink($this->path('mp-handheld.css'),null,'handheld');
|
||||
}
|
||||
|
||||
// Allow other plugins to load their styles.
|
||||
|
|
|
@ -51,13 +51,13 @@ class ModPlusPlugin extends Plugin
|
|||
{
|
||||
$user = common_current_user();
|
||||
if ($user) {
|
||||
$action->script('plugins/ModPlus/modplus.js');
|
||||
$action->script($this->path('modplus.js'));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function onEndShowStatusNetStyles($action) {
|
||||
$action->cssLink('plugins/ModPlus/modplus.css');
|
||||
$action->cssLink($this->path('modplus.css'));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -326,12 +326,12 @@ class NewMenuPlugin extends Plugin
|
|||
|
||||
function onEndShowStyles($action)
|
||||
{
|
||||
if (($this->showCSS ||
|
||||
if (($this->loadCSS ||
|
||||
in_array(common_config('site', 'theme'),
|
||||
array('default', 'identica', 'h4ck3r'))) &&
|
||||
($action instanceof AccountSettingsAction ||
|
||||
$action instanceof ConnectSettingsAction)) {
|
||||
$action->cssLink(common_path('plugins/NewMenu/newmenu.css'));
|
||||
$action->cssLink($this->path('newmenu.css'));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -41,7 +41,12 @@ border-radius-topright:7px;
|
|||
border-radius-topright:0;
|
||||
-moz-border-radius-topright:0;
|
||||
-webkit-border-top-right-radius:0;
|
||||
min-height: 360px;
|
||||
}
|
||||
body[id$=settings] #aside_primary {
|
||||
display:none;
|
||||
float: right;
|
||||
width: 17.25%;
|
||||
margin-right: 10.45%;
|
||||
margin-top: 6px;
|
||||
min-height: 0px;
|
||||
}
|
||||
|
|
|
@ -419,12 +419,12 @@ class OStatusPlugin extends Plugin
|
|||
}
|
||||
|
||||
function onEndShowStatusNetStyles($action) {
|
||||
$action->cssLink('plugins/OStatus/theme/base/css/ostatus.css');
|
||||
$action->cssLink($this->path('theme/base/css/ostatus.css'));
|
||||
return true;
|
||||
}
|
||||
|
||||
function onEndShowStatusNetScripts($action) {
|
||||
$action->script('plugins/OStatus/js/ostatus.js');
|
||||
$action->script($this->path('js/ostatus.js'));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1112,7 +1112,8 @@ class Ostatus_profile extends Memcached_DataObject
|
|||
return $url;
|
||||
}
|
||||
}
|
||||
return common_path('plugins/OStatus/images/96px-Feed-icon.svg.png');
|
||||
|
||||
return Plugin::staticPath('OStatus', 'images/96px-Feed-icon.svg.png');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1781,12 +1782,14 @@ class Ostatus_profile extends Memcached_DataObject
|
|||
$oprofile = Ostatus_profile::ensureWebfinger($rest);
|
||||
break;
|
||||
default:
|
||||
common_log(LOG_WARNING,
|
||||
"Unrecognized URI protocol for profile: $protocol ($uri)");
|
||||
throw new ServerException("Unrecognized URI protocol for profile: $protocol ($uri)");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
throw new ServerException("No URI protocol for profile: ($uri)");
|
||||
}
|
||||
}
|
||||
|
||||
return $oprofile;
|
||||
}
|
||||
|
||||
|
|
|
@ -116,8 +116,9 @@ class RealtimePlugin extends Plugin
|
|||
|
||||
function onEndShowStatusNetStyles($action)
|
||||
{
|
||||
$action->cssLink('plugins/Realtime/realtimeupdate.css',
|
||||
null, 'screen, projection, tv');
|
||||
$action->cssLink(Plugin::staticPath('Realtime', 'realtimeupdate.css'),
|
||||
null,
|
||||
'screen, projection, tv');
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -322,7 +323,7 @@ class RealtimePlugin extends Plugin
|
|||
|
||||
function _getScripts()
|
||||
{
|
||||
return array('plugins/Realtime/realtimeupdate.min.js');
|
||||
return array(Plugin::staticPath('Realtime', 'realtimeupdate.min.js'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -33,7 +33,7 @@ class ShareNoticePlugin extends Plugin
|
|||
);
|
||||
|
||||
function onEndShowStatusNetStyles($action) {
|
||||
$action->cssLink('plugins/ShareNotice/css/sharenotice.css');
|
||||
$action->cssLink($this->path('css/sharenotice.css'));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ class TabFocusPlugin extends Plugin
|
|||
|
||||
function onEndShowScripts($action)
|
||||
{
|
||||
$action->script('plugins/TabFocus/tabfocus.js');
|
||||
$action->script($this->path('tabfocus.js'));
|
||||
}
|
||||
|
||||
function onPluginVersion(&$versions)
|
||||
|
|
|
@ -39,6 +39,10 @@ if (!defined('STATUSNET')) {
|
|||
*
|
||||
* Converts the notice form in browser to a rich-text editor.
|
||||
*
|
||||
* FIXME: this plugin DOES NOT load its static files from the configured
|
||||
* plugin server if one exists. There are cross-server permissions errors
|
||||
* if you try to do that (something about window.tinymce).
|
||||
*
|
||||
* @category WYSIWYG
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
|
|
|
@ -45,7 +45,7 @@ function add_twitter_user($twitter_id, $screen_name)
|
|||
$fuser = new Foreign_user();
|
||||
|
||||
$fuser->nickname = $screen_name;
|
||||
$fuser->uri = 'http://twitter.com/#!/' . $screen_name;
|
||||
$fuser->uri = 'http://twitter.com/' . $screen_name;
|
||||
$fuser->id = $twitter_id;
|
||||
$fuser->service = TWITTER_SERVICE;
|
||||
$fuser->created = common_sql_now();
|
||||
|
@ -173,6 +173,7 @@ function broadcast_twitter($notice)
|
|||
|
||||
// Don't bother with basic auth, since it's no longer allowed
|
||||
if (!empty($flink) && TwitterOAuthClient::isPackedToken($flink->credentials)) {
|
||||
if (is_twitter_bound($notice, $flink)) {
|
||||
if (!empty($notice->repeat_of) && is_twitter_notice($notice->repeat_of)) {
|
||||
$retweet = retweet_notice($flink, Notice::staticGet('id', $notice->repeat_of));
|
||||
if (is_object($retweet)) {
|
||||
|
@ -183,10 +184,11 @@ function broadcast_twitter($notice)
|
|||
// this or can discard safely.
|
||||
return $retweet;
|
||||
}
|
||||
} else if (is_twitter_bound($notice, $flink)) {
|
||||
} else {
|
||||
return broadcast_oauth($notice, $flink);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -264,7 +264,7 @@ class TwitterImport
|
|||
function ensureProfile($user)
|
||||
{
|
||||
// check to see if there's already a profile for this user
|
||||
$profileurl = 'http://twitter.com/#!/' . $user->screen_name;
|
||||
$profileurl = 'http://twitter.com/' . $user->screen_name;
|
||||
$profile = $this->getProfileByUrl($user->screen_name, $profileurl);
|
||||
|
||||
if (!empty($profile)) {
|
||||
|
|
|
@ -83,7 +83,7 @@ class TwitterloginAction extends Action
|
|||
$this->elementStart('a', array('href' => common_local_url('twitterauthorization',
|
||||
null,
|
||||
array('signin' => true))));
|
||||
$this->element('img', array('src' => common_path('plugins/TwitterBridge/Sign-in-with-Twitter-lighter.png'),
|
||||
$this->element('img', array('src' => Plugin::staticPath('TwitterBridge', 'Sign-in-with-Twitter-lighter.png'),
|
||||
'alt' => _m('Sign in with Twitter')));
|
||||
$this->elementEnd('a');
|
||||
}
|
||||
|
|
|
@ -176,12 +176,12 @@ class YammeradminpanelAction extends AdminPanelAction
|
|||
function showStylesheets()
|
||||
{
|
||||
parent::showStylesheets();
|
||||
$this->cssLink('plugins/YammerImport/css/admin.css', null, 'screen, projection, tv');
|
||||
$this->cssLink(Plugin::staticPath('YammerImport', 'css/admin.css'), null, 'screen, projection, tv');
|
||||
}
|
||||
|
||||
function showScripts()
|
||||
{
|
||||
parent::showScripts();
|
||||
$this->script('plugins/YammerImport/js/yammer-admin.js');
|
||||
$this->script(Plugin::staticPath('YammerImport', 'js/yammer-admin.js'));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user