Merge branch '0.7.x' into 0.8.x

This commit is contained in:
Evan Prodromou 2009-02-28 21:11:16 -08:00
commit fc44c9a7f4
19 changed files with 405 additions and 177 deletions

View File

@ -88,3 +88,9 @@ StartShowLocalNavBlock: Showing the local nav menu
EndShowLocalNavBlock: At the end of the local nav menu
- $action: the current action
StartShowHTML: Chance to set document content type, charset, language, DOCTYPE and html element properties
- $action: the current action
EndShowHTML: Showing after the html element
- $action: the current action

View File

@ -324,13 +324,12 @@ class AvatarsettingsAction extends AccountSettingsAction
return;
}
// If image is not being cropped assume pos & dimentions of original
// If image is not being cropped assume pos & dimensions of original.
$dest_x = $this->arg('avatar_crop_x') ? $this->arg('avatar_crop_x'):0;
$dest_y = $this->arg('avatar_crop_y') ? $this->arg('avatar_crop_y'):0;
$dest_w = $this->arg('avatar_crop_w') ? $this->arg('avatar_crop_w'):$filedata['width'];
$dest_h = $this->arg('avatar_crop_h') ? $this->arg('avatar_crop_h'):$filedata['height'];
$size = min($dest_w, $dest_h);
$size = ($size > MAX_ORIGINAL) ? MAX_ORIGINAL:$size;
$size = min($dest_w, $dest_h, MAX_ORIGINAL);
$user = common_current_user();
$profile = $user->getProfile();
@ -343,6 +342,7 @@ class AvatarsettingsAction extends AccountSettingsAction
unset($_SESSION['FILEDATA']);
$this->mode = 'upload';
$this->showForm(_('Avatar updated.'), true);
common_broadcast_profile($profile);
} else {
$this->showForm(_('Failed updating avatar.'));
}

View File

@ -283,7 +283,7 @@ class FinishremotesubscribeAction extends Action
$fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
$result = $fetcher->post($req->get_normalized_http_url(),
$req->to_postdata(),
array('User-Agent' => 'Laconica/' . LACONICA_VERSION));
array('User-Agent: Laconica/' . LACONICA_VERSION));
common_debug('got result: "'.print_r($result,true).'"', __FILE__);

View File

@ -253,7 +253,7 @@ class NewnoticeAction extends Action
}
}
$notice_form = new NoticeForm($this, $content);
$notice_form = new NoticeForm($this, '', $content);
$notice_form->show();
}

View File

