Merge branch 'master' of ssh://zach@dev.controlyourself.ca/var/www/trunk

This commit is contained in:
Zach Copley 2009-01-23 01:20:00 -08:00
commit 0ef33663ce
103 changed files with 51019 additions and 25133 deletions

View File

@ -71,4 +71,9 @@ class AccesstokenAction extends Action
$this->serverError($e->getMessage());
}
}
function isReadOnly()
{
return true;
}
}

View File

@ -123,5 +123,10 @@ class AllrssAction extends Rss10Action
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
return $avatar ? $avatar->url : null;
}
function isReadOnly()
{
return true;
}
}

View File

@ -72,9 +72,13 @@ class ApiAction extends Action
}
} else {
# Look for the user in the session
if (common_logged_in()) {
$this->user = common_current_user();
# Caller might give us a username even if not required
if (isset($_SERVER['PHP_AUTH_USER'])) {
$user = User::staticGet('nickname', $_SERVER['PHP_AUTH_USER']);
if ($user) {
$this->user = $user;
}
# Twitter doesn't throw an error if the user isn't found
}
$this->process_command();

View File

@ -97,5 +97,10 @@ class AvatarbynicknameAction extends Action
}
common_redirect($url, 302);
}
function isReadOnly()
{
return true;
}
}

View File

@ -107,4 +107,9 @@ class DocAction extends Action
{
return ucfirst($this->title);
}
function isReadOnly()
{
return true;
}
}

View File

@ -202,7 +202,7 @@ class FacebookhomeAction extends FacebookAction
$this->elementEnd('p');
$this->elementStart('form', array('method' => 'post',
'action' => "$app_url/index.php",
'action' => "index.php",
'id' => 'facebook-skip-permissions'));
$this->elementStart('ul', array('id' => 'fb-permissions-list'));

View File

@ -114,5 +114,10 @@ class FavoritesrssAction extends Rss10Action
{
return null;
}
function isReadOnly()
{
return true;
}
}

516
actions/grouplogo.php Normal file
View File

