summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvan Prodromou <evan@controlyourself.ca>2009-02-28 21:11:16 -0800
committerEvan Prodromou <evan@controlyourself.ca>2009-02-28 21:11:16 -0800
commitfc44c9a7f456a146a20884c56676baf7343dd923 (patch)
treebcc572d966709bfa19277eb48c3f2f831d49d8ba
parent02ba71b0f186b406071a97c3267603d4863a4b21 (diff)
parent458c03786735bd3e3b6619b2d20538bd55acd0c6 (diff)
Merge branch '0.7.x' into 0.8.x
-rw-r--r--EVENTS.txt6
-rw-r--r--actions/avatarsettings.php6
-rw-r--r--actions/finishremotesubscribe.php2
-rw-r--r--actions/newnotice.php2
-rw-r--r--actions/noticesearch.php146
-rw-r--r--actions/remotesubscribe.php3
-rw-r--r--actions/updateprofile.php8
-rw-r--r--actions/userauthorization.php2
-rw-r--r--classes/Profile_tag.php43
-rw-r--r--db/laconica.sql4
-rw-r--r--install.php213
-rw-r--r--lib/action.php9
-rw-r--r--lib/common.php13
-rw-r--r--lib/language.php61
-rw-r--r--lib/omb.php4
-rw-r--r--lib/router.php13
-rw-r--r--lib/util.php3
-rw-r--r--theme/base/css/display.css2
-rw-r--r--theme/base/css/print.css36
19 files changed, 402 insertions, 174 deletions
diff --git a/EVENTS.txt b/EVENTS.txt
index 37e2203d5..ed461ee9f 100644
--- a/EVENTS.txt
+++ b/EVENTS.txt
@@ -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
+
diff --git a/actions/avatarsettings.php b/actions/avatarsettings.php
index f38a44a24..6545d9489 100644
--- a/actions/avatarsettings.php
+++ b/actions/avatarsettings.php
@@ -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.'));
}
diff --git a/actions/finishremotesubscribe.php b/actions/finishremotesubscribe.php
index 76db887de..acfacbdc1 100644
--- a/actions/finishremotesubscribe.php
+++ b/actions/finishremotesubscribe.php
@@ -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__);
diff --git a/actions/newnotice.php b/actions/newnotice.php
index 9face9644..9f44d2516 100644
--- a/actions/newnotice.php
+++ b/actions/newnotice.php
@@ -253,7 +253,7 @@ class NewnoticeAction extends Action
}
}
- $notice_form = new NoticeForm($this, $content);
+ $notice_form = new NoticeForm($this, '', $content);
$notice_form->show();
}
diff --git a/actions/noticesearch.php b/actions/noticesearch.php
index dc58d7528..83e59dd9a 100644
--- a/actions/noticesearch.php
+++ b/actions/noticesearch.php
@@ -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'));
+ return;
}
+ $terms = preg_split('/[\s,]+/', $q);
+ $nl = new SearchNoticeList($notice, $this, $terms);
+
+ $cnt = $nl->show();
- $this->pagination($page > 1, $cnt > NOTICES_PER_PAGE,
- $page, 'noticesearch', array('q' => $q));
+ $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
+ $this->page, 'noticesearch', array('q' => $q));
}
+ function isReadOnly()
+ {
+ return true;
+ }
+}
- /**
- * 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)
+class SearchNoticeList extends NoticeList {
+ function __construct($notice, $out=null, $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));
+ parent::__construct($notice, $out);
+ $this->terms = $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');
+ 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;
- }
}
diff --git a/actions/remotesubscribe.php b/actions/remotesubscribe.php
index f727a63b8..7ea7acd6d 100644
--- a/actions/remotesubscribe.php
+++ b/actions/remotesubscribe.php
@@ -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;
}
diff --git a/actions/updateprofile.php b/actions/updateprofile.php
index 898c53543..4751a04ff 100644
--- a/actions/updateprofile.php
+++ b/actions/updateprofile.php
@@ -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;
}
diff --git a/actions/userauthorization.php b/actions/userauthorization.php
index ed17ceec9..0dc1841d4 100644
--- a/actions/userauthorization.php
+++ b/actions/userauthorization.php
@@ -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));
diff --git a/classes/Profile_tag.php b/classes/Profile_tag.php
index cb60cbaec..0a1ad9cd6 100644
--- a/classes/Profile_tag.php
+++ b/classes/Profile_tag.php
@@ -4,7 +4,7 @@
*/
require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
-class Profile_tag extends Memcached_DataObject
+class Profile_tag extends Memcached_DataObject
{
###START_AUTOCODE
/* the code below is auto generated do not remove the above tag */
@@ -23,45 +23,46 @@ class Profile_tag extends Memcached_DataObject
###END_AUTOCODE
static function getTags($tagger, $tagged) {
-
+
$tags = array();
# XXX: store this in memcached
-
+
$profile_tag = new Profile_tag();
$profile_tag->tagger = $tagger;
$profile_tag->tagged = $tagged;
-
+
$profile_tag->find();
-
+
while ($profile_tag->fetch()) {
$tags[] = $profile_tag->tag;
}
-
+
$profile_tag->free();
-
+
return $tags;
}
-
+
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
-
+
$to_delete = array_diff($oldtags, $newtags);
-
+
# Insert stuff that's in new and not in old
-
+
$to_insert = array_diff($newtags, $oldtags);
-
+
$profile_tag = new Profile_tag();
-
+
$profile_tag->tagger = $tagger;
$profile_tag->tagged = $tagged;
-
+
$profile_tag->query('BEGIN');
-
+
foreach ($to_delete as $deltag) {
$profile_tag->tag = $deltag;
$result = $profile_tag->delete();
@@ -70,7 +71,7 @@ class Profile_tag extends Memcached_DataObject
return false;
}
}
-
+
foreach ($to_insert as $instag) {
$profile_tag->tag = $instag;
$result = $profile_tag->insert();
@@ -79,12 +80,12 @@ class Profile_tag extends Memcached_DataObject
return false;
}
}
-
+
$profile_tag->query('COMMIT');
-
+
return true;
}
-
+
# Return profiles with a given tag
static function getTagged($tagger, $tag) {
$profile = new Profile();
diff --git a/db/laconica.sql b/db/laconica.sql
index 6cacdba44..c7c1826d2 100644
--- a/db/laconica.sql
+++ b/db/laconica.sql
@@ -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)
diff --git a/install.php b/install.php
new file mode 100644
index 000000000..18fc362b6
--- /dev/null
+++ b/install.php
@@ -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> \ No newline at end of file
diff --git a/lib/action.php b/lib/action.php
index f19a047cf..2ba877c9c 100644
--- a/lib/action.php
+++ b/lib/action.php
@@ -93,7 +93,10 @@ class Action extends HTMLOutputter // lawsuit
*/
function showPage()
{
- $this->startHTML();
+ 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))) {
diff --git a/lib/common.php b/lib/common.php
index 4fc749ca0..0fff3af2e 100644
--- a/lib/common.php
+++ b/lib/common.php
@@ -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');
diff --git a/lib/language.php b/lib/language.php
index a73b73f28..79e9030ae 100644
--- a/lib/language.php
+++ b/lib/language.php
@@ -94,40 +94,43 @@ 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
*/
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'),
+ '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'),
+ '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' => '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 (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'),
+ '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'),
);
}
diff --git a/lib/omb.php b/lib/omb.php
index 29e14c75f..befcf4666 100644
--- a/lib/omb.php
+++ b/lib/omb.php
@@ -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__);
diff --git a/lib/router.php b/lib/router.php
index 4c036e7b2..1d1a5ba4c 100644
--- a/lib/router.php
+++ b/lib/router.php
@@ -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]+'));
@@ -416,4 +427,4 @@ class Router
return $this->m->generate($args, $params, $fragment);
}
-} \ No newline at end of file
+}
diff --git a/lib/util.php b/lib/util.php
index 5345a08bb..18e4f310c 100644
--- a/lib/util.php
+++ b/lib/util.php
@@ -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);
diff --git a/theme/base/css/display.css b/theme/base/css/display.css
index be124f433..c741ed4cb 100644
--- a/theme/base/css/display.css
+++ b/theme/base/css/display.css
@@ -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 {
diff --git a/theme/base/css/print.css b/theme/base/css/print.css
new file mode 100644
index 000000000..2da3e5e44
--- /dev/null
+++ b/theme/base/css/print.css
@@ -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;
+}