@ -113,123 +113,58 @@ class NoticesearchAction extends SearchAction
} else {
$cnt = $notice->find();
}
if ($cnt > 0) {
$terms = preg_split('/[\s,]+/', $q);
$this->elementStart('ul', array('class' => 'notices'));
for ($i = 0; $i < min($cnt, NOTICES_PER_PAGE); $i++) {
if ($notice->fetch()) {
$this->showNotice($notice, $terms);
} else {
// shouldn't happen!
break;
}
}
$this->elementEnd('ul');
} else {
if ($cnt === 0) {
$this->element('p', 'error', _('No results'));
}
$this->pagination($page > 1, $cnt > NOTICES_PER_PAGE,
$page, 'noticesearch', array('q' => $q));
}
/**
* Show notice
*
* @param class $notice notice
* @param array $terms terms to highlight
*
* @return void
*
* @todo refactor and combine with StreamAction::showNotice()
*/
function showNotice($notice, $terms)
{
$profile = $notice->getProfile();
if (!$profile) {
common_log_db_error($notice, 'SELECT', __FILE__);
$this->serverError(_('Notice without matching profile'));
return;
}
// XXX: RDFa
$this->elementStart('li', array('class' => 'hentry notice',
'id' => 'notice-' . $notice->id));
$terms = preg_split('/[\s,]+/', $q);
$nl = new SearchNoticeList($notice, $this, $terms);
$this->elementStart('div', 'entry-title');
$this->elementStart('span', 'vcard author');
$avatar = $profile->getAvatar(AVATAR_STREAM_SIZE);
$this->elementStart('a', array('href' => $profile->profileurl,
'class' => 'url'));
$this->element('img', array('src' => ($avatar) ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_STREAM_SIZE),
'class' => 'avatar photo',
'width' => AVATAR_STREAM_SIZE,
'height' => AVATAR_STREAM_SIZE,
'alt' =>
($profile->fullname) ? $profile->fullname :
$profile->nickname));
$this->element('span', 'nickname fn', $profile->nickname);
$this->elementEnd('a');
$this->elementEnd('span');
$cnt = $nl->show();
$this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
$this->page, 'noticesearch', array('q' => $q));
}
function isReadOnly()
{
return true;
}
}
class SearchNoticeList extends NoticeList {
function __construct($notice, $out=null, $terms)
{
parent::__construct($notice, $out);
$this->terms = $terms;
}
function newListItem($notice)
{
return new SearchNoticeListItem($notice, $this->out, $this->terms);
}
}
class SearchNoticeListItem extends NoticeListItem {
function __construct($notice, $out=null, $terms)
{
parent::__construct($notice, $out);
$this->terms = $terms;
}
function showContent()
{
// FIXME: URL, image, video, audio
$this->elementStart('p', array('class' => 'entry-content'));
if ($notice->rendered) {
$this->raw($this->highlight($notice->rendered, $terms));
$this->out->elementStart('p', array('class' => 'entry-content'));
if ($this->notice->rendered) {
$this->out->raw($this->highlight($this->notice->rendered, $this->terms));
} else {
// XXX: may be some uncooked notices in the DB,
// we cook them right now. This should probably disappear in future
// versions (>> 0.4.x)
$this->raw($this->highlight(common_render_content($notice->content, $notice), $terms));
$this->out->raw($this->highlight(common_render_content($this->notice->content, $this->notice), $this->terms));
}
$this->elementEnd('p');
$this->elementEnd('div');
$this->out->elementEnd('p');
$noticeurl = common_local_url('shownotice', array('notice' => $notice->id));
$this->elementStart('div', 'entry-content');
$this->elementStart('dl', 'timestamp');
$this->element('dt', null, _('Published'));
$this->elementStart('dd', null);
$this->elementStart('a', array('rel' => 'bookmark',
'href' => $noticeurl));
$dt = common_date_iso8601($notice->created);
$this->element('abbr', array('class' => 'published',
'title' => $dt),
common_date_string($notice->created));
$this->elementEnd('a');
$this->elementEnd('dd');
$this->elementEnd('dl');
if ($notice->reply_to) {
$replyurl = common_local_url('shownotice',
array('notice' => $this->notice->reply_to));
$this->elementStart('dl', 'response');
$this->element('dt', null, _('To'));
$this->elementStart('dd');
$this->element('a', array('href' => $replyurl,
'rel' => 'in-reply-to'),
_('in reply to'));
$this->elementEnd('dd');
$this->elementEnd('dl');
}
$this->elementEnd('div');
$this->elementStart('div', 'notice-options');
$reply_url = common_local_url('newnotice',
array('replyto' => $profile->nickname));
$this->elementStart('dl', 'notice_reply');
$this->element('dt', null, _('Reply to this notice'));
$this->elementStart('dd');
$this->elementStart('a', array('href' => $reply_url,
'title' => _('Reply to this notice')));
$this->text(_('Reply'));
$this->element('span', 'notice_id', $notice->id);
$this->elementEnd('a');
$this->elementEnd('dd');
$this->elementEnd('dl');
$this->elementEnd('div');
$this->elementEnd('li');
}
/**
@ -242,7 +177,7 @@ class NoticesearchAction extends SearchAction
*/
function highlight($text, $terms)
{
/* Highligh serach terms */
/* Highligh search terms */
$pattern = '/('.implode('|', array_map('htmlspecialchars', $terms)).')/i';
$result = preg_replace($pattern, '<strong>\\1</strong>', $text);
@ -253,10 +188,5 @@ class NoticesearchAction extends SearchAction
} while ($count);
return $result;
}
function isReadOnly()
{
return true;
}
}

View File

@ -321,8 +321,7 @@ class RemotesubscribeAction extends Action
$result = $fetcher->post($req->get_normalized_http_url(),
$req->to_postdata(),
array('User-Agent' => 'Laconica/' . LACONICA_VERSION));
array('User-Agent: Laconica/' . LACONICA_VERSION));
if ($result->status != 200) {
return null;
}

View File