@ -0,0 +1,516 @@
<?php
/**
* Laconica, the distributed open-source microblogging tool
*
* Upload an avatar
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Settings
* @package Laconica
* @author Evan Prodromou <evan@controlyourself.ca>
* @author Zach Copley <zach@controlyourself.ca>
* @copyright 2008-2009 Control Yourself, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://laconi.ca/
*/
if (!defined('LACONICA')) {
exit(1);
}
require_once INSTALLDIR.'/lib/accountsettingsaction.php';
/**
* Upload an avatar
*
* We use jCrop plugin for jQuery to crop the image after upload.
*
* @category Settings
* @package Laconica
* @author Evan Prodromou <evan@controlyourself.ca>
* @author Zach Copley <zach@controlyourself.ca>
* @author Sarven Capadisli <csarven@controlyourself.ca>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://laconi.ca/
*/
class GrouplogoAction extends Action
{
var $mode = null;
var $imagefile = null;
var $filename = null;
/**
* Prepare to run
*/
function prepare($args)
{
parent::prepare($args);
if (!common_config('inboxes','enabled')) {
$this->serverError(_('Inboxes must be enabled for groups to work'));
return false;
}
if (!common_logged_in()) {
$this->clientError(_('You must be logged in to create a group.'));
return false;
}
$nickname_arg = $this->trimmed('nickname');
$nickname = common_canonical_nickname($nickname_arg);
// Permanent redirect on non-canonical nickname
if ($nickname_arg != $nickname) {
$args = array('nickname' => $nickname);
common_redirect(common_local_url('editgroup', $args), 301);
return false;
}
if (!$nickname) {
$this->clientError(_('No nickname'), 404);
return false;
}
$groupid = $this->trimmed('groupid');
if ($groupid) {
$this->group = User_group::staticGet('id', $groupid);
} else {
$this->group = User_group::staticGet('nickname', $nickname);
}
if (!$this->group) {
$this->clientError(_('No such group'), 404);
return false;
}
$cur = common_current_user();
if (!$cur->isAdmin($this->group)) {
$this->clientError(_('You must be an admin to edit the group'), 403);
return false;
}
return true;
}
function handle($args)
{
parent::handle($args);
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$this->handlePost();
} else {
$this->showForm();
}
}
function showForm($msg = null)
{
$this->msg = $msg;
$this->showPage();
}
/**
* Title of the page
*
* @return string Title of the page
*/
function title()
{
return _('Group logo');
}
/**
* Instructions for use
*
* @return instructions for use
*/
function getInstructions()
{
return _('You can upload a logo image for your group.');
}
/**
* Content area of the page
*
* Shows a form for uploading an avatar.
*
* @return void
*/
function showContent()
{
if ($this->mode == 'crop') {
$this->showCropForm();
} else {
$this->showUploadForm();
}
}
function showUploadForm()
{
$user = common_current_user();
$profile = $user->getProfile();
if (!$profile) {
common_log_db_error($user, 'SELECT', __FILE__);
$this->serverError(_('User without matching profile'));
return;
}
$original = $this->group->original_logo;
$this->elementStart('form', array('enctype' => 'multipart/form-data',
'method' => 'post',
'id' => 'form_settings_logo',
'class' => 'form_settings',
'action' =>
common_local_url('grouplogo',
array('nickname' => $this->group->nickname))));
$this->elementStart('fieldset');
$this->element('legend', null, _('Group logo'));
$this->hidden('token', common_session_token());
$this->elementStart('ul', 'form_data');
if ($original) {
$this->elementStart('li', array('id' => 'avatar_original',
'class' => 'avatar_view'));
$this->element('h2', null, _("Original"));
$this->elementStart('div', array('id'=>'avatar_original_view'));
$this->element('img', array('src' => $this->group->original_logo,
'alt' => $this->group->nickname));
$this->elementEnd('div');
$this->elementEnd('li');
}
if ($this->group->homepage_logo) {
$this->elementStart('li', array('id' => 'avatar_preview',
'class' => 'avatar_view'));
$this->element('h2', null, _("Preview"));
$this->elementStart('div', array('id'=>'avatar_preview_view'));
$this->element('img', array('src' => $this->group->homepage_logo,
'width' => AVATAR_PROFILE_SIZE,
'height' => AVATAR_PROFILE_SIZE,
'alt' => $this->group->nickname));
$this->elementEnd('div');
$this->elementEnd('li');
}
$this->elementStart('li', array ('id' => 'settings_attach'));
$this->element('input', array('name' => 'avatarfile',
'type' => 'file',
'id' => 'avatarfile'));
$this->element('input', array('name' => 'MAX_FILE_SIZE',
'type' => 'hidden',
'id' => 'MAX_FILE_SIZE',
'value' => MAX_AVATAR_SIZE));
$this->elementEnd('li');
$this->elementEnd('ul');
$this->elementStart('ul', 'form_actions');
$this->elementStart('li');
$this->submit('upload', _('Upload'));
$this->elementEnd('li');
$this->elementEnd('ul');
$this->elementEnd('fieldset');
$this->elementEnd('form');
}
function showCropForm()
{
$this->elementStart('form', array('method' => 'post',
'id' => 'form_settings_avatar',
'class' => 'form_settings',
'action' =>
common_local_url('grouplogo',
array('nickname' => $this->group->nickname))));
$this->elementStart('fieldset');
$this->element('legend', null, _('Avatar settings'));
$this->hidden('token', common_session_token());
$this->elementStart('ul', 'form_data');
$this->elementStart('li',
array('id' => 'avatar_original',
'class' => 'avatar_view'));
$this->element('h2', null, _("Original"));
$this->elementStart('div', array('id'=>'avatar_original_view'));
$this->element('img', array('src' => common_avatar_url($this->filedata['filename']),
'width' => $this->filedata['width'],
'height' => $this->filedata['height'],
'alt' => $this->group->nickname));
$this->elementEnd('div');
$this->elementEnd('li');
$this->elementStart('li',
array('id' => 'avatar_preview',
'class' => 'avatar_view'));
$this->element('h2', null, _("Preview"));
$this->elementStart('div', array('id'=>'avatar_preview_view'));
$this->element('img', array('src' => common_avatar_url($this->filedata['filename']),
'width' => AVATAR_PROFILE_SIZE,
'height' => AVATAR_PROFILE_SIZE,
'alt' => $this->group->nickname));
$this->elementEnd('div');
foreach (array('avatar_crop_x', 'avatar_crop_y',
'avatar_crop_w', 'avatar_crop_h') as $crop_info) {
$this->element('input', array('name' => $crop_info,
'type' => 'hidden',
'id' => $crop_info));
}
$this->submit('crop', _('Crop'));
$this->elementEnd('li');
$this->elementEnd('ul');
$this->elementEnd('fieldset');
$this->elementEnd('form');
}
/**
* Handle a post
*
* We mux on the button name to figure out what the user actually wanted.
*
* @return void
*/
function handlePost()
{
// CSRF protection
$token = $this->trimmed('token');
if (!$token || $token != common_session_token()) {
$this->show_form(_('There was a problem with your session token. '.
'Try again, please.'));
return;
}
if ($this->arg('upload')) {
$this->uploadAvatar();
} else if ($this->arg('crop')) {
$this->cropAvatar();
} else {
$this->showForm(_('Unexpected form submission.'));
}
}
/**
* Handle an image upload
*
* Does all the magic for handling an image upload, and crops the
* image by default.
*
* @return void
*/
function uploadAvatar()
{
try {
$imagefile = ImageFile::fromUpload('avatarfile');
} catch (Exception $e) {
$this->showForm($e->getMessage());
return;
}
$filename = common_avatar_filename($this->group->id,
image_type_to_extension($imagefile->type),
null,
'group-temp-'.common_timestamp());
$filepath = common_avatar_path($filename);
move_uploaded_file($imagefile->filename, $filepath);
$filedata = array('filename' => $filename,
'filepath' => $filepath,
'width' => $imagefile->width,
'height' => $imagefile->height,
'type' => $imagefile->type);
$_SESSION['FILEDATA'] = $filedata;
$this->filedata = $filedata;
$this->mode = 'crop';
$this->showForm(_('Pick a square area of the image to be your avatar'),
true);
}
/**
* Handle the results of jcrop.
*
* @return void
*/
function cropAvatar()
{
$user = common_current_user();
$profile = $user->getProfile();
$x = $this->arg('avatar_crop_x');
$y = $this->arg('avatar_crop_y');
$w = $this->arg('avatar_crop_w');
$h = $this->arg('avatar_crop_h');
$filedata = $_SESSION['FILEDATA'];
if (!$filedata) {
$this->serverError(_('Lost our file data.'));
return;
}
$filepath = common_avatar_path($filedata['filename']);
if (!file_exists($filepath)) {
$this->serverError(_('Lost our file.'));
return;
}
switch ($filedata['type']) {
case IMAGETYPE_GIF:
$image_src = imagecreatefromgif($filepath);
break;
case IMAGETYPE_JPEG:
$image_src = imagecreatefromjpeg($filepath);
break;
case IMAGETYPE_PNG:
$image_src = imagecreatefrompng($filepath);
break;
default:
$this->serverError(_('Unknown file type'));
return;
}
common_debug("W = $w, H = $h, X = $x, Y = $y");
$image_dest = imagecreatetruecolor($w, $h);
$background = imagecolorallocate($image_dest, 0, 0, 0);
ImageColorTransparent($image_dest, $background);
imagealphablending($image_dest, false);
imagecopyresized($image_dest, $image_src, 0, 0, $x, $y, $w, $h, $w, $h);
$cur = common_current_user();
$filename = common_avatar_filename($this->group->id,
image_type_to_extension($imagefile->type),
null,
'group-'.common_timestamp());
$filepath = common_avatar_path($filename);
switch ($filedata['type']) {
case IMAGETYPE_GIF:
imagegif($image_dest, $filepath);
break;
case IMAGETYPE_JPEG:
imagejpeg($image_dest, $filepath);
break;
case IMAGETYPE_PNG:
imagepng($image_dest, $filepath);
break;
default:
$this->serverError(_('Unknown file type'));
return;
}
if ($this->group->setOriginal($filename, $filedata['type'])) {
@unlink(common_avatar_path($filedata['filename']));
unset($_SESSION['FILEDATA']);
$this->mode = 'upload';
$this->showForm(_('Logo updated.'), true);
} else {
$this->showForm(_('Failed updating logo.'));
}
}
function showPageNotice()
{
if ($this->msg) {
$this->element('div', ($this->success) ? 'success' : 'error',
$this->msg);
} else {
$inst = $this->getInstructions();
$output = common_markup_to_html($inst);
$this->elementStart('div', 'instructions');
$this->raw($output);
$this->elementEnd('div');
}
}
/**
* Add the jCrop stylesheet
*
* @return void
*/
function showStylesheets()
{
parent::showStylesheets();
$jcropStyle =
common_path('theme/base/css/jquery.Jcrop.css?version='.LACONICA_VERSION);
$this->element('link', array('rel' => 'stylesheet',
'type' => 'text/css',
'href' => $jcropStyle,
'media' => 'screen, projection, tv'));
}
/**
* Add the jCrop scripts
*
* @return void
*/
function showScripts()
{
parent::showScripts();
$jcropPack = common_path('js/jcrop/jquery.Jcrop.pack.js');
$jcropGo = common_path('js/jcrop/jquery.Jcrop.go.js');
$this->element('script', array('type' => 'text/javascript',
'src' => $jcropPack));
$this->element('script', array('type' => 'text/javascript',
'src' => $jcropGo));
}
function showLocalNav()
{
$nav = new GroupNav($this, $this->group);
$nav->show();
}
function isReadOnly()
{
return true;
}
}