@ -162,7 +162,13 @@ class UpdateprofileAction extends Action
if ($avatar) {
$temp_filename = tempnam(sys_get_temp_dir(), 'listenee_avatar');
copy($avatar, $temp_filename);
if (!$profile->setOriginal($temp_filename)) {
$imagefile = new ImageFile($profile->id, $temp_filename);
$filename = Avatar::filename($profile->id,
image_type_to_extension($imagefile->type),
null,
common_timestamp());
rename($temp_filename, Avatar::path($filename));
if (!$profile->setOriginal($filename)) {
$this->serverError(_('Could not save avatar info'), 500);
return false;
}

View File

@ -105,7 +105,7 @@ class UserauthorizationAction extends Action
$this->elementStart('div', 'profile');
if ($avatar) {
$this->element('img', array('src' => $avatar,
'class' => 'avatar profile',
'class' => 'avatar',
'width' => AVATAR_PROFILE_SIZE,
'height' => AVATAR_PROFILE_SIZE,
'alt' => $nickname));

View File

@ -45,6 +45,7 @@ class Profile_tag extends Memcached_DataObject
static function setTags($tagger, $tagged, $newtags) {
$newtags = array_unique($newtags);
$oldtags = Profile_tag::getTags($tagger, $tagged);
# Delete stuff that's old that not in new

View File

@ -173,7 +173,7 @@ create table token (
tok char(32) not null comment 'identifying value',
secret char(32) not null comment 'secret value',
type tinyint not null default 0 comment 'request or access',
state tinyint default 0 comment 'for requests; 0 = initial, 1 = authorized, 2 = used',
state tinyint default 0 comment 'for requests, 0 = initial, 1 = authorized, 2 = used',
created datetime not null comment 'date this record was created',
modified timestamp comment 'date this record was modified',
@ -346,7 +346,7 @@ 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 tinyint default 1 comment 'reason it is in the inbox; 1=subscription',
source tinyint default 1 comment 'reason it is in the inbox, 1=subscription',
constraint primary key (user_id, notice_id),
index notice_inbox_notice_id_idx (notice_id)

213
install.php Normal file
View File

@ -0,0 +1,213 @@
<?
define('INSTALLDIR', dirname(__FILE__));
function main()
{
if (!checkPrereqs())
{
return;
}
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
handlePost();
} else {
showForm();
}
}
function checkPrereqs()
{
if (file_exists(INSTALLDIR.'/config.php')) {
?><p class="error">Config file &quot;config.php&quot; already exists.</p>
<?
return false;
}
if (version_compare(PHP_VERSION, '5.0.0', '<')) {
?><p class="error">Require PHP version 5 or greater.</p><?
return false;
}
$reqs = array('gd', 'mysql', 'curl',
'xmlwriter', 'mbstring',
'gettext');
foreach ($reqs as $req) {
if (!checkExtension($req)) {
?><p class="error">Cannot load required extension &quot;<?= $req ?>&quot;.</p><?
return false;
}
}
if (!is_writable(INSTALLDIR)) {
?><p class="error">Cannot write config file to &quot;<?= INSTALLDIR ?>&quot;.</p>
<p>On your server, try this command:</p>
<blockquote>chmod a+w <?= INSTALLDIR ?></blockquote>
<?
return false;
}
if (!is_writable(INSTALLDIR.'/avatar/')) {
?><p class="error">Cannot write avatar directory &quot;<?= INSTALLDIR ?>/avatar/&quot;.</p>
<p>On your server, try this command:</p>
<blockquote>chmod a+w <?= INSTALLDIR ?>/avatar/</blockquote>
<?
return false;
}
return true;
}
function checkExtension($name)
{
if (!extension_loaded($name)) {
if (!dl($name.'.so')) {
return false;
}
}
return true;
}
function showForm()
{
?>
<p>Enter your database connection information below to initialize the database.</p>
<form method='post' action='install.php'>
<fieldset>
<ul class='form_data'>
<li>
<label for='sitename'>Site name</label>
<input type='text' id='sitename' name='sitename' />
<p>The name of your site</p>
</li>
<li>
<li>
<label for='host'>Hostname</label>
<input type='text' id='host' name='host' />
<p>Database hostname</p>
</li>
<li>
<label for='host'>Database</label>
<input type='text' id='database' name='database' />
<p>Database name</p>
</li>
<li>
<label for='username'>Username</label>
<input type='text' id='username' name='username' />
<p>Database username</p>
</li>
<li>
<label for='password'>Password</label>
<input type='password' id='password' name='password' />
<p>Database password</p>
</li>
</ul>
<input type='submit' name='submit' value='Submit'>
</fieldset>
</form>
<?
}
function updateStatus($status, $error=false)
{
?>
<li>
<?
print $status;
?>
</li>
<?
}
function handlePost()
{
?>
<ul>
<?
$host = $_POST['host'];
$database = $_POST['database'];
$username = $_POST['username'];
$password = $_POST['password'];
$sitename = $_POST['sitename'];
updateStatus("Starting installation...");
updateStatus("Checking database...");
$conn = mysql_connect($host, $username, $password);
if (!$conn) {
updateStatus("Can't connect to server '$host' as '$username'.", true);
showForm();
return;
}
updateStatus("Changing to database...");
$res = mysql_select_db($database, $conn);
if (!$res) {
updateStatus("Can't change to database.", true);
showForm();
return;
}
updateStatus("Running database script...");
$res = runDbScript(INSTALLDIR.'/db/laconica.sql', $conn);
if ($res === false) {
updateStatus("Can't run database script.", true);
showForm();
return;
}
updateStatus("Writing config file...");
$sqlUrl = "mysqli://$username:$password@$host/$database";
$res = writeConf($sitename, $sqlUrl);
if (!$res) {
updateStatus("Can't write config file.", true);
showForm();
return;
}
updateStatus("Done!");
?>
</ul>
<?
}
function writeConf($sitename, $sqlUrl)
{
$res = file_put_contents(INSTALLDIR.'/config.php',
"<?\n".
"\$config['site']['name'] = \"$sitename\";\n\n".
"\$config['db']['database'] = \"$sqlUrl\";\n\n");
return $res;
}
function runDbScript($filename, $conn)
{
$sql = trim(file_get_contents($filename));
$stmts = explode(';', $sql);
foreach ($stmts as $stmt) {
$stmt = trim($stmt);
if (!mb_strlen($stmt)) {
continue;
}
$res = mysql_query($stmt, $conn);
if ($res === false) {
return $res;
}
}
return true;
}
?>
<html>
<head>
<title>Install Laconica</title>
<link rel="stylesheet" type="text/css" href="theme/base/css/display.css?version=0.7.1" media="screen, projection, tv"/>
<link rel="stylesheet" type="text/css" href="theme/base/css/modal.css?version=0.7.1" media="screen, projection, tv"/>
<link rel="stylesheet" type="text/css" href="theme/default/css/display.css?version=0.7.1" media="screen, projection, tv"/>
</head>
<body>
<div id="wrap">
<div id="core">
<div id="content">
<h1>Install Laconica</h1>
<? main() ?>
</div>
</div>
</div>
</body>
</html>

View File

@ -93,7 +93,10 @@ class Action extends HTMLOutputter // lawsuit
*/
function showPage()
{
if (Event::handle('StartShowHTML', array($this))) {
$this->startHTML();
Event::handle('EndShowHTML', array($this));
}
$this->showHead();
$this->showBody();
$this->endHTML();
@ -173,6 +176,10 @@ class Action extends HTMLOutputter // lawsuit
// TODO: "handheld" CSS for other mobile devices
'media' => 'only screen and (max-device-width: 480px)')); // Mobile WebKit
}
$this->element('link', array('rel' => 'stylesheet',
'type' => 'text/css',
'href' => theme_path('css/print.css', 'base') . '?version=' . LACONICA_VERSION,
'media' => 'print'));
Event::handle('EndShowLaconicaStyles', array($this));
}
if (Event::handle('StartShowUAStyles', array($this))) {

View File

@ -178,12 +178,25 @@ if (strlen($_path) > 0) {
$_config_files[] = INSTALLDIR.'/config.php';
$_have_a_config = false;
foreach ($_config_files as $_config_file) {
if (file_exists($_config_file)) {
include_once($_config_file);
$_have_a_config = true;
}
}
// XXX: Throw a conniption if database not installed
// Fixup for laconica.ini
$_db_name = substr($config['db']['database'], strrpos($config['db']['database'], '/') + 1);
if ($_db_name != 'laconica' && !array_key_exists('ini_'.$_db_name, $config['db'])) {
$config['db']['ini_'.$_db_name] = INSTALLDIR.'/classes/laconica.ini';
}
// XXX: how many of these could be auto-loaded on use?
require_once('Validate.php');

View File

@ -94,7 +94,7 @@ function get_nice_language_list()
* Get a list of all languages that are enabled in the default config
*
* This should ONLY be called when setting up the default config in common.php.
* Any other attempt to get a list of lanugages should instead call
* Any other attempt to get a list of languages should instead call
* common_config('site','languages')
*
* @return array mapping of language codes to language info
@ -110,13 +110,16 @@ function get_all_languages() {
'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'),
'fi' => array('q' => 0.5, 'lang' => 'fi', 'name' => 'Finnish', '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'),
'he' => array('q' => 0.5, 'lang' => 'he_IL', 'name' => 'Hebrew', 'direction' => 'rtl'),
'it' => array('q' => 0.9, 'lang' => 'it_IT', 'name' => 'Italian', 'direction' => 'ltr'),
'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'),
'nb' => array('q' => 0.1, 'lang' => 'nb_NO', 'name' => 'Norwegian (Bokmål)', 'direction' => 'ltr'),
'no' => array('q' => 0.1, 'lang' => 'nb_NO', 'name' => 'Norwegian (Bokmål)', 'direction' => 'ltr'),
'nn' => array('q' => 0.1, 'lang' => 'nn_NO', 'name' => 'Norwegian (Nynorsk)', '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'),

View File

@ -206,7 +206,7 @@ function omb_post_notice_keys($notice, $postnoticeurl, $tk, $secret)
$result = $fetcher->post($req->get_normalized_http_url(),
$req->to_postdata(),
array('User-Agent' => 'Laconica/' . LACONICA_VERSION));
array('User-Agent: Laconica/' . LACONICA_VERSION));
common_debug('Got HTTP result "'.print_r($result,true).'"', __FILE__);
@ -291,7 +291,7 @@ function omb_update_profile($profile, $remote_profile, $subscription)
common_debug('postdata = '.$req->to_postdata(), __FILE__);
$result = $fetcher->post($req->get_normalized_http_url(),
$req->to_postdata(),
array('User-Agent' => 'Laconica/' . LACONICA_VERSION));
array('User-Agent: Laconica/' . LACONICA_VERSION));
common_debug('Got HTTP result "'.print_r($result,true).'"', __FILE__);

View File

@ -116,6 +116,12 @@ class Router
$m->connect('main/openid', array('action' => 'openidlogin'));
$m->connect('main/remote', array('action' => 'remotesubscribe'));
$m->connect('main/remote?nickname=:nickname', array('action' => 'remotesubscribe'), array('nickname' => '[A-Za-z0-9_-]+'));
foreach (array('requesttoken', 'accesstoken', 'userauthorization',
'postnotice', 'updateprofile', 'finishremotesubscribe') as $action) {
$m->connect('index.php?action=' . $action, array('action' => $action));
}
// settings
@ -128,6 +134,7 @@ class Router
foreach (array('group', 'people', 'notice') as $s) {
$m->connect('search/'.$s, array('action' => $s.'search'));
$m->connect('search/'.$s.'?q=:q', array('action' => $s.'search'), array('q' => '.+'));
}
$m->connect('search/notice/rss', array('action' => 'noticesearchrss'));
@ -135,6 +142,9 @@ class Router
// notice
$m->connect('notice/new', array('action' => 'newnotice'));
$m->connect('notice/new?replyto=:replyto',
array('action' => 'newnotice'),
array('replyto' => '[A-Za-z0-9_-]+'));
$m->connect('notice/:notice',
array('action' => 'shownotice'),
array('notice' => '[0-9]+'));
@ -150,6 +160,7 @@ class Router
array('id' => '[0-9]+'));
$m->connect('message/new', array('action' => 'newmessage'));
$m->connect('message/new?to=:to', array('action' => 'newmessage'), array('to' => '[A-Za-z0-9_-]'));
$m->connect('message/:message',
array('action' => 'showmessage'),
array('message' => '[0-9]+'));