View File

@ -51,6 +51,11 @@ class GroupsAction extends Action
var $page = null;
var $profile = null;
function isReadOnly()
{
return true;
}
function title()
{
if ($this->page == 1) {

View File

@ -103,5 +103,10 @@ class GroupSearchResults extends GroupList
{
return preg_replace($this->pattern, '<strong>\\1</strong>', htmlspecialchars($text));
}
function isReadOnly()
{
return true;
}
}

View File

@ -54,7 +54,7 @@ class LogoutAction extends Action
*/
function isReadOnly()
{
return true;
return false;
}
/**

View File

@ -73,4 +73,9 @@ class MicrosummaryAction extends Action
print $user->nickname . ': ' . $notice->content;
}
function isReadOnly()
{
return true;
}
}

View File

@ -222,5 +222,10 @@ class NoticesearchAction extends SearchAction
} while ($count);
return $result;
}
function isReadOnly()
{
return true;
}
}

View File

@ -95,4 +95,9 @@ class NoticesearchrssAction extends Rss10Action
{
return null;
}
function isReadOnly()
{
return true;
}
}

View File

@ -122,5 +122,10 @@ class NudgeAction extends Action
// XXX: notify by SMS
}
}
function isReadOnly()
{
return true;
}
}

View File

@ -90,16 +90,27 @@ class OpenidloginAction extends Action
function showContent() {
$formaction = common_local_url('openidlogin');
$this->elementStart('form', array('method' => 'post',
'id' => 'openidlogin',
'id' => 'form_openid_login',
'class' => 'form_settings',
'action' => $formaction));
$this->elementStart('fieldset');
$this->element('legend', null, _('OpenID login'));
$this->hidden('token', common_session_token());
$this->elementStart('ul', 'form_data');
$this->elementStart('li');
$this->input('openid_url', _('OpenID URL'),
$this->openid_url,
_('Your OpenID URL'));
$this->elementEnd('li');
$this->elementStart('li', array('id' => 'settings_rememberme'));
$this->checkbox('rememberme', _('Remember me'), false,
_('Automatically login in the future; ' .
'not for shared computers!'));
$this->elementEnd('li');
$this->elementEnd('ul');
$this->submit('submit', _('Login'));
$this->elementEnd('fieldset');
$this->elementEnd('form');
}

View File

@ -83,5 +83,10 @@ class OpensearchAction extends Action
$this->elementEnd('OpenSearchDescription');
common_end_xml();
}
function isReadOnly()
{
return true;
}
}

View File

@ -109,5 +109,10 @@ class PeopleSearchResults extends ProfileList
{
return preg_replace($this->pattern, '<strong>\\1</strong>', htmlspecialchars($text));
}
function isReadOnly()
{
return true;
}
}

View File

@ -56,6 +56,11 @@ class PublicAction extends Action
var $page = null;
function isReadOnly()
{
return true;
}
/**
* Read and validate arguments
*

View File

@ -102,5 +102,10 @@ class PublicrssAction extends Rss10Action
{
// nop
}
function isReadOnly()
{
return true;
}
}

View File

@ -191,4 +191,9 @@ class RepliesAction extends Action
$this->page, 'replies',
array('nickname' => $this->user->nickname));
}
function isReadOnly()
{
return true;
}
}

View File

@ -82,4 +82,9 @@ class RepliesrssAction extends Rss10Action
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
return ($avatar) ? $avatar->url : null;
}
function isReadOnly()
{
return true;
}
}

View File

@ -176,4 +176,9 @@ class ShowmessageAction extends MailboxAction
{
return '';
}
function isReadOnly()
{
return true;
}
}

View File

@ -60,6 +60,11 @@ class ShowstreamAction extends Action
var $page = null;
var $profile = null;
function isReadOnly()
{
return true;
}
function title()
{
if ($this->page == 1) {

View File

@ -103,4 +103,9 @@ class SubscribersList extends ProfileList
'nickname' => $this->owner->nickname));
$bf->show();
}
function isReadOnly()
{
return true;
}
}

View File

@ -66,4 +66,9 @@ class TagrssAction extends Rss10Action
'description' => sprintf(_('Microblog tagged with %s'), $tagname));
return $c;
}
function isReadOnly()
{
return true;
}
}

View File

@ -26,14 +26,17 @@ class TwitapiaccountAction extends TwitterapiAction
function verify_credentials($args, $apidata)
{
parent::handle($args);
if (!in_array($apidata['content-type'], array('xml', 'json'))) {
$this->clientError(_('API method not found!'), $code = 404);
return;
if ($apidata['content-type'] == 'xml') {
header('Content-Type: application/xml; charset=utf-8');
print '<authorized>true</authorized>';
} elseif ($apidata['content-type'] == 'json') {
header('Content-Type: application/json; charset=utf-8');
print '{"authorized":true}';
} else {
common_user_error(_('API method not found!'), $code=404);
}
$this->show_extended_profile($apidata['user'], $apidata);
}
function end_session($args, $apidata)

View File

@ -33,6 +33,7 @@ class TwitapiusersAction extends TwitterapiAction
return;
}
$this->auth_user = $apidata['user'];
$user = null;
$email = $this->arg('email');
@ -44,11 +45,76 @@ class TwitapiusersAction extends TwitterapiAction
if (!$user) {
// XXX: Twitter returns a random(?) user instead of throwing and err! -- Zach
$this->clientError(_('Not found.'), 404, $apidata['content-type']);
$this->client_error(_('Not found.'), 404, $apidata['content-type']);
return;
}
$this->show_extended_profile($user, $apidata);
$profile = $user->getProfile();
if (!$profile) {
common_server_error(_('User has no profile.'));
return;
}
$twitter_user = $this->twitter_user_array($profile, true);
// Add in extended user fields offered up by this method
$twitter_user['created_at'] = $this->date_twitter($profile->created);
$subbed = DB_DataObject::factory('subscription');
$subbed->subscriber = $profile->id;
$subbed_count = (int) $subbed->count() - 1;
$notices = DB_DataObject::factory('notice');
$notices->profile_id = $profile->id;
$notice_count = (int) $notices->count();
$twitter_user['friends_count'] = (is_int($subbed_count)) ? $subbed_count : 0;
$twitter_user['statuses_count'] = (is_int($notice_count)) ? $notice_count : 0;
// Other fields Twitter sends...
$twitter_user['profile_background_color'] = '';
$twitter_user['profile_text_color'] = '';
$twitter_user['profile_link_color'] = '';
$twitter_user['profile_sidebar_fill_color'] = '';
$faves = DB_DataObject::factory('fave');
$faves->user_id = $user->id;
$faves_count = (int) $faves->count();
$twitter_user['favourites_count'] = $faves_count;
$timezone = 'UTC';
if ($user->timezone) {
$timezone = $user->timezone;
}
$t = new DateTime;
$t->setTimezone(new DateTimeZone($timezone));
$twitter_user['utc_offset'] = $t->format('Z');
$twitter_user['time_zone'] = $timezone;
if (isset($this->auth_user)) {
if ($this->auth_user->isSubscribed($profile)) {
$twitter_user['following'] = 'true';
} else {
$twitter_user['following'] = 'false';
}
// Not implemented yet
$twitter_user['notifications'] = 'false';
}
if ($apidata['content-type'] == 'xml') {
$this->init_document('xml');
$this->show_twitter_xml_user($twitter_user);
$this->end_document('xml');
} elseif ($apidata['content-type'] == 'json') {
$this->init_document('json');
$this->show_json_objects($twitter_user);
$this->end_document('json');
}
}
}

View File

@ -52,6 +52,11 @@ class UsergroupsAction extends Action
var $page = null;
var $profile = null;
function isReadOnly()
{
return true;
}
function title()
{
if ($this->page == 1) {

View File

@ -94,5 +94,10 @@ class UserrssAction extends Rss10Action
header('X-SUP-ID: '.$url);
parent::initRss($limit);
}
function isReadOnly()
{
return true;
}
}

View File

@ -129,6 +129,8 @@ class Notice extends Memcached_DataObject
$notice->is_local = $is_local;
}
$notice->query('BEGIN');
$notice->reply_to = $reply_to;
$notice->created = common_sql_now();
$notice->content = common_shorten_links($content);
@ -160,6 +162,9 @@ class Notice extends Memcached_DataObject
$notice->saveTags();
$notice->saveGroups();
$notice->addToInboxes();
$notice->query('COMMIT');
# Clear the cache for subscribed users, so they'll update at next request
# XXX: someone clever could prepend instead of clearing the cache
@ -167,7 +172,6 @@ class Notice extends Memcached_DataObject
$notice->blowCaches();
}
$notice->addToInboxes();
return $notice;
}

View File

@ -403,7 +403,7 @@ class User extends Memcached_DataObject
'FROM notice JOIN notice_inbox ON notice.id = notice_inbox.notice_id ' .
'WHERE notice_inbox.user_id = %d ';
# NOTE: we override ORDER
$order = 'ORDER BY notice_inbox.created DESC, notice_inbox.notice_id DESC ';
$order = null;
}
return Notice::getStream(sprintf($qry, $this->id),
'user:notices_with_friends:' . $this->id,

View File

@ -86,4 +86,82 @@ class User_group extends Memcached_DataObject
return $members;
}
function setOriginal($filename, $type)
{
$orig = clone($this);
$this->original_logo = common_avatar_url($filename);
$this->homepage_logo = common_avatar_url($this->scale($filename,
AVATAR_PROFILE_SIZE,
$type));
$this->stream_logo = common_avatar_url($this->scale($filename,
AVATAR_STREAM_SIZE,
$type));
$this->mini_logo = common_avatar_url($this->scale($filename,
AVATAR_MINI_SIZE,
$type));
common_debug(common_log_objstring($this));
return $this->update($orig);
}
function scale($filename, $size, $type)
{
$filepath = common_avatar_path($filename);
if (!file_exists($filepath)) {
$this->serverError(_('Lost our file.'));
return;
}
$info = @getimagesize($filepath);
switch ($type) {
case IMAGETYPE_GIF:
$image_src = imagecreatefromgif($filepath);
break;
case IMAGETYPE_JPEG:
$image_src = imagecreatefromjpeg($filepath);
break;
case IMAGETYPE_PNG:
$image_src = imagecreatefrompng($filepath);
break;
default:
$this->serverError(_('Unknown file type'));
return;
}
$image_dest = imagecreatetruecolor($size, $size);
$background = imagecolorallocate($image_dest, 0, 0, 0);
ImageColorTransparent($image_dest, $background);
imagealphablending($image_dest, false);
imagecopyresized($image_dest, $image_src, 0, 0, $x, $y, $size, $size, $info[0], $info[1]);
$cur = common_current_user();
$outname = common_avatar_filename($cur->id,
image_type_to_extension($type),
null,
common_timestamp());
$outpath = common_avatar_path($outname);
switch ($type) {
case IMAGETYPE_GIF:
imagegif($image_dest, $outpath);
break;
case IMAGETYPE_JPEG:
imagejpeg($image_dest, $outpath);
break;
case IMAGETYPE_PNG:
imagepng($image_dest, $outpath);
break;
default:
$this->serverError(_('Unknown file type'));
return;
}
return $outname;
}
}

View File

@ -49,7 +49,8 @@ create table "user" (
emailnotifysub integer default 1 /* comment 'Notify by email of subscriptions' */,
emailnotifyfav integer default 1 /* comment 'Notify by email of favorites' */,
emailnotifynudge integer default 1 /* comment 'Notify by email of nudges' */,
emailmicroid integer default 1 /* comment 'whether to publish email microid' */,
emailnotifymsg integer default 1 / * comment 'Notify by email of direct messages' */,
emailmicroid integer default 1 /* comment 'whether to publish email microid' */,
language varchar(50) /* comment 'preferred language' */,
timezone varchar(50) /* comment 'timezone' */,
emailpost integer default 1 /* comment 'Post by email' */,
@ -66,6 +67,7 @@ create table "user" (
uri varchar(255) unique /* comment 'universally unique identifier, usually a tag URI' */,
autosubscribe integer default 0 /* comment 'automatically subscribe to users who subscribe to us' */,
urlshorteningservice varchar(50) default 'ur1.ca' /* comment 'service to use for auto-shortening URLs' */,
inboxed integer default 0 /* comment 'has an inbox been created for this user?' */,
created timestamp not null /* comment 'date this record was created' */,
modified timestamp /* comment 'date this record was modified' */
@ -86,6 +88,8 @@ create table remote_profile (
create table subscription (
subscriber integer not null /* comment 'profile listening' */,
subscribed integer not null /* comment 'profile being listened to' */,
jabber integer default 1 /* comment 'deliver jabber messages',
sms integer default 1 comment 'deliver sms messages',
token varchar(255) /* comment 'authorization token' */,
secret varchar(255) /* comment 'token secret' */,
created timestamp not null /* comment 'date this record was created' */,
@ -271,8 +275,6 @@ create table foreign_user (
service int not null /* comment 'foreign key to service' */ references foreign_service(id) ,
uri varchar(255) not null unique /* comment 'identifying URI' */,
nickname varchar(255) /* comment 'nickname on foreign service' */,
user_id int /* comment 'link to user on this system, if exists' */ references "user" (id),
credentials varchar(255) /* comment 'authc credentials, typically a password' */,
created timestamp not null /* comment 'date this record was created' */,
modified timestamp /* comment 'date this record was modified' */,
@ -280,11 +282,25 @@ create table foreign_user (
);
create index foreign_user_user_id_idx on foreign_user using btree(user_id);
create table foreign_link (
user_id int /* comment 'link to user on this system, if exists' */ references "user" (id),
foreign_id int /* comment 'link' */ references foreign_user (id),
service int not null /* comment 'foreign key to service' */ references foreign_service (id),
credentials varchar(255) /* comment 'authc credentials, typically a password',
noticesync int not null default 1 /* comment 'notice synchronisation, bit 1 = sync outgoing, bit 2 = sync incoming, bit 3 = filter local replies' */,
friendsync int not null default 2 /* comment 'friend synchronisation, bit 1 = sync outgoing, bit 2 = sync incoming
created timestamp not null /* comment 'date this record was created' */,
modified timestamp not null /* comment 'date this record was modified' */,
primary key (user_id,foreign_id,service)
);
create index foreign_user_user_id_idx on foreign_link using btree(user_id);
create table foreign_subscription (
service int not null /* comment 'service where relationship happens' */ references foreign_service(id) ,
subscriber int not null /* comment 'subscriber on foreign service' */ ,
subscribed int not null /* comment 'subscribed user' */ ,
created timestamp not null /* comment 'date this record was created' */,
created timestamp not null /* comment 'date this record was created' /,
primary key (service, subscriber, subscribed)
);
@ -320,6 +336,38 @@ create index message_from_idx on message using btree(from_profile);
create index message_to_idx on message using btree(to_profile);
create index message_created_idx on message using btree(created);
create table notice_inbox (
user_id integer not null /* comment 'user receiving the message' */ references user (id),
notice_id integer not null /* comment 'notice received' */ references notice (id),
created datetime not null /* comment 'date the notice was created' */,
source integer default 1 /* comment 'reason it is in the inbox; 1=subscription' */,
primary key (user_id, notice_id)
);
create index notice_inbox_notice_id_idx (notice_id) on notice_inbox using btree(notice_id);
create table profile_tag (
tagger integer not null /* comment 'user making the tag' */ references "user" (id),
tagged integer not null /* comment 'profile tagged' */ references profile (id),
tag varchar(64) not null /* comment 'hash tag associated with this notice' */,
modified timestamp /* comment 'date the tag was added' */,
primary key (tagger, tagged, tag)
);
create index profile_tag_modified_idx on profile_tag using btree(modified);
create index profile_tag_tagger_tag_idx on profile_tag using btree(tagger,tag);
create table profile_block (
blocker integer not null i/* comment 'user making the block' */ references user (id),
blocked integer not null /* comment 'profile that is blocked' */ references profile (id),
modified timestamp /* comment 'date of blocking' */,
primary key (blocker, blocked)
);
/* Textsearch stuff */
create index textsearch_idx on profile using gist(textsearch);

View File

@ -89,13 +89,14 @@ RewriteRule ^peopletag/([a-zA-Z0-9]+)$ index.php?action=peopletag&tag=$1 [L,QSA]
RewriteRule ^featured/?$ index.php?action=featured [L,QSA]
RewriteRule ^favorited/?$ index.php?action=favorited [L,QSA]
RewriteRule ^group/new index.php?action=newgroup [L,QSA]
RewriteRule ^group/([a-zA-Z0-9]+)/edit index.php?action=editgroup&nickname=$1 [L,QSA]
RewriteRule ^group/([a-zA-Z0-9]+)/join index.php?action=joingroup&nickname=$1 [L,QSA]
RewriteRule ^group/([a-zA-Z0-9]+)/leave index.php?action=leavegroup&nickname=$1 [L,QSA]
RewriteRule ^group/([a-zA-Z0-9]+)/members index.php?action=groupmembers&nickname=$1 [L,QSA]
RewriteRule ^group/([0-9]+)/id index.php?action=groupbyid&id=$1 [L,QSA]
RewriteRule ^group/([a-zA-Z0-9]+)/rss index.php?action=grouprss&nickname=$1 [L,QSA]
RewriteRule ^group/new$ index.php?action=newgroup [L,QSA]
RewriteRule ^group/([a-zA-Z0-9]+)/edit$ index.php?action=editgroup&nickname=$1 [L,QSA]
RewriteRule ^group/([a-zA-Z0-9]+)/join$ index.php?action=joingroup&nickname=$1 [L,QSA]
RewriteRule ^group/([a-zA-Z0-9]+)/leave$ index.php?action=leavegroup&nickname=$1 [L,QSA]
RewriteRule ^group/([a-zA-Z0-9]+)/members$ index.php?action=groupmembers&nickname=$1 [L,QSA]
RewriteRule ^group/([a-zA-Z0-9]+)/logo$ index.php?action=grouplogo&nickname=$1 [L,QSA]
RewriteRule ^group/([0-9]+)/id$ index.php?action=groupbyid&id=$1 [L,QSA]
RewriteRule ^group/([a-zA-Z0-9]+)/rss$ index.php?action=grouprss&nickname=$1 [L,QSA]
RewriteRule ^group/([a-zA-Z0-9]+)$ index.php?action=showgroup&nickname=$1 [L,QSA]
RewriteRule ^group$ index.php?action=groups [L,QSA]

View File

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

View File

@ -101,6 +101,12 @@ class GroupNav extends Widget
sprintf(_('Edit %s group properties'), $nickname),
$action_name == 'editgroup',
'nav_group_admin');
$this->out->menuItem(common_local_url('grouplogo', array('nickname' =>
$nickname)),
_('Logo'),
sprintf(_('Add or edit %s logo'), $nickname),
$action_name == 'grouplogo',
'nav_group_logo');
}
$this->out->elementEnd('ul');
}

View File

@ -99,54 +99,35 @@ function get_nice_language_list()
*
* @return array mapping of language codes to language info
*/
function get_all_languages()
{
return
array('en-us' => array('q' => 1, 'lang' => 'en_US',
'name' => 'English (US)', 'direction' => 'ltr'),
'en-nz' => array('q' => 1, 'lang' => 'en_NZ',
'name' => 'English (NZ)', 'direction' => 'ltr'),
'en-gb' => array('q' => 1, 'lang' => 'en_GB',
'name' => 'English (British)', 'direction' => 'ltr'),
'en' => array('q' => 1, 'lang' => 'en',
'name' => 'English', 'direction' => 'ltr'),
'da' => array('q' => 0.1, 'lang' => 'da_DK',
'name' => 'Danish', 'direction' => 'ltr'),
'nl' => array('q' => 1, 'lang' => 'nl_NL',
'name' => 'Dutch', 'direction' => 'ltr'),
'eo' => array('q' => 0.1, 'lang' => 'eo',
'name' => 'Esperanto', 'direction' => 'ltr'),
'fr-fr' => array('q' => 0.9, 'lang' => 'fr_FR',
'name' => 'French', 'direction' => 'ltr'),
'de' => array('q' => 1, 'lang' => 'de_DE',
'name' => 'German', 'direction' => 'ltr'),
'it' => array('q' => 1, 'lang' => 'it_IT',
'name' => 'Italian', 'direction' => 'ltr'),
'ko' => array('q' => 0.1, 'lang' => 'ko',
'name' => 'Korean', 'direction' => 'ltr'),
'nb' => array('q' => 1, 'lang' => 'nb_NO',
'name' => 'Norwegian (bokmal)', 'direction' => 'ltr'),
'pt' => array('q' => 0.2, 'lang' => 'pt',
'name' => 'Portuguese', 'direction' => 'ltr'),
'pt-br' => array('q' => 1, 'lang' => 'pt_BR',
'name' => 'Portuguese Brazil', 'direction' => 'ltr'),
'es' => array('q' => 1, 'lang' => 'es',
'name' => 'Spanish', 'direction' => 'ltr'),
'tr' => array('q' => 1, 'lang' => 'tr_TR',
'name' => 'Turkish', 'direction' => 'ltr'),
'uk' => array('q' => 1, 'lang' => 'uk_UA',
'name' => 'Ukrainian', 'direction' => 'ltr'),
'pl' => array('q' => 1, 'lang' => 'pl_PL',
'name' => 'Polish', 'direction' => 'ltr'),
'mk' => array('q' => 1, 'lang' => 'mk_MK',
'name' => 'Macedonian', 'direction' => 'ltr'),
'jp' => array('q' => 0.1, 'lang' => 'ja_JP',
'name' => 'Japanese', 'direction' => 'ltr'),
'cs' => array('q' => 1, 'lang' => 'cs_CZ',
'name' => 'Czech', 'direction' => 'ltr'),
'ca' => array('q' => 1, 'lang' => 'ca_ES',
'name' => 'Catalan', 'direction' => 'ltr'),
function get_all_languages() {
return array(
'bg' => array('q' => 0.8, 'lang' => 'bg_BG', 'name' => 'Bulgarian', 'direction' => 'ltr'),
'ca' => array('q' => 0.5, 'lang' => 'ca_ES', 'name' => 'Catalan', 'direction' => 'ltr'),
'cs' => array('q' => 0.5, 'lang' => 'cs_CZ', 'name' => 'Czech', 'direction' => 'ltr'),
'de' => array('q' => 0.5, 'lang' => 'de_DE', 'name' => 'German', 'direction' => 'ltr'),
'el' => array('q' => 0.1, 'lang' => 'el', 'name' => 'Greek', 'direction' => 'ltr'),
'en-us' => array('q' => 1, 'lang' => 'en_US', 'name' => 'English (US)', 'direction' => 'ltr'),
'en-gb' => array('q' => 0.3, 'lang' => 'en_GB', 'name' => 'English (British)', 'direction' => 'ltr'),
'en' => array('q' => 1, 'lang' => 'en', 'name' => 'English', 'direction' => 'ltr'),
'es' => array('q' => 0.5, 'lang' => 'es', 'name' => 'Spanish', 'direction' => 'ltr'),
'fr-fr' => array('q' => 0.2, 'lang' => 'fr_FR', 'name' => 'French', 'direction' => 'ltr'),
'he' => array('q' => 0.5, 'lang' => 'he_IL', 'name' => 'Hebrew', 'direction' => 'ltr'),
'it' => array('q' => 0.9, 'lang' => 'it_IT', 'name' => 'Italian', 'direction' => 'rtl'),
'jp' => array('q' => 0.5, 'lang' => 'ja_JP', 'name' => 'Japanese', 'direction' => 'ltr'),
# 'ko' => array('q' => 0, 'lang' => 'ko', 'name' => 'Korean', 'direction' => 'ltr'),
'mk' => array('q' => 0.5, 'lang' => 'mk_MK', 'name' => 'Macedonian', 'direction' => 'ltr'),
'nb' => array('q' => 0.1, 'lang' => 'nb_NO', 'name' => 'Norwegian (bokmal)', 'direction' => 'ltr'),
'nl' => array('q' => 0.5, 'lang' => 'nl_NL', 'name' => 'Dutch', 'direction' => 'ltr'),
'pl' => array('q' => 0.5, 'lang' => 'pl_PL', 'name' => 'Polish', 'direction' => 'ltr'),
# 'pt' => array('q' => 0, 'lang' => 'pt', 'name' => 'Portuguese', 'direction' => 'ltr'),
'pt-br' => array('q' => 0.7, 'lang' => 'pt_BR', 'name' => 'Portuguese Brazil', 'direction' => 'ltr'),
'ru' => array('q' => 0.1, 'lang' => 'ru_RU', 'name' => 'Russian', 'direction' => 'ltr'),
'sv' => array('q' => 0.9, 'lang' => 'sv_SE', 'name' => 'Swedish', 'direction' => 'ltr'),
'te' => array('q' => 0.3, 'lang' => 'te_IN', 'name' => 'Telugu', 'direction' => 'ltr'),
'tr' => array('q' => 0.5, 'lang' => 'tr_TR', 'name' => 'Turkish', 'direction' => 'ltr'),
'uk' => array('q' => 0.7, 'lang' => 'uk_UA', 'name' => 'Ukrainian', 'direction' => 'ltr'),
'vi' => array('q' => 0.7, 'lang' => 'vi_VN', 'name' => 'Vietnamese', 'direction' => 'ltr'),
'zh-cn' => array('q' => 0.9, 'lang' => 'zh_CN', 'name' => 'Chinese (Simplified)', 'direction' => 'ltr'),
'zh-hant' => array('q' => 0.2, 'lang' => 'zh_hant', 'name' => 'Chinese (Taiwanese)', 'direction' => 'ltr'),
);
}

View File

@ -77,7 +77,7 @@ class LoginGroupNav extends Widget
'register' =>
array(_('Register'),
_('Sign up for a new account')),
'openid' =>
'openidlogin' =>
array(_('OpenID'),
_('Login or register with OpenID')));

View File

@ -60,23 +60,22 @@ function subs_subscribe_to($user, $other)
subs_notify($other, $user);
if (common_config('memcached', 'enabled')) {
$cache = new Memcache();
if ($cache->connect(common_config('memcached', 'server'), common_config('memcached', 'port'))) {
$cache = common_memcache();
if ($cache) {
$cache->delete(common_cache_key('user:notices_with_friends:' . $user->id));
}
}
if ($other->autosubscribe && !$other->isSubscribed($user) && !$user->hasBlocked($other)) {
if (!$other->subscribeTo($user)) {
return _('Could not subscribe other to you.');
}
if (common_config('memcached', 'enabled')) {
$cache = new Memcache();
if ($cache->connect(common_config('memcached', 'server'), common_config('memcached', 'port'))) {
$cache = common_memcache();
if ($cache) {
$cache->delete(common_cache_key('user:notices_with_friends:' . $other->id));
}
}
subs_notify($user, $other);
}
@ -134,12 +133,11 @@ function subs_unsubscribe_to($user, $other)
if (!$sub->delete())
return _('Couldn\'t delete subscription.');
if (common_config('memcached', 'enabled')) {
$cache = new Memcache();
if ($cache->connect(common_config('memcached', 'server'), common_config('memcached', 'port'))) {
$cache = common_memcache();
if ($cache) {
$cache->delete(common_cache_key('user:notices_with_friends:' . $user->id));
}
}
return true;
}

View File

@ -900,6 +900,8 @@ function common_fancy_url($action, $args=null)
return common_path('group/'.$args['nickname'].'/rss');
case 'groupmembers':
return common_path('group/'.$args['nickname'].'/members');
case 'grouplogo':
return common_path('group/'.$args['nickname'].'/logo');
case 'usergroups':
return common_path($args['nickname'].'/groups');
case 'groups':

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -78,11 +78,12 @@ foreach ($ids as $id) {
'FROM subscription JOIN notice ON subscription.subscribed = notice.profile_id ' .
'WHERE subscription.subscriber = ' . $user->id . ' ' .
'AND notice.created >= subscription.created ' .
'AND now() - notice.created < ' . (7 * 24 * 3600) . ' ' .
'AND NOT EXISTS (SELECT user_id, notice_id ' .
'FROM notice_inbox ' .
'WHERE user_id = ' . $user->id . ' ' .
'AND notice_id = notice.id)');
'AND notice_id = notice.id) ' .
'ORDER BY notice.created DESC ' .
'LIMIT 0, 1000');
if (is_null($result) || $result === false) {
common_log_db_error($inbox, 'INSERT', __FILE__);
@ -105,5 +106,6 @@ foreach ($ids as $id) {
if ($cache) {
$cache->delete(common_cache_key('user:notices_with_friends:' . $user->id));
$cache->delete(common_cache_key('user:notices_with_friends:' . $user->id . ';last'));
}
}

0
scripts/sitemap.php Normal file → Executable file
View File

59
scripts/uncache_users.php Normal file
View File

@ -0,0 +1,59 @@
#!/usr/bin/env php
<?php
/*
* Laconica - a distributed open-source microblogging tool
* Copyright (C) 2008, Controlez-Vous, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
# Abort if called from a web server
if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
print "This script must be run from the command line\n";
exit();
}
ini_set("max_execution_time", "0");
ini_set("max_input_time", "0");
set_time_limit(0);
mb_internal_encoding('UTF-8');
define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
define('LACONICA', true);
require_once(INSTALLDIR . '/lib/common.php');
$id_file = ($argc > 1) ? $argv[1] : 'ids.txt';
common_log(LOG_INFO, 'Updating user inboxes.');
$ids = file($id_file);
foreach ($ids as $id) {
$user = User::staticGet('id', $id);
if (!$user) {
common_log(LOG_WARNING, 'No such user: ' . $id);
continue;
}
$user->decache();
$memc = common_memcache();
$memc->delete(common_cache_key('user:notices_with_friends:'. $user->id));
$memc->delete(common_cache_key('user:notices_with_friends:'. $user->id . ';last'));
}

0
scripts/update_pot.sh Normal file → Executable file
View File

Some files were not shown because too many files have changed in this diff Show More