View File

@ -456,6 +456,9 @@ function common_replace_urls_callback($text, $callback) {
if (!in_array($url_parts[2], $tlds)) continue;
// Make sure we didn't capture a hash tag
if (strpos($url, '#') === 0) continue;
// Put the url back the way we found it.
$url = (mb_strpos($orig_url, htmlspecialchars($url)) === FALSE) ? $url:htmlspecialchars($url);

View File

@ -297,7 +297,7 @@ padding:4px 11px;
border-width:1px;
border-style:solid;
border-bottom:0;
text-shadow: 4px 4px 4px #ddd;
text-shadow: 2px 2px 2px #ddd;
font-weight:bold;
}
#site_nav_local_views .nav {

36
theme/base/css/print.css Normal file
View File

@ -0,0 +1,36 @@
/** theme: base
*
* @package Laconica
* @author Sarven Capadisli <csarven@controlyourself.ca>
* @copyright 2009 Control Yourself, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://laconi.ca/
*/
a:after { background-color:#fff; }
a:not([href^="#"]):after { content:" ( "attr(href)" ) "; }
img { border:none; }
p { orphans: 2; widows: 1; }
#site_nav_global_primary,
#site_nav_local_views,
#form_notice,
.pagination,
#site_nav_global_secondary,
.entity_actions,
.notice-options,
#aside_primary,
.form_subcription_edit .submit {
display:none;
}
.timestamp dt, .timestamp dd,
.device dt, .device dd {
display:inline;
}
.profiles li,
.notices li {
margin-bottom:18px;
}