--- /dev/null
+<IfModule mod_rewrite.c>
+ RewriteEngine On
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteRule ^(.*)$ dispatcher.php/$1 [QSA,L]
+</IfModule>
--- /dev/null
+Version 1.0
+
+Copyright 2001-2009 The Horde Project (http://www.horde.org/)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1. Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+3. The end-user documentation included with the redistribution, if
+any, must include the following acknowledgment:
+
+ "This product includes software developed by the Horde Project
+ (http://www.horde.org/)."
+
+Alternately, this acknowledgment may appear in the software itself, if
+and wherever such third-party acknowledgments normally appear.
+
+4. The names "Horde", "The Horde Project", and "Jonah" must not be
+used to endorse or promote products derived from this software without
+prior written permission. For written permission, please contact
+core@horde.org.
+
+5. Products derived from this software may not be called "Horde" or
+"Jonah", nor may "Horde" or "Jonah" appear in their name, without
+prior written permission of the Horde Project.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE HORDE PROJECT OR ITS CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+This software consists of voluntary contributions made by many
+individuals on behalf of the Horde Project. For more information on
+the Horde Project, please see <http://www.horde.org/>.
--- /dev/null
+Jonah
+Version 0.1
+
+What is Jonah?
+--------------
+
+Jonah is a portal system for displaying news and other data from
+various sources, written in PHP and utilizing the Horde Application
+Framework.
+
+This software is OSI Certified Open Source Software.
+OSI Certified is a certification mark of the Open Source Initiative.
+
+
+Jonah Documentation
+-------------------
+
+LICENSE - Jonah's license
+README - This file
+docs/CHANGES - A list of changes by release
+docs/CREDITS - Who developed this
+docs/HELP - How you can help
+docs/INSTALL - Installation instructions and notes
+
+
+Now what?
+---------
+
+All installation documentation is located in the docs/ subdirectory
+
+You MUST have the Horde framework installed on your system before you
+install Jonah.
+
+Read the docs/INSTALL file. It contains everything you need to know in
+order to install Jonah on your system. (if it doesn't, it should, so yell at
+us!)
+
+Enjoy,
+The Jonah Team
--- /dev/null
+<?php
+/**
+ * Copyright 2004-2009 The Horde Project (http://www.horde.org/)
+ *
+ * $Horde: jonah/channels/aggregate.php,v 1.19 2009/11/24 04:15:37 chuck Exp $
+ *
+ * See the enclosed file LICENSE for license information (BSD). If you
+ * did not receive this file, see http://cvs.horde.org/co.php/jonah/LICENSE.
+ *
+ * @author Jan Schneider <jan@horde.org>
+ */
+
+@define('JONAH_BASE', dirname(__FILE__) . '/..');
+require_once JONAH_BASE . '/lib/base.php';
+require_once JONAH_BASE . '/lib/News.php';
+require_once 'Horde/Form.php';
+require_once 'Horde/Form/Renderer.php';
+
+function _getLinks($id, $subid, $name, $title)
+{
+ $url = Horde::applicationUrl('channels/aggregate.php');
+ $url = Horde_Util::addParameter($url, 'channel_id', $id);
+ $url = Horde_Util::addParameter($url, 'subchannel_id', $subid);
+ $edit = array('url' => Horde_Util::addParameter($url,'action', 'edit'), 'text' => sprintf(_("Edit channel \"%s\""), $name), 'title' => $title);
+ $delete = array('url' => Horde_Util::addParameter($url, 'action', 'delete'), 'text' => sprintf(_("Remove channel \"%s\""), $name), 'title' => $title);
+ return array($edit, $delete);
+}
+
+$news = Jonah_News::factory();
+$renderer = new Horde_Form_Renderer();
+$vars = Horde_Variables::getDefaultVariables();
+
+/* Set up some variables. */
+$channel_id = $vars->get('channel_id');
+$channel = $news->getChannel($channel_id);
+if (is_a($channel, 'PEAR_Error')) {
+ Horde::fatal($channel, __FILE__, __LINE__);
+}
+$channel_name = $channel['channel_name'];
+$ids = preg_split('/:/', $channel['channel_url'], -1, PREG_SPLIT_NO_EMPTY);
+
+/* Get the vars for channel type. */
+$channel_type = $channel['channel_type'];
+if ($channel_type != JONAH_AGGREGATED_CHANNEL) {
+ $notification->push(_("This is no aggregated channel."), 'horde.error');
+ header('Location: ' . Horde_Util::addParameter(Horde::applicationUrl('channels/edit.php', true), 'channel_id', $channel_id));
+ exit;
+}
+
+/* Check permissions and deny if not allowed. */
+if (!Jonah::checkPermissions(Jonah::typeToPermName($channel_type), Horde_Perms::EDIT, $channel_id)) {
+ $notification->push(_("You are not authorised for this action."), 'horde.warning');
+ Horde::authenticationFailureRedirect();
+}
+
+/* Set up the form. */
+$form = new Horde_Form($vars, sprintf(_("Aggregated channels for channel \"%s\""), $channel_name), 'channel_aggregate');
+$form->setButtons(_("Add"));
+$form->addHidden('', 'channel_id', 'int', false);
+$form->addVariable(_("Channel Name"), 'channel_name', 'text', true);
+$form->addVariable(_("Source URL"), 'channel_url', 'text', true, false, _("The url to use to fetch the stories, for example 'http://www.example.com/stories.rss'"));
+$form->addVariable(_("Link"), 'channel_link', 'text', false);
+$form->addVariable(_("Image"), 'channel_img', 'text', false);
+
+if ($form->validate($vars)) {
+ $subchannel = array('channel_url' => $vars->get('channel_url'),
+ 'channel_name' => $vars->get('channel_name'),
+ 'channel_link' => $vars->get('channel_link'),
+ 'channel_img' => $vars->get('channel_img'),
+ 'channel_type' => JONAH_EXTERNAL_CHANNEL);
+ if ($vars->get('subchannel_id')) {
+ $subchannel['channel_id'] = $vars->get('subchannel_id');
+ }
+ $save = $news->saveChannel($subchannel);
+ if (is_a($save, 'PEAR_Error')) {
+ $notification->push(sprintf(_("There was an error saving the channel: %s"), $save->getMessage()), 'horde.error');
+ } else {
+ $notification->push(sprintf(_("The channel \"%s\" has been saved."), $vars->get('channel_name')), 'horde.success');
+ if (!$vars->get('subchannel_id')) {
+ $ids[] = $save;
+ $channel['channel_url'] = implode(':', $ids);
+ $save = $news->saveChannel($channel);
+ if (is_a($save, 'PEAR_Error')) {
+ $notification->push(sprintf(_("There was an error updating the channel: %s"), $save->getMessage()), 'horde.error');
+ } else {
+ $notification->push(sprintf(_("The channel \"%s\" has been updated."), $channel['channel_name']), 'horde.success');
+ }
+ }
+
+ header('Location: ' . Horde_Util::addParameter(Horde::applicationUrl('channels/aggregate.php', true), 'channel_id', $channel_id));
+ exit;
+ }
+} elseif ($vars->get('action') == 'delete') {
+ $subchannel = $news->getChannel($vars->get('subchannel_id'));
+ $result = $news->deleteChannel($vars->get('subchannel_id'));
+ if (is_a($result, 'PEAR_Error')) {
+ $notification->push(sprintf(_("There was an error removing the channel: %s"), $result->getMessage()), 'horde.error');
+ } else {
+ $notification->push(sprintf(_("The channel \"%s\" has been removed."), $subchannel['channel_name']), 'horde.success');
+ array_splice($ids, array_search($subchannel['channel_id'], $ids), 1);
+ $channel['channel_url'] = implode(':', $ids);
+ $save = $news->saveChannel($channel);
+ if (is_a($save, 'PEAR_Error')) {
+ $notification->push(sprintf(_("There was an error updating the channel: %s"), $save->getMessage()), 'horde.error');
+ } else {
+ $notification->push(sprintf(_("The channel \"%s\" has been updated."), $channel['channel_name']), 'horde.success');
+ }
+ }
+
+ header('Location: ' . Horde_Util::addParameter(Horde::applicationUrl('channels/aggregate.php', true), 'channel_id', $channel_id));
+ exit;
+} elseif ($vars->get('action') == 'edit') {
+ $form->addHidden('', 'subchannel_id', 'int', false);
+ $form->setButtons(_("Update"));
+ $subchannel = $news->getChannel($vars->get('subchannel_id'));
+ $vars->set('channel_name', $subchannel['channel_name']);
+ $vars->set('channel_url', $subchannel['channel_url']);
+ $vars->set('channel_link', $subchannel['channel_link']);
+ $vars->set('channel_img', $subchannel['channel_img']);
+}
+
+foreach ($ids as $id) {
+ $subchannel = $news->getChannel($id);
+ if (is_a($subchannel, 'PEAR_Error')) {
+ $name = $subchannel->getMessage();
+ $url = '';
+ } elseif (empty($subchannel['channel_name'])) {
+ $name = $subchannel['channel_url'];
+ $url = $subchannel['channel_url'];
+ } else {
+ $name = $subchannel['channel_name'];
+ $url = $subchannel['channel_url'];
+ }
+ $form->insertVariableBefore('channel_name', '', 'subchannel' . $id, 'link', false, false, null, array(_getLinks($channel_id, $id, $name, $url)));
+}
+
+$main = Horde_Util::bufferOutput(array($form, 'renderActive'), $renderer, $vars, 'aggregate.php', 'post');
+
+$template = new Horde_Template();
+$template->set('main', $main);
+$template->set('menu', Jonah::getMenu('string'));
+$template->set('notify', Horde_Util::bufferOutput(array($notification, 'notify'), array('listeners' => 'status')));
+
+require JONAH_TEMPLATES . '/common-header.inc';
+echo $template->fetch(JONAH_TEMPLATES . '/main/main.html');
+require $registry->get('templates', 'horde') . '/common-footer.inc';
--- /dev/null
+<?php
+/**
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * $Horde: jonah/channels/delete.php,v 1.36 2009/11/24 04:15:37 chuck Exp $
+ *
+ * See the enclosed file LICENSE for license information (BSD). If you
+ * did not receive this file, see http://cvs.horde.org/co.php/jonah/LICENSE.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Marko Djukic <marko@oblo.com>
+ */
+
+define('JONAH_BASE', dirname(__FILE__) . '/..');
+require_once JONAH_BASE . '/lib/base.php';
+require_once JONAH_BASE . '/lib/News.php';
+require_once 'Horde/Form.php';
+require_once 'Horde/Form/Renderer.php';
+
+$news = Jonah_News::factory();
+
+/* Set up the form variables and the form. */
+$vars = Horde_Variables::getDefaultVariables();
+$form_submit = $vars->get('submitbutton');
+$channel_id = $vars->get('channel_id');
+
+$channel = $news->getChannel($channel_id);
+if (is_a($channel, 'PEAR_Error')) {
+ $notification->push(_("Invalid channel specified for deletion."), 'horde.message');
+ $url = Horde::applicationUrl('channels/index.php', true);
+ header('Location: ' . $url);
+ exit;
+}
+
+/* Check permissions and deny if not allowed. */
+if (!Jonah::checkPermissions(Jonah::typeToPermName($channel['channel_type']), Horde_Perms::DELETE, $channel_id)) {
+ $notification->push(_("You are not authorised for this action."), 'horde.warning');
+ Horde::authenticationFailureRedirect();
+}
+
+/* If not yet submitted set up the form vars from the fetched
+ * channel. */
+if (empty($form_submit)) {
+ $vars = new Horde_Variables($channel);
+}
+
+$title = sprintf(_("Delete News Channel \"%s\"?"), $vars->get('channel_name'));
+$form = new Horde_Form($vars, $title);
+
+$form->setButtons(array(_("Delete"), _("Do not delete")));
+$form->addHidden('', 'channel_id', 'int', true, true);
+
+$msg = _("Really delete this News Channel?");
+if ($vars->get('channel_type') == JONAH_INTERNAL_CHANNEL) {
+ $msg .= ' ' . _("All stories created in this channel will be lost!");
+} else {
+ $msg .= ' ' . _("Any cached stories for this channel will be lost!");
+}
+$form->addVariable($msg, 'confirm', 'description', false);
+
+if ($form_submit == _("Delete")) {
+ if ($form->validate($vars)) {
+ $form->getInfo($vars, $info);
+ $delete = $news->deleteChannel($info);
+ if (is_a($delete, 'PEAR_Error')) {
+ $notification->push(sprintf(_("There was an error deleting the channel: %s"), $delete->getMessage()), 'horde.error');
+ } else {
+ $notification->push(_("The channel has been deleted."), 'horde.success');
+ $url = Horde::applicationUrl('channels/index.php', true);
+ header('Location: ' . $url);
+ exit;
+ }
+ }
+} elseif (!empty($form_submit)) {
+ $notification->push(_("Channel has not been deleted."), 'horde.message');
+ $url = Horde::applicationUrl('channels/index.php', true);
+ header('Location: ' . $url);
+ exit;
+}
+
+$template = new Horde_Template();
+$template->set('main', Horde_Util::bufferOutput(array($form, 'renderActive'), new Horde_Form_Renderer(), $vars, 'delete.php', 'post'));
+$template->set('menu', Jonah::getMenu('string'));
+$template->set('notify', Horde_Util::bufferOutput(array($notification, 'notify'), array('listeners' => 'status')));
+
+require JONAH_TEMPLATES . '/common-header.inc';
+echo $template->fetch(JONAH_TEMPLATES . '/main/main.html');
+require $registry->get('templates', 'horde') . '/common-footer.inc';
--- /dev/null
+<?php
+/**
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * $Horde: jonah/channels/edit.php,v 1.37 2009/11/24 04:15:37 chuck Exp $
+ *
+ * See the enclosed file LICENSE for license information (BSD). If you
+ * did not receive this file, see http://cvs.horde.org/co.php/jonah/LICENSE.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Marko Djukic <marko@oblo.com>
+ */
+
+@define('JONAH_BASE', dirname(__FILE__) . '/..');
+require_once JONAH_BASE . '/lib/base.php';
+require_once JONAH_BASE . '/lib/News.php';
+require_once JONAH_BASE . '/lib/Forms/Feed.php';
+require_once 'Horde/Form/Renderer.php';
+
+$news = Jonah_News::factory();
+
+/* Set up the form variables and the form. */
+$vars = Horde_Variables::getDefaultVariables();
+$form = new FeedForm($vars);
+
+/* Set up some variables. */
+$formname = $vars->get('formname');
+$channel_id = $vars->get('channel_id');
+
+/* Form not yet submitted and is being edited. */
+if (!$formname && $channel_id) {
+ $vars = new Horde_Variables($news->getChannel($channel_id));
+}
+
+/* Get the vars for channel type. */
+$channel_type = $vars->get('channel_type');
+$old_channel_type = $vars->get('old_channel_type');
+$changed_type = false;
+
+/* Check permissions and deny if not allowed. */
+if (!Jonah::checkPermissions(Jonah::typeToPermName($channel_type), Horde_Perms::EDIT, $channel_id)) {
+ $notification->push(_("You are not authorised for this action."), 'horde.warning');
+ Horde::authenticationFailureRedirect();
+}
+
+/* If this is null then new form, so set both to default. */
+if (is_null($channel_type)) {
+ $channel_type = Jonah_News::getDefaultType();
+ $old_channel_type = $channel_type;
+}
+
+/* Check if channel type has been changed and notify. */
+if ($channel_type != $old_channel_type && $formname) {
+ $changed_type = true;
+ $notification->push(_("Feed type changed."), 'horde.message');
+}
+$vars->set('old_channel_type', $channel_type);
+
+/* Output the extra fields required for this channel type. */
+$form->setExtraFields($channel_type, $channel_id);
+
+if ($formname && !$changed_type) {
+ if ($form->validate($vars)) {
+ $form->getInfo($vars, $info);
+ $save = $news->saveChannel($info);
+ if (is_a($save, 'PEAR_Error')) {
+ $notification->push(sprintf(_("There was an error saving the feed: %s"), $save->getMessage()), 'horde.error');
+ } else {
+ $notification->push(sprintf(_("The feed \"%s\" has been saved."), $info['channel_name']), 'horde.success');
+ if ($channel_type == JONAH_AGGREGATED_CHANNEL) {
+ $notification->push(_("You can now edit the sub-feeds."), 'horde.message');
+ } else {
+ header('Location: ' . Horde::applicationUrl('channels/index.php', true));
+ exit;
+ }
+ }
+ }
+}
+
+$renderer = new Horde_Form_Renderer();
+$main = Horde_Util::bufferOutput(array($form, 'renderActive'), $renderer, $vars, 'edit.php', 'post');
+
+$template = new Horde_Template();
+$template->set('main', $main);
+$template->set('menu', Jonah::getMenu('string'));
+$template->set('notify', Horde_Util::bufferOutput(array($notification, 'notify'), array('listeners' => 'status')));
+
+$title = $form->getTitle();
+require JONAH_TEMPLATES . '/common-header.inc';
+echo $template->fetch(JONAH_TEMPLATES . '/main/main.html');
+require $registry->get('templates', 'horde') . '/common-footer.inc';
--- /dev/null
+<?php
+/**
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * $Horde: jonah/channels/index.php,v 1.49 2009/11/24 04:15:37 chuck Exp $
+ *
+ * See the enclosed file LICENSE for license information (BSD). If you
+ * did not receive this file, see http://cvs.horde.org/co.php/jonah/LICENSE.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Marko Djukic <marko@oblo.com>
+ */
+
+@define('JONAH_BASE', dirname(__FILE__) . '/..');
+require_once JONAH_BASE . '/lib/base.php';
+require_once JONAH_BASE . '/lib/News.php';
+
+if (!Jonah::checkPermissions('jonah:news', Horde_Perms::EDIT)) {
+ $notification->push(_("You are not authorised for this action."), 'horde.warning');
+ Horde_Auth::authenticateFailure();
+}
+
+$have_news = Jonah_News::getAvailableTypes();
+if (empty($have_news)) {
+ $notification->push(_("News is not enabled."), 'horde.warning');
+ $url = Horde::applicationUrl('index.php', true);
+ header('Location: ' . $url);
+ exit;
+}
+
+$news = Jonah_News::factory();
+
+$channels = $news->getChannels(array_keys($have_news));
+if (is_a($channels, 'PEAR_Error')) {
+ $notification->push(sprintf(_("An error occurred fetching channels: %s"), $channels->getMessage()), 'horde.error');
+ $channels = false;
+} elseif ($channels) {
+ $channels = Jonah::checkPermissions('channels', Horde_Perms::SHOW, $channels);
+ /* Build channel specific fields. */
+ foreach ($channels as $key => $channel) {
+ /* Edit channel link. */
+ $url = Horde::applicationUrl('channels/edit.php');
+ $url = Horde_Util::addParameter($url, 'channel_id', $channel['channel_id']);
+ $channels[$key]['edit_link'] = Horde::link($url, _("Edit channel"), '', '', '', _("Edit channel")) . Horde::img('edit.png', _("Edit channel"), '', $registry->getImageDir('horde')) . '</a>';
+
+ /* Delete channel link. */
+ $url = Horde::applicationUrl('channels/delete.php');
+ $url = Horde_Util::addParameter($url, 'channel_id', $channel['channel_id']);
+ $channels[$key]['delete_link'] = Horde::link($url, _("Delete channel"), '', '', '', _("Delete channel")) . Horde::img('delete.png', _("Delete channel"), null, $registry->getImageDir('horde')) . '</a>';
+
+ /* View stories link. */
+ $url = Horde::applicationUrl('stories/index.php');
+ $url = Horde_Util::addParameter($url, 'channel_id', $channel['channel_id']);
+ $channels[$key]['stories_url'] = $url;
+
+ /* Channel type specific links. */
+ $channels[$key]['addstory_link'] = '';
+ $channels[$key]['refresh_link'] = '';
+
+ switch ($channel['channel_type']) {
+ case JONAH_INTERNAL_CHANNEL:
+ /* Add story link. */
+ $url = Horde::applicationUrl('stories/edit.php');
+ $url = Horde_Util::addParameter($url, 'channel_id', $channel['channel_id']);
+ $channels[$key]['addstory_link'] = Horde::link($url, _("Add story"), '', '', '', _("Add story")) . Horde::img('new.png', _("Add story")) . '</a>';
+ break;
+
+ case JONAH_EXTERNAL_CHANNEL:
+ case JONAH_AGGREGATED_CHANNEL:
+ /* Refresh cache link. */
+ $url = Horde::applicationUrl('stories/index.php');
+ $url = Horde_Util::addParameter($url, array('channel_id' => $channel['channel_id'], 'refresh' => '1', 'url' => Horde::selfUrl()));
+ $channels[$key]['refresh_link'] = Horde::link($url, _("Refresh channel"), '', '', '', _("Refresh channel")) . Horde::img('reload.png', _("Refresh channel"), '', $registry->getImageDir('horde')) . '</a>';
+ break;
+ }
+
+ $channels[$key]['channel_type'] = Jonah::getChannelTypeLabel($channel['channel_type']);
+ /* TODO: pref setting for date display. */
+ $channels[$key]['channel_updated'] = ($channel['channel_updated'] ? date('M d, Y H:i', (int)$channel['channel_updated']) : '-');
+ }
+}
+
+$template = new Horde_Template();
+$template->set('header', _("Manage Feeds"));
+$template->set('listheaders', array(array('attrs' => ' class="sortdown"', 'label' => _("Name")),
+ array('attrs' => '', 'label' => _("Type")),
+ array('attrs' => '', 'label' => _("Last Update"))));
+$template->set('channels', $channels, true);
+$template->set('menu', Jonah::getMenu('string'));
+$template->set('search_img', Horde::img('search.png', _("Search"), '', $registry->getImageDir('horde')));
+$template->set('notify', Horde_Util::bufferOutput(array($notification, 'notify'), array('listeners' => 'status')));
+
+$title = _("Feeds");
+Horde::addScriptFile('prototype.js', 'horde', true);
+Horde::addScriptFile('tables.js', 'horde', true);
+Horde::addScriptFile('QuickFinder.js', 'horde', true);
+require JONAH_TEMPLATES . '/common-header.inc';
+echo $template->fetch(JONAH_TEMPLATES . '/channels/index.html');
+require $registry->get('templates', 'horde') . '/common-footer.inc';
--- /dev/null
+conf.php
+conf.bak.php
+prefs.php
+routes.local.php
+templates.php
--- /dev/null
+Deny from all
--- /dev/null
+<?xml version="1.0"?>
+<!-- $Horde: jonah/config/conf.xml,v 1.37 2008/06/29 23:17:13 chuck Exp $ -->
+<configuration>
+
+ <configsection name="news">
+ <configheader>Feed Settings</configheader>
+ <configmultienum name="enable" desc="Select which types of feeds to allow:">external
+ <values>
+ <value desc="Remote Feed">external</value>
+ <value desc="Local Feed">internal</value>
+ <value desc="Aggregated Feed">aggregated</value>
+ <value desc="Composite Feed">composite</value>
+ </values>
+ </configmultienum>
+
+ <configsection name="storage">
+ <configswitch name="driver" desc="What driver should we use for local feeds?">sql
+ <case name="sql" desc="SQL">
+ <configsection name="params">
+ <configsql switchname="driverconfig"/>
+ </configsection>
+ </case>
+ </configswitch>
+ </configsection>
+
+ <configmultienum name="story_types" desc="Which type of body types do you wish to enable for story creation? If none are selected the system will default to text.">
+ <values>
+ <value desc="text">text</value>
+ <value desc="rich text">richtext</value>
+ <value desc="URL links to external pages">links</value>
+ </values>
+ </configmultienum>
+ </configsection>
+
+ <configsection name="sharing">
+ <configheader>Story Sharing</configheader>
+ <configboolean name="allow" desc="Can users share stories with their friends?">true</configboolean>
+ </configsection>
+
+ <configsection name="comments">
+ <configheader>Story Comments</configheader>
+ <configboolean name="allow" desc="Can users comment on stories?">true</configboolean>
+ </configsection>
+
+ <configsection name="menu">
+ <configheader>Menu Settings</configheader>
+ <configmultienum name="apps" desc="Select any applications that should be linked in Jonah's menu">
+ <values>
+ <configspecial name="list-horde-apps" />
+ </values>
+ </configmultienum>
+ </configsection>
+
+</configuration>
--- /dev/null
+<?php
+/**
+ * $Horde: jonah/config/templates.php.dist,v 1.15 2007/10/10 03:49:00 mrubinsk Exp $
+ *
+ * This file stores the templates used to generate different views of
+ * news channels.
+ */
+
+$templates['standard'] = array('name' => _("Standard"),
+ 'template' => '<table width="100%" cellspacing="0" class="linedRow">
+<if:error><tr class="text"><td><em><tag:error /></em></td></tr></if:error>
+<if:stories>
+<loop:stories>
+<tr><td><strong><a target="_blank" href="<tag:stories.story_link />"><tag:story_marker /> <tag:stories.story_title /></a></strong><div style="padding-left:10px"><tag:stories.story_desc /></div></td></tr>
+</loop:stories>
+</if:stories>
+
+<if:form><tr><td><tag:form /></td></tr></if:form></table>');
+
+$templates['media'] = array('name' => _("Media"),
+ 'template' => '<table width="100%" cellspacing="0" class="linedRow">
+<if:error><tr class="text"><td><em><tag:error /></em></td></tr></if:error>
+<if:stories>
+<loop:stories>
+<tr><td><strong><a target="_blank" href="<tag:stories.story_link />"><img title="<tag:stories.story_media_description />" style="margin:7px;" src="<tag:stories.story_media_thumbnail_url />" /></a></strong><div style="margin:7px;"><a target="_blank" href="<tag:stories.story_link />"><tag:stories.story_media_title /></a></div></td></tr>
+</loop:stories>
+</if:stories>
+
+<if:form><tr><td><tag:form /></td></tr></if:form></table>');
+
+$templates['internal'] = array('name' => _("Internal"),
+ 'template' => '<table width="100%" cellspacing="0" class="linedRow">
+<if:error><tr class="text"><td><em><tag:error /></em></td></tr></if:error>
+<if:stories>
+<loop:stories>
+<tr><td><strong><a href="<tag:stories.story_link />"><tag:story_marker /> <tag:stories.story_title /></a></strong><div style="padding-left:10px"><tag:stories.story_desc /></div></td></tr>
+</loop:stories>
+</if:stories>
+
+<if:form><tr><td><tag:form /></td></tr></if:form></table>');
+
+$templates['compact'] = array('name' => _("Compact"),
+ 'template' => '<table width="100%" cellspacing="1" class="linedRow">
+<if:error><tr class="text"><td><em><tag:error /></em></td></tr></if:error>
+<if:stories>
+<loop:stories>
+<tr class="text"><td><a target="_blank" href="<tag:stories.story_link />"><tag:story_marker /> <tag:stories.story_title /></a></td></tr>
+</loop:stories>
+</if:stories>
+
+<if:form><tr><td><tag:form /></td></tr></if:form></table>');
+
+$templates['ultracompact'] = array('name' => _("Ultracompact"),
+ 'template' => '<if:error><em><tag:error /></em></if:error>
+<if:stories>
+<loop:stories>
+:: <a target="blank" href="<tag:stories.story_link />"><tag:stories.story_title /></a>
+</loop:stories>
+</if:stories>');
--- /dev/null
+<IfModule mod_rewrite.c>
+ RewriteEngine On
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteRule ^(.*)$ index.php/$1 [QSA,L]
+</IfModule>
--- /dev/null
+<?php
+/**
+ * Script to handle requests for html delivery of stories.
+ *
+ * $Horde: jonah/delivery/html.php,v 1.24 2009/06/10 05:24:47 slusarz Exp $
+ *
+ * Copyright 2004-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (BSD). If you did not
+ * did not receive this file, see http://cvs.horde.org/co.php/jonah/LICENSE.
+ *
+ * @author Jan Schneider <jan@horde.org>
+ */
+
+$session_control = 'readonly';
+@define('AUTH_HANDLER', true);
+@define('JONAH_BASE', dirname(__FILE__) . '/..');
+require_once JONAH_BASE . '/lib/base.php';
+require_once JONAH_BASE . '/lib/News.php';
+require JONAH_BASE . '/config/templates.php';
+
+// TODO - check if a user, have button to add channel to their
+// personal aggregated channel.
+
+$news = Jonah_News::factory();
+
+/* Get the id and format of the channel to display. */
+$criteria = Horde_Util::nonInputVar('criteria');
+if (!$criteria) {
+ $criteria['channel_id'] = Horde_Util::getFormData('channel_id');
+ $criteria['channel_format'] = Horde_Util::getFormData('format');
+}
+
+if (empty($criteria['channel_format'])) {
+ // Select the default channel format
+ $criteria['channel_format'] = key($templates);
+}
+
+/* Get requested channel. */
+$channel = $news->getChannel($criteria['channel_id']);
+if (is_a($channel, 'PEAR_Error')) {
+ Horde::logMessage($channel, __FILE__, __LINE__, PEAR_LOG_ERR);
+ $notification->push(_("Invalid channel."), 'horde.error');
+ $url = Horde::applicationUrl('delivery/index.php', true);
+ header('Location: ' . $url);
+ exit;
+}
+
+$title = sprintf(_("HTML Delivery for \"%s\""), $channel['channel_name']);
+
+$options = array();
+foreach ($templates as $key => $info) {
+ $options[] = '<option value="' . $key . '"' . ($key == $criteria['channel_format'] ? ' selected="selected"' : '') . '>' . $info['name'] . '</option>';
+}
+
+$template = new Horde_Template();
+$template->setOption('gettext', 'true');
+$template->set('url', Horde::selfUrl());
+$template->set('session', Horde_Util::formInput());
+$template->set('channel_id', $criteria['channel_id']);
+$template->set('channel_name', $channel['channel_name']);
+$template->set('format', $criteria['channel_format']);
+$template->set('options', $options);
+$template->set('stories', $news->renderChannel($criteria['channel_id'], $criteria['channel_format']));
+$template->set('menu', Jonah::getMenu('string'));
+$template->set('notify', Horde_Util::bufferOutput(array($notification, 'notify'), array('listeners' => 'status')));
+
+require JONAH_TEMPLATES . '/common-header.inc';
+echo $template->fetch(JONAH_TEMPLATES . '/delivery/html.html');
+require $registry->get('templates', 'horde') . '/common-footer.inc';
--- /dev/null
+<?php
+/**
+ * $Horde: jonah/delivery/index.php,v 1.27 2009/06/10 05:24:47 slusarz Exp $
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (BSD). If you did
+ * did not receive this file, see http://cvs.horde.org/co.php/jonah/LICENSE.
+ *
+ * @author Ben Klang <ben@alkaloid.net>
+ */
+
+$parts = explode('/', Horde_Util::getPathInfo());
+$lastpart = null;
+$deliverytype = null;
+$criteria = array();
+foreach ($parts as $part) {
+ if (empty($part)) {
+ // Double slash in the URL path. Ignore this empty part.
+ continue;
+ }
+
+ switch($part) {
+ case 'html':
+ case 'rss':
+ $deliveryType = $part;
+ break;
+
+ case 'type':
+ // Feed type is specially mangled
+ $lastpart = 'feed_type';
+ break;
+
+ case 'format':
+ // Format is specially mangled
+ $lastpart = 'channel_format';
+ break;
+
+ case 'author':
+ case 'channel_format':
+ case 'tag':
+ case 'tag_id':
+ case 'story':
+ case 'story_id':
+ case 'channel':
+ case 'channel_id':
+ $lastpart = $part;
+ break;
+
+ default:
+ if (!empty($lastpart)) {
+ $criteria[$lastpart] = $part;
+ $lastpart = null;
+ } else {
+ // An unknown directive
+ Horde::logMessage("Malformed request URL: " . Horde_Util::getPathInfo(),
+ __FILE__, __LINE__, PEAR_LOG_WARNING);
+ exit;
+ }
+ break;
+ }
+}
+
+if (empty($deliveryType)) {
+ $deliveryType = 'html';
+}
+
+include dirname(__FILE__) . '/' . basename($deliveryType) . '.php';
--- /dev/null
+<?php
+/**
+ * $Horde: jonah/delivery/rss.php,v 1.41 2010/01/04 02:23:14 chuck Exp $
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (BSD). If you did
+ * did not receive this file, see http://cvs.horde.org/co.php/jonah/LICENSE.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+
+$session_control = 'readonly';
+@define('AUTH_HANDLER', true);
+@define('JONAH_BASE', dirname(__FILE__) . '/..');
+require_once JONAH_BASE . '/lib/base.php';
+require_once JONAH_BASE . '/lib/News.php';
+require_once JONAH_BASE . '/lib/version.php';
+
+$news = Jonah_News::factory();
+
+// See if the criteria has already been loaded by the index page
+$criteria = Horde_Util::nonInputVar('criteria');
+if (!$criteria) {
+ $criteria = array();
+ $criteria['channel_id'] = Horde_Util::getFormData('channel_id');
+ $criteria['tag_id'] = Horde_Util::getFormData('tag_id');
+ $criteria['feed_type'] = basename(Horde_Util::getFormData('type'));
+}
+
+if (empty($criteria['feed_type'])) {
+ // If not specified, default to RSS2
+ $criteria['feed_type'] = 'rss2';
+}
+
+/* Fetch the channel info and the story list and check they are both valid.
+ * Do a simple exit in case of errors. */
+
+
+$channel = $news->getChannel($criteria['channel_id']);
+if (is_a($channel, 'PEAR_Error')) {
+ Horde::logMessage($channel, __FILE__, __LINE__, PEAR_LOG_ERR);
+ header('HTTP/1.0 404 Not Found');
+ echo '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
+<html><head>
+<title>404 Not Found</title>
+</head><body>
+<h1>Not Found</h1>
+<p>The requested feed (' . htmlspecialchars($criteria['channel_id']) . ') was not found on this server.</p>
+</body></html>';
+ exit;
+}
+
+/* Check for a tag search. */
+if (!empty($criteria['tag_id'])) {
+ $tag_name = array_shift($news->getTagNames(array($criteria['tag_id'])));
+ $stories = $news->searchTagsById(array($criteria['tag_id']), 10, 0, array($criteria['channel_id']));
+} else {
+ $stories = $news->getStories($criteria['channel_id'], 10, 0, false, time());
+}
+if (is_a($stories, 'PEAR_Error')) {
+ Horde::logMessage($stories, __FILE__, __LINE__, PEAR_LOG_ERR);
+ $stories = array();
+}
+
+
+$template = new Horde_Template();
+$template->set('charset', Horde_Nls::getCharset());
+$template->set('jonah', 'Jonah ' . JONAH_VERSION . ' (http://www.horde.org/jonah/)');
+$template->set('xsl', $registry->get('themesuri') . '/feed-rss.xsl');
+if (!empty($criteria['tag_id'])) {
+ $template->set('channel_name', sprintf(_("Stories tagged with %s in %s"), $tag_name, @htmlspecialchars($channel['channel_name'], ENT_COMPAT, Horde_Nls::getCharset())));
+} else {
+ $template->set('channel_name', @htmlspecialchars($channel['channel_name'], ENT_COMPAT, Horde_Nls::getCharset()));
+}
+$template->set('channel_desc', @htmlspecialchars($channel['channel_desc'], ENT_COMPAT, Horde_Nls::getCharset()));
+$template->set('channel_updated', htmlspecialchars(date('r', $channel['channel_updated'])));
+$template->set('channel_official', htmlspecialchars($channel['channel_official']));
+$template->set('channel_rss', htmlspecialchars(Horde_Util::addParameter(Horde::applicationUrl('delivery/rss.php', true, -1), array('type' => 'rss', 'channel_id' => $channel['channel_id']))));
+$template->set('channel_rss2', htmlspecialchars(Horde_Util::addParameter(Horde::applicationUrl('delivery/rss.php', true, -1), array('type' => 'rss2', 'channel_id' => $channel['channel_id']))));
+foreach ($stories as &$story) {
+ $story['story_title'] = @htmlspecialchars($story['story_title'], ENT_COMPAT, Horde_Nls::getCharset());
+ $story['story_desc'] = @htmlspecialchars($story['story_desc'], ENT_COMPAT, Horde_Nls::getCharset());
+ $story['story_link'] = htmlspecialchars($story['story_link']);
+ $story['story_permalink'] = (isset($story['story_permalink']) ? htmlspecialchars($story['story_permalink']) : '');
+ $story['story_published'] = htmlspecialchars(date('r', $story['story_published']));
+ if (!empty($story['story_body_type']) && $story['story_body_type'] == 'text') {
+ $story['story_body'] = Horde_Text_Filter::filter($story['story_body'], 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::MICRO, 'class' => null));
+ }
+}
+$template->set('stories', $stories);
+
+$browser->downloadHeaders($channel['channel_name'] . '.rss', 'text/xml', true);
+$tpl = JONAH_TEMPLATES . '/delivery/' . $criteria['feed_type'];
+if (!empty($channel['channel_full_feed'])) {
+ $tpl .= '_full';
+}
+echo $template->fetch($tpl . '.xml');
--- /dev/null
+<?php
+/**
+ * $Horde: jonah/dispatcher.php,v 1.9 2009/06/10 05:24:46 slusarz Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (BSD). If you did
+ * did not receive this file, see http://cvs.horde.org/co.php/jonah/LICENSE.
+ *
+ * @author Ben Klang <ben@alkaloid.net>
+ */
+$session_control = 'readonly';
+@define('AUTH_HANDLER', true);
+@define('JONAH_BASE', dirname(__FILE__));
+require_once JONAH_BASE . '/lib/base.php';
+require_once JONAH_BASE . '/lib/News.php';
+require JONAH_BASE . '/config/templates.php';
+
+// Grab, and hopefully match, the URL
+$url = Horde_Util::getPathInfo();
+parse_str($_SERVER['QUERY_STRING'], $args);
+Horde_Util::dispelMagicQuotes($args);
+
+$criteria = array();
+$result = $m->match($url);
+if (isset($result['controller']) && $result['controller'] == 'admin') {
+ // Insert admin controllers here.
+} elseif (isset($result['feed'])) {
+ // Default settings
+ $defaults = array(
+ 'format' => 'html',
+ 'feed' => $result['feed'],
+ );
+
+ // Check for the format specification
+ if ($pos = strrpos($result['feed'], '.')) {
+ $criteria['feed'] = substr($result['feed'], 0, $pos);
+ $criteria['format'] = substr($result['feed'], $pos + 1);
+ }
+
+ if (!empty($result['filter'])) {
+ switch ($result['filter']) {
+ case 'author':
+ $criteria['author'] = $result['value'];
+ break;
+
+ case 'date':
+ if (preg_match('/\d{4}-\d{1,2}/', $result['value'])) {
+ list($year, $month) = explode('-', $result['value']);
+ $criteria['updated-min'] = new Horde_Date(array(
+ 'month' => $month,
+ 'year' => $year));
+ // Set the end date to the end of the requested month
+ $criteria['updated-max'] = new Horde_Date(array(
+ 'month' => ++$month,
+ 'year' => $year));
+ $criteria['updated-max']->sec--;
+ $criteria['updated-max']->correct();
+ break;
+ }
+
+ case 'tag':
+ $criteria['tags'] = array($result['value']);
+ break;
+ }
+ }
+
+ if (isset($args['tags'])) {
+ if (strpos($args['tags'], '|') !== false) {
+ // We have an OR list of tags
+ $criteria['tags'] = explode('|', $args['tags']);
+ } elseif (strpos($args['tags'], ',') !== false) {
+ // We have an AND list of tags
+ $criteria['alltags'] = explode(',', $args['tags']);
+ } else {
+ // Just a single tag
+ $criteria['tags'] = array($args['tags']);
+ }
+ }
+ unset($args['tags']);
+
+ // These dates are expected to be in RFC 3339 format
+ if (isset($args['updated-min'])) {
+ $criteria['updated-min'] = new Horde_Date($args['updated-min']);
+ unset($args['updated-min']);
+ }
+ if (isset($args['updated-max'])) {
+ $criteria['updated-max'] = new Horde_Date($args['updated-max']);
+ unset($args['updated-max']);
+ }
+ if (isset($args['published-min'])) {
+ $criteria['published-min'] = new Horde_Date($args['published-min']);
+ unset($args['published-min']);
+ }
+ if (isset($args['published-max'])) {
+ $criteria['published-max'] = new Horde_Date($args['published-max']);
+ unset($args['published-max']);
+ }
+
+ // Parse keyword search arguments
+ $keywords = array();
+ $notkeywords = array();
+ if (isset($args['q'])) {
+ $query = $args['q'];
+
+ // Look for quoted strings
+ while (($quotepos = strpos($query, '"')) !== false) {
+ if ($quotepos !== 0) {
+ $keywords = array_merge(explode(' ', substr($query, 0, $quotepos)), $keywords);
+ $query = substr($query, $quotepos);
+ }
+
+ $keywords[] = substr($query, 1, strpos($query, '"', 1) - 1);
+ $query = substr($query, 1);
+ $query = substr($query, strpos($query, '"', 1) + 1);
+ }
+
+ // Split up any remaining text into keywords
+ $keywords = array_merge(explode(' ', $query), $keywords);
+
+ // Remove duplicates and empty values
+ $keywords = array_flip($keywords);
+ unset ($keywords['']);
+ $keywords = array_flip($keywords);
+
+ // We're done with 'q'. Unset it to prevent it being copied into
+ // $criteria below.
+ unset($args['q']);
+
+ foreach ($keywords as $index => $keyword) {
+ if (substr($keyword, 0, 1) == '-') {
+ $notkeywords[] = substr($keyword, 1);
+ unset($keywords[$index]);
+ }
+ }
+
+ // Save the criteria
+ if (!empty($keywords)) {
+ $criteria['keywords'] = $keywords;
+ }
+ if (!empty($notkeywords)) {
+ $criteria['notkeywords'] = $notkeywords;
+ }
+ }
+
+ // Preserve remaining args
+ $criteria = array_merge($defaults, $args, $criteria);
+
+ require dirname(__FILE__) . '/feed.php';
+}
--- /dev/null
+----
+v0.1
+----
+
+[jan] Enable output compression.
+[cjh] Remove the Jonah-specific portal (Bug #7584).
+[cjh] Add support for full-content feeds (Request #6400).
+[cjh] Remove delivery lists.
+[jan] Add Turkish translation (METU <horde-tr@metu.edu.tr>).
+[mjr] Add a media block for displaying feeds containing media namespace nodes.
+[mjr] Add support for parsing the media namespace extensions to RSS 2.0 feeds.
+[mjr] Add searchTag api support to allow other applications to list tags and
+ perform tag searches of Jonah resources.
+[mjr] Add tagging support for internal channels.
+[cjh] Allow the story and latest blocks to count story reads.
+[cjh] Add PDF generation of stories.
+[cjh] Detect charset information from XML prologue as well as the
+ Content-Type header (s_gatterbauer@idlm.net, Bug #4340).
+[jan] Add RSS 2.0 feed generator.
+[jan] Add permalink field for stories.
+[jan] Add URL field to channel configuration for paged story lists.
+[cjh] Deliver RSS feeds in a way that supports USM
+ (http://www.kbcafe.com/rss/usm.html, Request #2593).
+[cjh] Standardize the date fields for stories with an updated field that is
+ always the timestamp that the story was last modified, and a published
+ field which is controlled by the author and determines the release date.
+[ben] Better support for MS-SQL.
+[cjh] Use Horde_Block_Layout_View for the My News page.
+[cjh] Add jonah:admin permission (tevans@tachometry.com, Bug #2571).
+[cjh] Add internal template type for internal stories (tevans@tachometry.com,
+ Bug #2571).
+[cjh] Add a block for showing the latest story from an internal channel
+ (Roel Gloudemans <roel@gloudemans.info>).
+[cjh] Initial feeds script now uses Jonah objects to do feed
+ creation to avoid sequence problems.
+[jan] Add Dutch translation (Resan Sa-Ardnuam <horde@sa-ardnuam.nl>).
+[cjh] Initial Atom feed support (Bug #1581).
+[jan] Add support for release dates.
+[jan] Show links to internal channels in sidebar menu.
+[jan] Allow comments on stories.
+[jan] Add support for composite channels.
+[jan] Allow to specify an alternative story URL.
+[jan] Track how many times a story was read.
+[jan] Add support for aggregating channels.
+[jon] Add the ability to share stories with friends via email.
+[mdj] newsfeed.php is no longer used, now there is a /delivery/ directory
+ offering for now the RSS feed as before and an email delivery.
+[mdj] Added option for HTML composition of stories.
+[cjh] Stocks code has been moved to Juno.
+[cjh] Remove the last weather code from Jonah.
+[cjh] Remove METAR code in favor of the Horde-level METAR block.
+[jan] Add Romanian translation (Eugen Hoanca <eugenh@urban-grafx.ro>).
+[jan] Add Finnish translation (Leena Heino <Leena.Heino@uta.fi>).
+[cjh] Clean up Jonah_Headlines:: to require fewer special cases.
+[cjh] Move all channel HTML generation over to Horde_Template::
+[cjh] Add an RSS generation class.
+[cjh] Start adding news-authoring capabilities.
+[jan] Add German translation.
+[cjh] Add option of using a DB to store weather stations
+ (Ben Scott <bscott@chiark.greenend.org.uk>).
+[cjh] Add the option to update a channel by executing a script
+ (Mario Andres Yepes C <marioy@upb.edu.co>).
+[jan] Add Traditional Chinese translation (Chih-Wei Yeh
+ <cwyeh@ccca.nctu.edu.tw>).
+[cjh] Add a preference for using metric units
+ (Tim Gorter <email@teletechnics.com>).
+[cjh] Revamp headline management and subscriptions; add multiple sizes
+ for each channel, add weather and stock support, and many more
+ channels (Eric Rechlin <eric@hpcalc.org>).
+[cjh] Add a Block system and an experimental portal-like interface.
+[cjh] Add menu to all pages.
+[cjh] Add user preference for which channels to display.
+[cjh] License is the Horde BSD license.
+[cjh] Close a potential problem with register_globals On and $js_onLoad.
+[cjh] Don't redirect from backend.php to any login screen; either
+ authentication is there, or we exit().
+[cjh] Use JONAH_TEMPLATES constant for all template paths.
+[cjh] Use $registry->get() for all Registry information.
+[cjh] Update cli-backend.php so that it can actually be useful.
+[cjh] Use Horde admin settings.
+[max] Greatly modernize and update.
+[avsm] Replace $conf['paths'] with the $registry equivalents.
+[cjh] Get rid of package.HTMLDocument.php use.
+
+
+------
+v0.0.2
+------
+
+[cjh] Everything now works with call-time pass-by-reference disabled, and
+ with the current state of XML in php4.
+[cjh] Added a simple update.sh script that can be run from cron.
+[cjh] The backend can now be restricted with username/password or by IP.
+[cjh] Updated test.php to recognize php4 stable releases.
+
+
+------
+v0.0.1
+------
+
+[cjh] More or less complete documentation.
+[cjh] Supports RSS and My.Userland site summary files.
--- /dev/null
+========================
+ Jonah Development Team
+========================
+
+
+Core Developers
+===============
+
+Chuck Hagenbuch <chuck@horde.org>
+
+- original concept
+- original code
+
+Jan Schneider <jan@horde.org>
+
+- composite channels
+- aggregator
+- comments
+
+Jon Parise <jon@csh.rit.edu>
+
+- documentation
+- testing
+
+
+Localization
+============
+
+===================== ======================================================
+Chinese Traditional Chih-Wei Yeh <cwyeh@ccca.nctu.edu.tw>
+Dutch Resan Sa-Ardnuam <horde@sa-ardnuam.nl>
+Finnish Leena Heino <Leena.Heino@uta.fi>
+French Eric Rostetter <eric.rostetter@physics.utexas.edu>
+German Jan Schneider <jan@horde.org>
+Romanian Eugen Hoanca <eugenh@urban-grafx.ro>
+Spanish Mario Andrés Yepes C <marioy@iname.com>
+ Manuel Perez Ayala <mperaya@alcazaba.unex.es>
+Turkish Middle East Technical University <horde-tr@metu.edu.tr>
+===================== ======================================================
--- /dev/null
+==========================
+|| INSTALLING Jonah 0.1 ||
+==========================
+
+This document contains instructions for installing the Jonah software
+on your system.
+
+For information on the capabilities and features of Jonah, see
+the file README in the top-level directory of the distribution.
+
+
+Obtaining Jonah
+~~~~~~~~~~~~~~~
+
+Jonah can be obtained from the Horde website and FTP server, at
+
+ http://www.horde.org/jonah/
+ ftp://ftp.horde.org/pub/jonah/
+
+Bleeding-edge development versions of Jonah are available via CVS; see
+the file docs/HACKING in the Horde distribution for information on
+accessing the Horde CVS repository.
+
+
+PREREQUISITES
+~~~~~~~~~~~~~
+
+To function properly, Jonah REQUIRES the following:
+
+ 1. A working Horde installation.
+
+ Jonah runs within the Horde Application Framework, a set of
+ common tools for Web applications written in PHP. You must
+ install Horde before installing Jonah.
+
+ The Horde Framework can be obtained from the Horde website and
+ FTP server, at
+
+ http://www.horde.org/horde/
+ ftp://ftp.horde.org/pub/horde/
+
+ Many of Jonah's prerequisites are also Horde prerequisites.
+ Be sure to have completed all of the steps in the INSTALL
+ file for the Horde Framework before installing Jonah.
+
+ 2. The following PHP capabilities:
+
+ For the released tarball versions, you will require
+ a working php 3 or 4 install with working XML support
+ and fopen-wrappers enabled.
+
+ For the CVS version, you will require a working php version
+ 4.2.0 or better, with XML and fopen-wrappers enabled.
+
+
+Installing Jonah
+~~~~~~~~~~~~~~~~
+
+Jonah is written in PHP, and must be installed in a web-accessible
+directory. The precise location of this directory will differ from
+system to system. Conventionally, Jonah is installed directly underneath
+Horde in the web server's document tree.
+
+Since Jonah is written in PHP, there is no compilation necessary;
+simply expand the distribution where you want it to reside and rename
+the root directory of the distribution to whatever you wish to appear
+in the URL. For example, with the Apache web server's default document
+root of '/usr/local/apache/htdocs', you would type:
+
+ cd /usr/local/apache/htdocs/horde
+ tar zxvf /path/to/jonah-0.0.2.tar.gz
+ mv jonah-0.0.2 jonah
+
+and would then find Jonah at the URL
+
+ http://your-server/horde/jonah/
+
+At this point, you should probably point your browser at your Jonah's test
+page at http://your.server/horde/jonah/test.php. It will run some checks
+on the version of php you're running, whether or not you have XML support,
+miscellaneous php settings, and whether or not the config files are in the
+right places.
+
+Unless you've skipped ahead, it'll complain about not finding the config
+files. If no other problems are reported, continue on to the next section
+on configuring Jonah. If it finds other problems on the test page, you
+should correct them first before proceeding.
+
+
+CONFIGURING Jonah
+~~~~~~~~~~~~~~~~~
+
+1. Configuring Horde for Jonah
+
+ a. Register the application
+
+ You will first need to add a jonah stanza to your version of the
+ horde/config/registry.php file. Edit this file, and add the
+ following stanza where appropriate (after the other entries like it
+ or between existing stanzas to position it in the menu where you
+ like):
+
+$this->applications['jonah'] = array(
+ 'fileroot' => dirname(__FILE__) . '/../jonah',
+ 'webroot' => $this->applications['horde']['webroot'] . '/jonah',
+ 'icon' => $this->applications['horde']['webroot'] . '/jonah/graphics/jonah.png',
+ 'name' => _("Jonah"),
+ 'status' => 'active'
+);
+
+ If you have changed the location of Jonah relative to Horde,
+ either in the URL or in the file system or both, you must
+ update the 'fileroot' and/or 'webroot' settings to their correct
+ values.
+
+2. Configuring Jonah
+
+ To configure Jonah, change to the config/ directory of the
+ installed distribution, and make copies of all of the configuration
+ "dist" files without the "dist" suffix. An example of how to copy
+ the files on a unix based system is:
+
+ cd config/
+ for foo in *.dist; do cp $foo `basename $foo .dist`; done
+
+ Documentation on the format and purpose of those files can be found in
+ each file. You may edit these files if you wish to customize Jonah's
+ appearance and behavior. The default values, while reasonable, may not
+ be appropriate for your site, so it is best to check them and make any
+ desired changes before proceeding. Some important files and their
+ contents are:
+
+ You must then login to Horde as a Horde Administrator to finish the
+ configuration of Jonah. Use the Horde "Administration" menu item to get
+ the the Administration page, and then on the click on the "Configuration"
+ icon to get the Configuration page. Select "Headlines" from the selection
+ list of applications, and click on the "Configure" button. Fill in or
+ change any configuration values as needed. When done click on "Generate
+ Headlines Configuration" to generate the conf.php file. If your web server
+ doesn't have write permissions to the Jonah configuration directory or
+ file, it will not be able to write the file. In this case, cut and
+ paste the returned configuration information into the file
+ jonah/config/conf.php.
+
+ Note for international users: Jonah uses GNU gettext to provide local
+ translations of text displayed by applications; the translations are
+ found in the po/ directory. If a translation is not yet available
+ for your locale (and you wish to create one), or if you're having
+ trouble using a provided translation, please see the
+ horde/docs/TRANSLATIONS file for instructions.
+
+ (a). prefs.php
+
+ This file contains all the preferences and their default values.
+ Reasons you might edit this are to change default values or to
+ lock preferences so the user can't change them.
+
+ (b). templates.php
+
+ This file defines the formatting templates used to display the
+ channels for various layout styles. You will probably not want
+ to modify this file.
+
+ (c). conf.php or conf.xml
+ This file contains the general configuration for Jonah, such as
+ sql settings, expiration settings, etc.
+
+ For CVS HEAD, You must login to Horde as a Horde Administrator to
+ finish the configuration of Jonah. Use the Horde "Administration"
+ menu item to get to the Administration page, and then click on the
+ "Configuration" icon to get the Configuration page. Select Jonah
+ from the selection list of applications, and click on the
+ "Configure" button. Fill in or change any configuration values as
+ needed. When done click on "Generate Jonah Configuration" to
+ generate the conf.php file. If your web server doesn't have write
+ permissions to the jonah configuration directory or file, it will
+ not be able to write the file. In this case, cut and paste the
+ returned configuration information in the file jonah/config/conf.php.
+
+
+OBTAINING SUPPORT
+-----------------
+
+If you encounter problems with Jonah, help is available!
+
+The Horde Frequently Asked Questions List (FAQ), available on the Web
+at
+
+ http://www.horde.org/faq/
+
+The Horde Project runs a number of mailing lists, for individual
+applications and for issues relating to the project as a whole.
+Information, archives, and subscription information can be found at
+
+ http://www.horde.org/mail/
+
+Lastly, Horde developers, contributors and users may also be found on IRC,
+on the channel #horde on the Freenode Network (irc.freenode.net).
+
+Please keep in mind that Jonah is free software written by volunteers.
+For information on reasonable support expectations, please read
+
+ http://www.horde.org/support.php
+
+Thanks for using Jonah!
+
+The Jonah team
+
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQU$QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ2.)UQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQXt -QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQt )$WQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ- -. ]QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
+QQQQQQQQQQWQQQQWQQQQWQQQQWQQQQWQQQQW@( y.._wZQ#Kvv>*-??YY*?TYT$QQWQQQQWQQQQWQQ
+QQQQQQQWQQQQQWQQQQWQQQQWQQQQWQQQQ@?CmWIliimw;vI==n.::+W7?T#WWWmwg:+-~"T9QQWQQQQ
+QQQQQQQQQQWQQQQQQQQQQQQQQQQQ@TZ}^+==;==+|??+i=nt +:::::- _._.XI---v +o$QQQQQ
+QQQQQQWQQQQQQQQQWQQQWQQWQ@?n`W =ii:--:-+|:.==---==:::wawyWWWWWWWmW##agggv<dQQQQ
+QQQQQQQQQWQQWQQWQQQQQQW?e>I=+:-:::::::::::::::::::::-n]WWWWW##ZZXnliisaxgQQQQQQ
+QQQQQQQWQQQQQQQQQQQWP\?|__X-:::::::::::::::::::::::::=]###XXS1xliiQxwqmQQQQQQQQ
+QQQQQQWQQQQQQQWQQQDmxw#ZZZmWp i::::::::::::: __ggggggaZXX11Ili>iiioqQQQQWQQQQQQ
+QQQQQQQQQQQWQQQQD(xZS1vuXZ#Zbc|yy#XXy=_awZmmm####Z#UXSvvvIMMiInqgQQQQQQQQQQQQQQ
+QQQQQQQQQQQQQQQUd3Sv+"oZZZXXXoSSnXSeS12n1on1onnvvlM""Qgap ::: ]dQQQQQQQWQQQQWQQ
+QQQQQQWQQWQQQWE^W _xXS11vIvnvM!MvgsgaaawawwyQQQQQQQQF` -::.]mQQQQQQQQQQWQQQQ
+QQQQQQQQQQQQW[': .jnvxnNY<wwwmQQQQQQQQQQQWWWQQQQQQQQQQf -:-.jQQQQQQQQQQQQQQQQ
+QQQQQQQWQQQW[,++j%xi1amQQQQWQQQQQQQQQQQQQQQQQQWQQQQQQQf :-:::XdQQQQQQQWQQWQQQWQ
+QQQQQQWQQQQQ[v _liQgQQQQQQQQQQQQQQQQQQQQQQQQQQQQQWQQQQQL, -:.wQQQQQQQQQQQQQQWQQ
+QQQQQQQQQQWicI)isyQQQQQQQQQQQQQQQQQQQQQQQQQWQQWQQQQQQQQQQgawWQWQQQQQQQWQQWQQQQQ
+QQQQQQQQQQQl1qWwQQQQQQQQQQQQWQQWQQWQQQQQQQWQQQQQQQQWQQWQQQQWWQQQQQQQQWQQQQQQQQQ
+QQQQQQWQQ@CauqmQQQQQQQQQWQQWQQQQQQQQQWQQWQQQQQQQWQQQQQQQQQQQQQQQQQQQQQQQQQQQWQQ
+QQQQBVbaoX2v2dWQQQQQQQQQQQQQQQQQQWQQQQQQQQQQQQWQQQQWQQQQQQWQQQQQWQQWQQQQWQQWQQQ
+QP5###Z#XXoSZLJQQQQQQQQWQQWQQQWQQQQQWQQQWQQQWQQQQQQQQQWQWQQQQQWQQQQQQQWQQQQQQQQ
+===============================================================================
+
+$Horde: jonah/docs/INSTALL,v 1.21 2007/06/19 09:56:35 jan Exp $
--- /dev/null
+=============================
+ Jonah Development TODO List
+=============================
+
+:Last update: $Date: 2007/12/14 18:31:17 $
+:Revision: $Revision: 1.7 $
+:Contact: dev@lists.horde.org
+
+$Horde: jonah/docs/TODO,v 1.7 2007/12/14 18:31:17 mrubinsk Exp $
--- /dev/null
+<?php
+/**
+ * Script to handle requests for html delivery of stories.
+ *
+ * $Horde: jonah/feed.php,v 1.6 2009/06/10 05:24:46 slusarz Exp $
+ *
+ * Copyright 2004-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (BSD). If you did not
+ * did not receive this file, see http://cvs.horde.org/co.php/jonah/LICENSE.
+ *
+ * @author Jan Schneider <jan@horde.org>
+ */
+
+$session_control = 'readonly';
+@define('AUTH_HANDLER', true);
+require_once dirname(__FILE__) . '/lib/base.php';
+require JONAH_BASE . '/config/templates.php';
+
+/* Get the id and format of the feed to display. */
+$criteria = Horde_Util::nonInputVar('criteria');
+if (empty($criteria['channel_format'])) {
+ // Select the default channel format
+ $criteria['channel_format'] = key($templates);
+}
+
+$options = array();
+foreach ($templates as $key => $info) {
+ $options[] = '<option value="' . $key . '"' . ($key == $criteria['channel_format'] ? ' selected="selected"' : '') . '>' . $info['name'] . '</option>';
+}
+
+if (empty($criteria['channel_id']) && !empty($criteria['feed'])) {
+ $criteria['channel_id'] = $jonah_driver->getChannelId($criteria['feed']);
+}
+
+if (empty($criteria['channel_id'])) {
+ $notification->push(_("No valid feed name or ID requested."), 'horde.error');
+} else {
+ $stories = $jonah_driver->getStories($criteria);
+}
+
+if (!empty($stories)) {
+ die(print_r($stories, true));
+}
+
+$template = new Horde_Template();
+$template->setOption('gettext', 'true');
+$template->set('url', Horde::selfUrl());
+$template->set('session', Horde_Util::formInput());
+$template->set('channel_id', $criteria['channel_id']);
+$template->set('channel_name', $channel['channel_name']);
+$template->set('format', $criteria['channel_format']);
+$template->set('options', $options);
+$template->set('stories', $news->renderChannel($criteria['channel_id'], $criteria['channel_format']));
+$template->set('menu', Jonah::getMenu('string'));
+$template->set('notify', Horde_Util::bufferOutput(array($notification, 'notify'), array('listeners' => 'status')));
+
+require JONAH_TEMPLATES . '/common-header.inc';
+echo $template->fetch(JONAH_TEMPLATES . '/delivery/html.html');
+require $registry->get('templates', 'horde') . '/common-footer.inc';
--- /dev/null
+<?php
+/**
+ * $Horde: jonah/index.php,v 1.39 2009/07/17 20:30:28 chuck Exp $
+ *
+ * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (BSD). If you
+ * did not receive this file, see http://cvs.horde.org/co.php/jonah/LICENSE.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+
+@define('JONAH_BASE', dirname(__FILE__));
+$jonah_configured = (is_readable(JONAH_BASE . '/config/conf.php') &&
+ is_readable(JONAH_BASE . '/config/templates.php'));
+
+if (!$jonah_configured) {
+ require JONAH_BASE . '/../lib/Test.php';
+ Horde_Test::configFilesMissing('Jonah', JONAH_BASE,
+ array('conf.php'),
+ array('templates.php' => 'This file defines the HTML (or other) templates that are used to generate different views of the news channels that Jonah provides.'));
+}
+
+require JONAH_BASE . '/channels/index.php';
--- /dev/null
+Deny from all
--- /dev/null
+<?php
+/**
+ * Jonah external API interface.
+ *
+ * $Horde: jonah/lib/Api.php,v 1.1 2009/11/11 01:32:03 mrubinsk Exp $
+ *
+ * This file defines Jonah's external API interface. Other
+ * applications can interact with Jonah through this API.
+ *
+ * @package Jonah
+ */
+class Jonah_Api extends Horde_Registry_Api
+{
+ /**
+ * Get a list of stored channels.
+ *
+ * @param integer $type The type of channel to filter for. Possible
+ * values are either JONAH_INTERNAL_CHANNEL
+ * to fetch only a list of internal channels,
+ * or JONAH_EXTERNAL_CHANNEL for only external.
+ * If null both channel types are returned.
+ *
+ * @return mixed An array of channels or PEAR_Error on error.
+ */
+ public function listFeeds($type = null)
+ {
+ require_once dirname(__FILE__) . '/base.php';
+ require_once JONAH_BASE . '/lib/News.php';
+
+ $news = Jonah_News::factory();
+ $channels = $news->getChannels($type);
+
+ return $channels;
+ }
+
+ /**
+ * Return the requested stories
+ *
+ * @param int $channel_id The channel to get the stories from.
+ * @param int $max_stories The maximum number of stories to get.
+ * @param int $start_at The story number to start retrieving.
+ * @param int $order How to order the results.
+ *
+ * @return An array of story information | PEAR_Error
+ */
+ public function stories($channel_id, $max_stories = 10, $start_at = 0,
+ $order = 0)
+ {
+ require_once dirname(__FILE__) . '/base.php';
+ require_once JONAH_BASE . '/lib/News.php';
+ $news = Jonah_News::factory();
+ $stories = $news->getStories($channel_id, $max_stories, $start_at, false,
+ time(), false, $order);
+
+ foreach (array_keys($stories) as $s) {
+ if (empty($stories[$s]['story_body_type']) || $stories[$s]['story_body_type'] == 'text') {
+ $stories[$s]['story_body_html'] = Horde_Text_Filter::filter($stories[$s]['story_body'], 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::MICRO, 'class' => null));
+ } else {
+ $stories[$s]['story_body_html'] = $stories[$s]['story_body'];
+ }
+ }
+
+ return $stories;
+ }
+
+ /**
+ * Fetches a story from a requested channel.
+ *
+ * @param integer $channel_id The channel id to fetch.
+ * @param integer $story_id The story id to fetch.
+ * @param boolean $read Whether to update the read count.
+ *
+ * @return mixed An array of story data | PEAR_Error
+ */
+ public function story($channel_id, $story_id, $read = true)
+ {
+ require_once dirname(__FILE__) . '/base.php';
+ require_once JONAH_BASE . '/lib/News.php';
+
+ $news = Jonah_News::factory();
+ $story = $news->getStory($channel_id, $story_id, $read);
+ if (is_a($story, 'PEAR_Error')) {
+ Horde::logMessage($story, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return false;
+ }
+ if (empty($story['story_body_type']) || $story['story_body_type'] == 'text') {
+ $story['story_body_html'] = Horde_Text_Filter::filter($story['story_body'], 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::MICRO, 'class' => null));
+ } else {
+ $story['story_body_html'] = $story['story_body'];
+ }
+
+ return $story;
+ }
+
+ /**
+ * Callback for comment API
+ *
+ * @param integer $id Internal data identifier
+ *
+ * @return mixed Name of object on success | false on failure
+ */
+ public function commentCallback($story_id)
+ {
+ if (!$GLOBALS['conf']['comments']['allow']) {
+ return false;
+ }
+
+ require_once dirname(__FILE__) . '/base.php';
+ require_once JONAH_BASE . '/lib/News.php';
+
+ $news = Jonah_News::factory();
+ $story = $news->getStory(null, $story_id);
+ if (is_a($story, 'PEAR_Error')) {
+ return false;
+ }
+
+ return $story['story_title'];
+ }
+
+ /**
+ * Check if comments are allowed.
+ *
+ * @return boolean
+ */
+ public function hasComments()
+ {
+ return $GLOBALS['conf']['comments']['allow'];
+ }
+
+ /**
+ * Retrieve the list of used tag_names, tag_ids and the total number
+ * of resources that are linked to that tag.
+ *
+ * @param array $tags An optional array of tag_ids. If omitted, all tags
+ * will be included.
+ *
+ * @param array $channel_id An optional array of channel_ids.
+ *
+ * @return mixed An array containing tag_name, and total | PEAR_Error
+ */
+ public function listTagInfo($tags = array(), $channel_id = null)
+ {
+ require_once dirname(__FILE__) . '/base.php';
+ require_once JONAH_BASE . '/lib/News.php';
+
+ $news = Jonah_News::factory();
+ return $news->listTagInfo($tags, $channel_id);
+ }
+
+ /**
+ * Return a set of tag_ids, given the tag name
+ *
+ * @param array $names An array of names to search for
+ *
+ * @return mixed An array of tag_name => tag_ids | PEAR_Error
+ */
+ public function getTagIds($names)
+ {
+ require_once dirname(__FILE__) . '/base.php';
+ require_once JONAH_BASE . '/lib/News.php';
+
+ $news = Jonah_News::factory();
+ return $news->getTagIds($names);
+ }
+
+ /**
+ * Searches internal channels for stories tagged with all requested tags.
+ * Returns an application-agnostic array (useful for when doing a tag search
+ * across multiple applications) containing the following keys:
+ * <pre>
+ * 'title' - The title for this resource.
+ * 'desc' - A terse description of this resource.
+ * 'view_url' - The URL to view this resource.
+ * 'app' - The Horde application this resource belongs to.
+ * </pre>
+ *
+ * The 'raw' story array can be returned instead by setting $raw = true.
+ *
+ * @param array $names An array of tag_names to search for (AND'd together).
+ * @param integer $max The maximum number of stories to return.
+ * @param integer $from The number of the story to start with.
+ * @param array $channel_id An array of channel_ids to limit the search to.
+ * @param integer $order How to order the results (a JONAH_ORDER_* constant)
+ * @param boolean $raw Return the raw story data?
+ *
+ * @return mixed An array of results | PEAR_Error
+ */
+ public function searchTags($names, $max = 10, $from = 0, $channel_id = array(),
+ $order = 0, $raw = false)
+ {
+ global $registry;
+
+ require_once dirname(__FILE__) . '/base.php';
+ require_once JONAH_BASE . '/lib/News.php';
+
+ $news = Jonah_News::factory();
+ $results = $news->searchTags($names, $max, $from, $channel_id, $order);
+ if (is_a($results, 'PEAR_Error')) {
+ return $results;
+ }
+ $return = array();
+ if ($raw) {
+ // Requesting the raw story information as returned from searchTags,
+ // but add some additional information that external apps might
+ // find useful.
+ $comments = $GLOBALS['conf']['comments']['allow'] && $registry->hasMethod('forums/numMessages');
+ foreach ($results as $story) {
+ if (empty($story['story_body_type']) || $story['story_body_type'] == 'text') {
+ $story['story_body_html'] = Horde_Text_Filter::filter($story['story_body'], 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::MICRO, 'class' => null));
+ } else {
+ $story['story_body_html'] = $story['story_body'];
+ }
+
+ if ($comments) {
+ $story['num_comments'] = $registry->call('forums/numMessages',
+ array($story['story_id'],
+ $registry->getApp()));
+ }
+
+ $return[$story['story_id']] = $story;
+ }
+ } else {
+ foreach($results as $story) {
+ if (!empty($story)) {
+ $return[] = array('title' => $story['story_title'],
+ 'desc' => $story['story_desc'],
+ 'view_url' => $story['story_link'],
+ 'app' => 'jonah');
+ }
+ }
+ }
+
+ return $return;
+ }
+
+ /**
+ * Get the count of stories in the specified channel
+ *
+ * @param int $channel_id
+ * @return mixed The story count
+ */
+ public function storyCount($channel_id)
+ {
+ global $registry;
+
+ require_once dirname(__FILE__) . '/base.php';
+
+ $results = $GLOBALS['jonah_driver']->getStoryCount($channel_id);
+ if (is_a($results, 'PEAR_Error')) {
+ return 0;
+ }
+
+ return $results;
+ }
+
+}
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Jonah application API.
+ *
+ * @package Kronolith
+ */
+class Jonah_Application extends Horde_Registry_Application
+{
+ public $version = 'H4 (1.0-cvs)';
+
+ /**
+ * Returns a list of available permissions.
+ *
+ * @return array
+ */
+ public function perms()
+ {
+ require_once dirname(__FILE__) . '/base.php';
+
+ $news = Jonah_News::factory();
+ $channels = $news->getChannels(JONAH_INTERNAL_CHANNEL);
+
+ /* Loop through internal channels and add their ids to the
+ * perms. */
+ $perms = array();
+ foreach ($channels as $channel) {
+ $perms['tree']['jonah']['news']['internal_channels'][$channel['channel_id']] = false;
+ }
+
+ /* Human names and default permissions. */
+ $perms['title']['jonah:admin'] = _("Administrator");
+ $perms['tree']['jonah']['admin'] = false;
+ $perms['title']['jonah:news'] = _("News");
+ $perms['tree']['jonah']['news'] = false;
+ $perms['title']['jonah:news:internal_channels'] = _("Internal Channels");
+ $perms['tree']['jonah']['news']['internal_channels'] = false;
+ $perms['title']['jonah:news:external_channels'] = _("External Channels");
+ $perms['tree']['jonah']['news']['external_channels'] = false;
+
+ /* Loop through internal channels and add them to the perms
+ * titles. */
+ foreach ($channels as $channel) {
+ $perms['title']['jonah:news:internal_channels:' . $channel['channel_id']] = $channel['channel_name'];
+ $perms['tree']['jonah']['news']['internal_channels'][$channel['channel_id']] = false;
+ }
+
+ $channels = $news->getChannels(JONAH_EXTERNAL_CHANNEL);
+
+ /* Loop through external channels and add their ids to the
+ * perms. */
+ foreach ($channels as $channel) {
+ $perms['title']['jonah:news:external_channels:' . $channel['channel_id']] = $channel['channel_name'];
+ $perms['tree']['jonah']['news']['external_channels'][$channel['channel_id']] = false;
+ }
+
+ return $perms;
+ }
+
+ /**
+ * Generate the menu to use on the prefs page.
+ *
+ * @return Horde_Menu A Horde_Menu object.
+ */
+ public function prefsMenu()
+ {
+ return Jonah::getMenu();
+ }
+
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+$block_name = _("Tag Cloud");
+
+/**
+ * Display Tag Cloud
+ *
+ * $Horde: jonah/lib/Block/cloud.php,v 1.11 2009/12/10 17:42:36 jan Exp $
+ *
+ * Copyright 2007-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael Rubinsky <mrubinsk@horde.org>
+ * @package Horde_Block
+ */
+class Horde_Block_jonah_cloud extends Horde_Block {
+
+ var $_app = 'jonah';
+
+ /**
+ */
+ function _params()
+ {
+ return array(
+ 'results_url' => array(
+ 'name' => _("Results URL"),
+ 'type' => 'text',
+ 'default' => Horde::applicationUrl('stories/results.php?tag_id=@id@')));
+ }
+
+ function _title()
+ {
+ return _("Tag Cloud");
+ }
+
+ function _content()
+ {
+ require_once dirname(__FILE__) . '/../base.php';
+ require_once JONAH_BASE . '/lib/News.php';
+
+ $news = Jonah_News::factory();
+
+ /* Get the tags */
+ $tags = $news->listTagInfo();
+ if (count($tags)) {
+ $cloud = new Horde_Ui_TagCloud();
+ foreach ($tags as $id => $tag) {
+ $cloud->addElement($tag['tag_name'], str_replace(array('@id@', '@tag@'), array($id, $tag['tag_name']), $this->_params['results_url']), $tag['total']);
+ }
+ $html = $cloud->buildHTML();
+ } else {
+ $html = '';
+ }
+ return $html;
+ }
+
+}
--- /dev/null
+<?php
+
+$block_name = _("Feeds");
+
+/**
+ * This class extends Horde_Block:: to provide a list of deliverable internal
+ * channels.
+ *
+ * $Horde: jonah/lib/Block/delivery.php,v 1.22 2009/06/10 05:24:47 slusarz Exp $
+ *
+ * Copyright 2004-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (BSD). If you
+ * did not receive this file, see http://cvs.horde.org/co.php/jonah/LICENSE.
+ *
+ * @author Roel Gloudemans <roel@gloudemans.info>
+ * @package Horde_Block
+ */
+class Horde_Block_Jonah_delivery extends Horde_Block {
+
+ var $_app = 'jonah';
+
+ function _title()
+ {
+ return _("Feeds");
+ }
+
+ function _content()
+ {
+ require_once dirname(__FILE__) . '/../base.php';
+ require_once JONAH_BASE . '/lib/Jonah.php';
+ require_once JONAH_BASE . '/lib/News.php';
+
+ $news = Jonah_News::factory();
+
+ $channels = array();
+ $channels = $news->getChannels(JONAH_INTERNAL_CHANNEL);
+ if (is_a($channels, 'PEAR_Error')) {
+ $channels = array();
+ }
+
+ $html = '';
+
+ foreach ($channels as $key => $channel) {
+ /* Link for HTML delivery. */
+ $url = Horde::applicationUrl('delivery/html.php');
+ $url = Horde_Util::addParameter($url, 'channel_id', $channel['channel_id']);
+ $label = sprintf(_("\"%s\" stories in HTML"), $channel['channel_name']);
+ $html .= '<tr><td width="140">' .
+ Horde::img('story_marker.png') . ' ' .
+ Horde::link($url, $label, '', '', '', $label) .
+ htmlspecialchars($channel['channel_name']) . '</a></td>';
+
+ $html .= '<td>' . ($channel['channel_updated'] ? date('M d, Y H:i', (int)$channel['channel_updated']) : '-') . '</td>';
+
+ /* Link for feed delivery. */
+ $url = Horde::applicationUrl('delivery/rss.php', true, -1);
+ $url = Horde_Util::addParameter($url, 'channel_id', $channel['channel_id']);
+ $label = sprintf(_("RSS Feed of \"%s\""), $channel['channel_name']);
+ $html .= '<td align="right" class="nowrap">' .
+ Horde::link($url, $label) .
+ Horde::img('feed.png') . '</a> ';
+ }
+
+ if ($html) {
+ return '<table cellspacing="0" width="100%" class="linedRow striped">' . $html . '</table>';
+ } else {
+ return '<p><em>' . _("No feeds are available.") . '</em></p>';
+ }
+ }
+
+}
--- /dev/null
+<?php
+
+$block_name = _("Latest News");
+
+/**
+ * This class extends Horde_Block:: to provide the api to embed news
+ * in other Horde applications.
+ *
+ * $Horde: jonah/lib/Block/latest.php,v 1.24 2009/07/09 08:18:26 slusarz Exp $
+ *
+ * Copyright 2002-2007 Roel Gloudemans <roel@gloudemans.info>
+ *
+ * See the enclosed file LICENSE for license information (BSD). If you
+ * did not receive this file, see http://cvs.horde.org/co.php/jonah/LICENSE.
+ *
+ * @author Roel Gloudemans <roel@gloudemans.info>
+ * @package Horde_Block
+ */
+class Horde_Block_Jonah_latest extends Horde_Block {
+
+ var $_app = 'jonah';
+
+ var $_story = null;
+
+ /**
+ */
+ function _params()
+ {
+ require_once dirname(__FILE__) . '/../base.php';
+ require_once JONAH_BASE . '/lib/Jonah.php';
+ require_once JONAH_BASE . '/lib/News.php';
+
+ $params['source'] = array('name' => _("News Source"),
+ 'type' => 'enum',
+ 'values' => array());
+
+ $news = Jonah_News::factory();
+ $channels = $news->getChannels(JONAH_INTERNAL_CHANNEL);
+ foreach ($channels as $channel) {
+ $params['source']['values'][$channel['channel_id']] = $channel['channel_name'];
+ }
+ natcasesort($params['source']['values']);
+
+ // Get first news source.
+ $channel = reset($channels);
+ $params['source']['default'] = $channel['channel_id'];
+
+ $params['countReads'] = array(
+ 'name' => _("Count reads of the latest story when this block is displayed"),
+ 'type' => 'boolean',
+ 'default' => false);
+
+ return $params;
+ }
+
+ /**
+ */
+ function _title()
+ {
+ require_once dirname(__FILE__) . '/../base.php';
+ require_once JONAH_BASE . '/lib/Jonah.php';
+ require_once JONAH_BASE . '/lib/News.php';
+
+ if (empty($this->_params['source'])) {
+ return _("Latest News");
+ }
+
+ $story = $this->_fetch();
+ return is_a($story, 'PEAR_Error')
+ ? @htmlspecialchars($story->getMessage(), ENT_COMPAT, Horde_Nls::getCharset())
+ : '<span class="storyDate">'
+ . @htmlspecialchars($story['story_updated_date'], ENT_COMPAT, Horde_Nls::getCharset())
+ . '</span> '
+ . @htmlspecialchars($story['story_title'], ENT_COMPAT, Horde_Nls::getCharset());
+ }
+
+ /**
+ */
+ function _content()
+ {
+ require_once dirname(__FILE__) . '/../base.php';
+ require_once JONAH_BASE . '/lib/Jonah.php';
+ require_once JONAH_BASE . '/lib/News.php';
+
+ if (empty($this->_params['source'])) {
+ return _("No channel specified.");
+ }
+
+ $story = $this->_fetch();
+ if (is_a($story, 'PEAR_Error')) {
+ return sprintf(_("Error fetching story: %s"), $story->getMessage());
+ }
+
+ if (empty($story['story_body_type']) || $story['story_body_type'] == 'text') {
+ $story['story_body'] = Horde_Text_Filter::filter($story['story_body'], 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::MICRO, 'class' => null));
+ }
+
+ return '<p class="storySubtitle">' . htmlspecialchars($story['story_desc']) .
+ '</p><div class="storyBody">' . $story['story_body'] . '</div>';
+ }
+
+ /**
+ * Get the latest story.
+ */
+ function _fetch()
+ {
+ if (empty($this->_params['source'])) {
+ return;
+ }
+
+ if (is_null($this->_story)) {
+ $news = Jonah_News::factory();
+ $this->_story = $news->getStory($this->_params['source'],
+ $news->getLatestStoryId($this->_params['source']),
+ !empty($this->_params['countReads']));
+ }
+
+ return $this->_story;
+ }
+
+}
--- /dev/null
+<?php
+
+$block_name = _("Feed");
+
+/**
+ * This class extends Horde_Block:: to provide an api to embed news
+ * in other Horde applications.
+ *
+ * $Horde: jonah/lib/Block/news.php,v 1.49 2009/07/09 08:18:26 slusarz Exp $
+ *
+ * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (BSD). If you
+ * did not receive this file, see http://cvs.horde.org/co.php/jonah/LICENSE.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @package Horde_Block
+ */
+class Horde_Block_Jonah_news extends Horde_Block {
+
+ var $_app = 'jonah';
+
+ function _params()
+ {
+ require_once dirname(__FILE__) . '/../base.php';
+ require_once JONAH_BASE . '/lib/Jonah.php';
+ require_once JONAH_BASE . '/lib/News.php';
+ require JONAH_BASE . '/config/templates.php';
+
+ $params['source'] = array('name' => _("Feed"),
+ 'type' => 'enum',
+ 'values' => array());
+
+ $news = Jonah_News::factory();
+ $channels = $news->getChannels();
+ foreach ($channels as $channel) {
+ $params['source']['values'][$channel['channel_id']] = $channel['channel_name'];
+ }
+ natcasesort($params['source']['values']);
+
+ $params['view'] = array('name' => _("View"),
+ 'type' => 'enum',
+ 'values' => array(),
+ );
+ foreach ($templates as $key => $template) {
+ $params['view']['values'][$key] = $template['name'];
+ }
+
+ $params['max'] = array('name' => _("Maximum Stories"),
+ 'type' => 'int',
+ 'default' => 10,
+ 'required' => false);
+
+ $params['from'] = array('name' => _("First Story"),
+ 'type' => 'int',
+ 'default' => 0,
+ 'required' => false);
+
+ return $params;
+ }
+
+ function _title()
+ {
+ require_once dirname(__FILE__) . '/../base.php';
+ require_once JONAH_BASE . '/lib/Jonah.php';
+ require_once JONAH_BASE . '/lib/News.php';
+
+ $news = Jonah_News::factory();
+ $channel = $news->getChannel($this->_params['source']);
+ if (is_a($channel, 'PEAR_Error')) {
+ return @htmlspecialchars($channel->getMessage(), ENT_COMPAT, Horde_Nls::getCharset());
+ }
+
+ if (!empty($channel['channel_link'])) {
+ $title = Horde::link(htmlspecialchars($channel['channel_link']), '', '', '_blank')
+ . @htmlspecialchars($channel['channel_name'], ENT_COMPAT, Horde_Nls::getCharset())
+ . '</a>';
+ } else {
+ $title = @htmlspecialchars($channel['channel_name'], ENT_COMPAT, Horde_Nls::getCharset());
+ }
+
+ return $title;
+ }
+
+ function _content()
+ {
+ require_once dirname(__FILE__) . '/../base.php';
+ require_once JONAH_BASE . '/lib/Jonah.php';
+ require_once JONAH_BASE . '/lib/News.php';
+
+ if (empty($this->_params['source'])) {
+ return _("No feed specified.");
+ }
+
+ require_once 'Horde/Template.php';
+ $news = Jonah_News::factory();
+ $params = $this->_params();
+
+ $view = isset($this->_params['view']) ? $this->_params['view'] : 'standard';
+ if (!isset($this->_params['max'])) {
+ $this->_params['max'] = $params['max']['default'];
+ }
+ if (!isset($this->_params['from'])) {
+ $this->_params['from'] = $params['from']['default'];
+ }
+
+ return $news->renderChannel($this->_params['source'], $view, $this->_params['max'], $this->_params['from']);
+ }
+
+}
--- /dev/null
+<?php
+
+$block_name = _("Most Popular Stories");
+
+/**
+ * This class extends Horde_Block:: to provide an api to embed news
+ * in other Horde applications.
+ *
+ * $Horde: jonah/lib/Block/news_popular.php,v 1.9 2009/07/09 08:18:26 slusarz Exp $
+ *
+ * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (BSD). If you
+ * did not receive this file, see http://cvs.horde.org/co.php/jonah/LICENSE.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Michael Rubinsky <mrubinsk@horde.org>
+ *
+ * @package Horde_Block
+ */
+class Horde_Block_Jonah_news_popular extends Horde_Block {
+
+ var $_app = 'jonah';
+
+ function _params()
+ {
+ require_once dirname(__FILE__) . '/../base.php';
+ require_once JONAH_BASE . '/lib/Jonah.php';
+ require_once JONAH_BASE . '/lib/News.php';
+ require JONAH_BASE . '/config/templates.php';
+
+ $params['source'] = array('name' => _("Feed"),
+ 'type' => 'enum',
+ 'values' => array());
+
+ $news = Jonah_News::factory();
+ $channels = $news->getChannels();
+ foreach ($channels as $channel) {
+ if ($channel['channel_type'] == JONAH_INTERNAL_CHANNEL) {
+ $params['source']['values'][$channel['channel_id']] = $channel['channel_name'];
+ }
+ }
+ natcasesort($params['source']['values']);
+
+ $params['view'] = array('name' => _("View"),
+ 'type' => 'enum',
+ 'values' => array());
+ foreach ($templates as $key => $template) {
+ $params['view']['values'][$key] = $template['name'];
+ }
+
+ $params['max'] = array('name' => _("Maximum Stories"),
+ 'type' => 'int',
+ 'default' => 10,
+ 'required' => false);
+
+ return $params;
+ }
+
+ function _title()
+ {
+ require_once dirname(__FILE__) . '/../base.php';
+ require_once JONAH_BASE . '/lib/Jonah.php';
+ require_once JONAH_BASE . '/lib/News.php';
+
+ $news = Jonah_News::factory();
+ $channel = $news->getChannel($this->_params['source']);
+ if (is_a($channel, 'PEAR_Error')) {
+ return @htmlspecialchars($channel->getMessage(), ENT_COMPAT, Horde_Nls::getCharset());
+ }
+
+ if (!empty($channel['channel_link'])) {
+ $title = Horde::link(htmlspecialchars($channel['channel_link']), '', '', '_blank')
+ . @htmlspecialchars($channel['channel_name'], ENT_COMPAT, Horde_Nls::getCharset())
+ . _(" - Most read stories") . '</a>';
+ } else {
+ $title = @htmlspecialchars($channel['channel_name'], ENT_COMPAT, Horde_Nls::getCharset())
+ . _(" - Most read stories");
+ }
+
+ return $title;
+ }
+
+ function _content()
+ {
+ require_once dirname(__FILE__) . '/../base.php';
+ require_once JONAH_BASE . '/lib/Jonah.php';
+ require_once JONAH_BASE . '/lib/News.php';
+
+ if (empty($this->_params['source'])) {
+ return _("No feed specified.");
+ }
+
+ require_once 'Horde/Template.php';
+ $news = Jonah_News::factory();
+ $params = $this->_params();
+
+ $view = isset($this->_params['view']) ? $this->_params['view'] : 'standard';
+ if (!isset($this->_params['max'])) {
+ $this->_params['max'] = $params['max']['default'];
+ }
+
+
+ return $news->renderChannel($this->_params['source'], $view, $this->_params['max'], 0, JONAH_ORDER_READ);
+ }
+
+}
--- /dev/null
+<?php
+
+$block_name = _("Story");
+
+/**
+ * This class extends Horde_Block:: to provide the api to embed news
+ * in other Horde applications.
+ *
+ * $Horde: jonah/lib/Block/story.php,v 1.11 2009/07/09 08:18:26 slusarz Exp $
+ *
+ * Copyright 2002-2007 Roel Gloudemans <roel@gloudemans.info>
+ *
+ * See the enclosed file LICENSE for license information (BSD). If you
+ * did not receive this file, see http://cvs.horde.org/co.php/jonah/LICENSE.
+ *
+ * @author Roel Gloudemans <roel@gloudemans.info>
+ * @package Horde_Block
+ */
+class Horde_Block_Jonah_story extends Horde_Block {
+
+ var $_app = 'jonah';
+
+ var $_story = null;
+
+ /**
+ */
+ function _params()
+ {
+ require_once dirname(__FILE__) . '/../base.php';
+ require_once JONAH_BASE . '/lib/Jonah.php';
+ require_once JONAH_BASE . '/lib/News.php';
+
+ $news = Jonah_News::factory();
+ $channels = $news->getChannels(JONAH_INTERNAL_CHANNEL);
+ $channel_choices = array();
+ foreach ($channels as $channel) {
+ $channel_choices[$channel['channel_id']] = $channel['channel_name'];
+ }
+ natcasesort($channel_choices);
+
+ return array('source' => array(
+ 'name' => _("Feed"),
+ 'type' => 'enum',
+ 'values' => $channel_choices),
+ 'story' => array(
+ 'name' => _("Story"),
+ 'type' => 'int'),
+ 'countReads' => array(
+ 'name' => _("Count reads of this story when this block is displayed"),
+ 'type' => 'boolean',
+ 'default' => false),
+ );
+ }
+
+ /**
+ */
+ function _title()
+ {
+ require_once dirname(__FILE__) . '/../base.php';
+ require_once JONAH_BASE . '/lib/Jonah.php';
+ require_once JONAH_BASE . '/lib/News.php';
+
+ if (empty($this->_params['source']) ||
+ empty($this->_params['story'])) {
+ return _("Story");
+ }
+
+ $story = $this->_fetch();
+ return is_a($story, 'PEAR_Error')
+ ? @htmlspecialchars($story->getMessage(), ENT_COMPAT, Horde_Nls::getCharset())
+ : '<span class="storyDate">'
+ . @htmlspecialchars($story['story_updated_date'], ENT_COMPAT, Horde_Nls::getCharset())
+ . '</span> '
+ . @htmlspecialchars($story['story_title'], ENT_COMPAT, Horde_Nls::getCharset());
+ }
+
+ /**
+ */
+ function _content()
+ {
+ require_once dirname(__FILE__) . '/../base.php';
+ require_once JONAH_BASE . '/lib/Jonah.php';
+ require_once JONAH_BASE . '/lib/News.php';
+
+ if (empty($this->_params['source']) || empty($this->_params['story'])) {
+ return _("No story is selected.");
+ }
+
+ $story = $this->_fetch();
+ if (is_a($story, 'PEAR_Error')) {
+ return sprintf(_("Error fetching story: %s"), $story->getMessage());
+ }
+
+ if (empty($story['story_body_type']) || $story['story_body_type'] == 'text') {
+ $story['story_body'] = Horde_Text_Filter::filter($story['story_body'], 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::MICRO, 'class' => null));
+ }
+
+ $tag_html = array();
+ foreach ($story['story_tags'] as $id => $tag) {
+ $link = Horde_Util::addParameter('results.php', array('tag_id' => $id, 'channel_id' => $this->_params['source']));
+ $tag_html[] = Horde::link($link) . $tag . '</a>';
+ }
+
+ return '<p class="storyTags">' . _("Tags: ")
+ . implode(', ', $story['story_tags'])
+ . '</p><p class="storySubtitle">'
+ . htmlspecialchars($story['story_desc'])
+ . '</p><div class="storyBody">' . $story['story_body']
+ . '</div>';
+ }
+
+ /**
+ * Get the story the block is configured for.
+ */
+ function _fetch()
+ {
+ if (is_null($this->_story)) {
+ $news = Jonah_News::factory();
+ $this->_story = $news->getStory($this->_params['source'],
+ $this->_params['story'],
+ $this->_params['countReads']);
+ }
+
+ return $this->_story;
+ }
+
+}
--- /dev/null
+<?php
+
+$block_name = _("Menu List");
+$block_type = 'tree';
+
+/**
+ * $Horde: jonah/lib/Block/tree_menu.php,v 1.7 2009/12/03 15:28:22 chuck Exp $
+ *
+ * @package Horde_Block
+ */
+class Horde_Block_jonah_tree_menu extends Horde_Block {
+
+ var $_app = 'jonah';
+
+ function _buildTree(&$tree, $indent = 0, $parent = null)
+ {
+ require_once dirname(__FILE__) . '/../base.php';
+ require_once JONAH_BASE . '/lib/News.php';
+
+ if (!Jonah::checkPermissions('jonah:news', Horde_Perms::EDIT) ||
+ !in_array('internal', $GLOBALS['conf']['news']['enable'])) {
+ return;
+ }
+
+ $url = Horde::applicationUrl('stories/');
+ $icondir = $GLOBALS['registry']->getImageDir();
+ $news = Jonah_News::factory();
+ $channels = $news->getChannels('internal');
+ if (is_a($channels, 'PEAR_Error')) {
+ return;
+ }
+ $channels = Jonah::checkPermissions('channels', Horde_Perms::SHOW, $channels);
+
+ foreach ($channels as $channel) {
+ $tree->addNode($parent . $channel['channel_id'],
+ $parent,
+ $channel['channel_name'],
+ $indent + 1,
+ false,
+ array('icon' => 'editstory.png',
+ 'icondir' => $icondir,
+ 'url' => Horde_Util::addParameter($url, array('channel_id' => $channel['channel_id']))));
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * @package Jonah
+ */
+
+/** Horde_Array */
+require_once 'Horde/Array.php';
+
+/**
+ * Jonah_Driver:: is responsible for storing, searching, sorting and filtering
+ * locally generated and managed articles. Aggregation is left to Hippo.
+ *
+ * $Horde: jonah/lib/Driver.php,v 1.7 2009/07/09 08:18:26 slusarz Exp $
+ *
+ * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (BSD). If you did not
+ * did not receive this file, see http://cvs.horde.org/co.php/jonah/LICENSE.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Marko Djukic <marko@oblo.com>
+ * @author Jan Schneider <jan@horde.org>
+ * @author Ben Klang <ben@alkaloid.net>
+ * @package Jonah
+ */
+class Jonah_Driver {
+
+ /**
+ * Hash containing connection parameters.
+ *
+ * @var array
+ */
+ var $_params = array();
+
+ /**
+ * Constructs a new Driver storage object.
+ *
+ * @param array $params A hash containing connection parameters.
+ */
+ function Jonah_Driver($params = array())
+ {
+ $this->_params = $params;
+ }
+
+ /**
+ */
+ function deleteChannel(&$info)
+ {
+ return $this->_deleteChannel($info['channel_id']);
+ }
+
+ /**
+ * Fetches the requested channel, while actually passing on the request to
+ * the backend _getChannel() function to do the real work.
+ *
+ * @param integer $channel_id The channel id to fetch.
+ *
+ * @return array|PEAR_Error The channel details as an array or a
+ * PEAR_Error if not valid or not found.
+ */
+ function getChannel($channel_id)
+ {
+ static $channel = array();
+
+ /* We need a non empty channel id. */
+ if (empty($channel_id)) {
+ return PEAR::raiseError(_("Missing channel id."));
+ }
+
+ /* Cache the fetching of channels. */
+ if (!isset($channel[$channel_id])) {
+ $channel[$channel_id] = $this->_getChannel($channel_id);
+ if (!is_a($channel[$channel_id], 'PEAR_Error')) {
+ if (empty($channel[$channel_id]['channel_link'])) {
+ $channel[$channel_id]['channel_official'] = Horde_Util::addParameter(Horde::applicationUrl('delivery/html.php', true, -1), 'channel_id', $channel_id, false);
+ } else {
+ $channel[$channel_id]['channel_official'] = str_replace(array('%25c', '%c'), array('%c', $channel_id), $channel[$channel_id]['channel_link']);
+ }
+ }
+ }
+
+ return $channel[$channel_id];
+ }
+
+ /**
+ * Returns the most recent or all stories from a channel.
+ *
+ * @param integer $criteria An associative array of attributes on which
+ * the resulting stories should be filtered.
+ * Examples:
+ * 'channel' => string Channel slug
+ * 'channel_id' => int Channel ID
+ * 'author' => string Story author
+ * 'updated-min' => Horde_Date Only return
+ * stories updated
+ * on or after this
+ * date
+ * 'updated-max' => Horde_Date Only return
+ * stories updated
+ * on or before this
+ * date
+ * 'published-min' => Horde_Date Only return
+ * stories
+ * published on or
+ * after this date
+ * 'published-max' => Horde_Date Only return
+ * stories
+ * published on or
+ * before date
+ * 'published-max' => Horde_Date Only return
+ * on or before this
+ * date
+ * 'tags' => array Array of tag names ANY of
+ * which may match the story to
+ * be included
+ * 'alltags' => array Array of tag names ALL of
+ * which must be associated
+ * with the story to be
+ * included
+ * 'keywords' => array Array of strings ALL of
+ * which matching must
+ * include
+ * 'published' => boolean Whether to return only
+ * published stories;
+ * null will return both
+ * published and
+ * unpublished
+ * 'startnumber' => int Story number to begin
+ * 'endnumber' => int Story number to end
+ * 'limit' => int Max number of stories
+ *
+ * @param integer $order How to order the results for internal
+ * channels. Possible values are the
+ * JONAH_ORDER_* constants.
+ *
+ * @return array The specified number (or less, if there are fewer) of
+ * stories from the given channel.
+ */
+ function getStories($criteria)
+ {
+ // Convert a channel slug into a channel ID if necessary
+ if (isset($criteria['channel']) && !isset($criteria['channel_id'])) {
+ $criteria['channel_id'] = $this->getIdBySlug($criteria['channel']);
+ }
+
+
+ // Validate that we have proper Horde_Date objects
+ if (isset($criteria['updated-min'])) {
+ if (!is_a($criteria['updated-min'], 'Horde_Date')) {
+ throw new Exception("Invalid date object provided for update start date.");
+ }
+ }
+ if (isset($criteria['updated-max'])) {
+ if (!is_a($criteria['updated-max'], 'Horde_Date')) {
+ throw new Exception("Invalid date object provided for update end date.");
+ }
+ }
+ if (isset($criteria['published-min'])) {
+ if (!is_a($criteria['published-min'], 'Horde_Date')) {
+ throw new Exception("Invalid date object provided for published start date.");
+ }
+ }
+ if (isset($criteria['published-max'])) {
+ if (!is_a($criteria['published-max'], 'Horde_Date')) {
+ throw new Exception("Invalid date object provided for published end date.");
+ }
+ }
+
+ // Collect the applicable tag IDs
+ $criteria['tagIDs'] = array();
+ if (isset($criteria['tags'])) {
+ $criteria['tagIDs'] = array_merge($criteria['tagIDs'], $this->getTagIds($criteria['tags']));
+ }
+ if (isset($criteria['alltags'])) {
+ $criteria['tagIDs'] = array_merge($criteria['tagIDs'], $this->getTagIds($criteria['alltags']));
+ }
+
+ return $this->_getStories($criteria);
+ }
+
+ /**
+ * Returns the most recent or all stories from a channel.
+ * This method is deprecated.
+ *
+ * @param integer $channel_id The news channel to get stories from.
+ * @param integer $max The maximum number of stories to get. If
+ * null, all stories will be returned.
+ * @param integer $from The number of the story to start with.
+ * @param boolean $refresh Force a refresh of stories in case this is
+ * an external channel.
+ * @param integer $date The timestamp of the date to start with.
+ * @param boolean $unreleased Return stories that have not yet been
+ * published?
+ * Defaults to false - only published stories.
+ * @param integer $order How to order the results for internal
+ * channels. Possible values are the
+ * JONAH_ORDER_* constants.
+ *
+ * @return array The specified number (or less, if there are fewer) of
+ * stories from the given channel.
+ */
+ function legacyGetStories($channel, $max = 10, $from = 0, $refresh = false,
+ $date = null, $unreleased = false,
+ $order = JONAH_ORDER_PUBLISHED)
+ {
+ global $conf, $registry;
+
+ $channel['channel_link'] = Horde_Util::addParameter(Horde::applicationUrl('delivery/html.php', true, -1), 'channel_id', $channel['channel_id']);
+ $stories = $this->_legacyGetStories($channel['channel_id'], $max, $from, $date, $unreleased, $order);
+ if (is_a($stories, 'PEAR_Error')) {
+ return $stories;
+ }
+ $date_format = $GLOBALS['prefs']->getValue('date_format');
+ $comments = $conf['comments']['allow'] && $registry->hasMethod('forums/numMessages');
+ foreach ($stories as $key => $story) {
+ $stories[$key]['story_link'] = $this->getStoryLink($channel, $story);
+ $stories[$key]['story_updated'] = $story['story_updated'];
+ $stories[$key]['story_updated_date'] = strftime($date_format, $story['story_updated']);
+ if ($comments) {
+ $stories[$key]['num_comments'] = $registry->call('forums/numMessages', array($story['story_id'], $registry->getApp()));
+ if (is_a($stories[$key]['num_comments'], 'PEAR_Error')) {
+ $stories[$key]['num_comments'] = null;
+ }
+ }
+ $stories[$key] = array_merge($channel, $stories[$key]);
+ }
+
+ return $stories;
+ }
+
+ /**
+ */
+ function _escapeExternalStories(&$story, $key, $channel)
+ {
+ $story = array_merge($channel, $story);
+ $story['story_link'] = Horde::externalUrl($story['story_url']);
+ }
+
+ /**
+ */
+ function saveStory(&$info)
+ {
+ /* Used for checking whether to send out delivery or not. */
+ if (empty($info['story_published'])) {
+ /* Story is not being released. */
+ $deliver = false;
+ } elseif (empty($info['story_id'])) {
+ /* Story is new. */
+ $deliver = true;
+ } else {
+ /* Story is old, has it been released already? */
+ $oldstory = $this->getStory(null, $info['story_id']);
+ if ((empty($oldstory['story_published']) ||
+ $oldstory['story_published'] > $oldstory['story_updated']) &&
+ $info['story_published'] <= time()) {
+ $deliver = true;
+ } else {
+ $deliver = false;
+ }
+ }
+
+ /* First save to the backend. */
+ $result = $this->_saveStory($info);
+ if (is_a($result, 'PEAR_Error') || !$deliver) {
+ /* Return here also if editing, do not bother doing deliveries for
+ * an edited story. */
+ return $result;
+ }
+ }
+
+ /**
+ */
+ function getStory($channel_id, $story_id, $read = false)
+ {
+ $channel = null;
+ if ($channel_id) {
+ $channel = $this->getChannel($channel_id);
+ if (is_a($channel, 'PEAR_Error')) {
+ return $channel;
+ }
+ if ($channel['channel_type'] == JONAH_EXTERNAL_CHANNEL) {
+ return $this->_getExternalStory($channel, $story_id);
+ }
+ }
+
+ $story = $this->_getStory($story_id, $read);
+ if (is_a($story, 'PEAR_Error')) {
+ return $story;
+ }
+
+ /* Format story link. */
+ $story['story_link'] = $this->getStoryLink($channel, $story);
+
+ /* Format dates. */
+ $date_format = $GLOBALS['prefs']->getValue('date_format');
+ $story['story_updated_date'] = strftime($date_format, $story['story_updated']);
+ if (!empty($story['story_published'])) {
+ $story['story_published_date'] = strftime($date_format, $story['story_published']);
+ }
+
+ return $story;
+ }
+
+ /**
+ * Returns the official link to a story.
+ *
+ * @param array $channel A channel hash.
+ * @param array $story A story hash.
+ *
+ * @return string The story link.
+ */
+ function getStoryLink($channel, $story)
+ {
+ if ((empty($story['story_url']) || !empty($story['story_body'])) &&
+ !empty($channel['channel_story_url'])) {
+ $url = $channel['channel_story_url'];
+ } else {
+ $url = Horde_Util::addParameter(Horde::applicationUrl('stories/view.php', true, -1), array('channel_id' => '%c', 'story_id' => '%s'), null, false);
+ }
+ return str_replace(array('%25c', '%25s', '%c', '%s'),
+ array('%c', '%s', $channel['channel_id'], $story['story_id']),
+ $url);
+ }
+
+ /**
+ */
+ function getChecksum($story)
+ {
+ return md5($story['story_title'] . $story['story_desc']);
+ }
+
+ /**
+ */
+ function getIntervalLabel($seconds = null)
+ {
+ $interval = array(1 => _("none"),
+ 1800 => _("30 mins"),
+ 3600 => _("1 hour"),
+ 7200 => _("2 hours"),
+ 14400 => _("4 hours"),
+ 28800 => _("8 hours"),
+ 43200 => _("12 hours"),
+ 86400 => _("24 hours"));
+
+ if ($seconds === null) {
+ return $interval;
+ } else {
+ return $interval[$seconds];
+ }
+ }
+
+ /**
+ * Returns the stories of a channel rendered with the specified template.
+ *
+ * @param integer $channel_id The news channel to get stories from.
+ * @param string $tpl The name of the template to use.
+ * @param integer $max The maximum number of stories to get. If
+ * null, all stories will be returned.
+ * @param integer $from The number of the story to start with.
+ * @param integer $order How to sort the results for internal channels
+ * Possible values are the JONAH_ORDER_*
+ * constants.
+ *
+ * @return string The rendered story listing.
+ */
+ function renderChannel($channel_id, $tpl, $max = 10, $from = 0, $order = JONAH_ORDER_PUBLISHED)
+ {
+ $channel = $this->getChannel($channel_id);
+ if (is_a($channel, 'PEAR_Error')) {
+ return sprintf(_("Error fetching feed: %s"), $channel->getMessage());
+ }
+
+ include JONAH_BASE . '/config/templates.php';
+ $escape = !isset($templates[$tpl]['escape']) ||
+ !empty($templates[$tpl]['escape']);
+ $template = new Horde_Template();
+
+ if ($escape) {
+ $channel['channel_name'] = htmlspecialchars($channel['channel_name']);
+ $channel['channel_desc'] = htmlspecialchars($channel['channel_desc']);
+ }
+ $template->set('channel', $channel, true);
+
+ /* Get one story more than requested to see if there are more
+ * stories. */
+ if ($max !== null) {
+ $stories = $this->getStories($channel_id, $max + 1, $from, false, time(), false, $order);
+ if (is_a($stories, 'PEAR_Error')) {
+ return $stories->getMessage();
+ }
+ } else {
+ $stories = $this->getStories($channel_id, null, 0, false, time(), false, $order);
+ if (is_a($stories, 'PEAR_Error')) {
+ return $stories->getMessage();
+ }
+ $max = count($stories);
+ }
+
+ if (!$stories) {
+ $template->set('error', _("No stories are currently available."), true);
+ $template->set('stories', false, true);
+ $template->set('image', false, true);
+ $template->set('form', false, true);
+ } else {
+ /* Escape. */
+ if ($escape) {
+ array_walk($stories, array($this, '_escapeStories'));
+ }
+
+ /* Process story summaries. */
+ array_walk($stories, array($this, '_escapeStoryDescriptions'));
+
+ $template->set('error', false, true);
+ $template->set('story_marker', Horde::img('story_marker.png'));
+ $template->set('image', false, true);
+ $template->set('form', false, true);
+ if ($from) {
+ $template->set('previous', max(0, $from - $max), true);
+ } else {
+ $template->set('previous', false, true);
+ }
+ if ($from && !empty($channel['channel_page_link'])) {
+ $template->set('previous_link',
+ str_replace(
+ array('%25c', '%25n', '%c', '%n'),
+ array('%c', '%n', $channel['channel_id'], max(0, $from - $max)),
+ $channel['channel_page_link']),
+ true);
+ } else {
+ $template->set('previous_link', false, true);
+ }
+ $more = count($stories) > $max;
+ if ($more) {
+ $template->set('next', $from + $max, true);
+ array_pop($stories);
+ } else {
+ $template->set('next', false, true);
+ }
+ if ($more && !empty($channel['channel_page_link'])) {
+ $template->set('next_link',
+ str_replace(
+ array('%25c', '%25n', '%c', '%n'),
+ array('%c', '%n', $channel['channel_id'], $from + $max),
+ $channel['channel_page_link']),
+ true);
+ } else {
+ $template->set('next_link', false, true);
+ }
+
+ $template->set('stories', $stories, true);
+ }
+
+ return $template->parse($templates[$tpl]['template']);
+ }
+
+ /**
+ */
+ function _escapeStories(&$value, $key)
+ {
+ $value['story_title'] = htmlspecialchars($value['story_title']);
+ $value['story_desc'] = htmlspecialchars($value['story_desc']);
+ if (isset($value['story_link'])) {
+ $value['story_link'] = htmlspecialchars($value['story_link']);
+ }
+ if (empty($value['story_body_type']) || $value['story_body_type'] != 'richtext') {
+ $value['story_body'] = htmlspecialchars($value['story_body']);
+ }
+ }
+
+ /**
+ */
+ function _escapeStoryDescriptions(&$value, $key)
+ {
+ $value['story_desc'] = nl2br($value['story_desc']);
+ }
+
+ /**
+ * Returns the provided story as a MIME part.
+ *
+ * @param array $story A data array representing a story.
+ *
+ * @return MIME_Part The MIME message part containing the story parts.
+ */
+ function &getStoryAsMessage(&$story)
+ {
+ require_once 'Horde/MIME/Part.php';
+
+ /* Add the story to the message based on the story's body type. */
+ switch ($story['story_body_type']) {
+ case 'richtext':
+ /* Get a plain text version of a richtext story. */
+ $body_html = $story['story_body'];
+ $body_text = Horde_Text_Filter::filter($body_html, 'html2text');
+
+ /* Add description. */
+ $body_html = '<p>' . Horde_Text_Filter::filter($story['story_desc'], 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::MICRO, 'charset' => Horde_Nls::getCharset(), 'class' => null, 'callback' => null)) . "</p>\n" . $body_html;
+ $body_text = Horde_String::wrap(' ' . $story['story_desc'], 70) . "\n\n" . $body_text;
+
+ /* Add the text version of the story to the base message. */
+ $message_text = new MIME_Part('text/plain');
+ $message_text->setCharset(Horde_Nls::getCharset());
+ $message_text->setContents($message_text->replaceEOL($body_text));
+ $message_text->setDescription(_("Plaintext Version of Story"));
+
+ /* Add an HTML version of the story to the base message. */
+ $message_html = new MIME_Part('text/html', Horde_String::wrap($body_html),
+ Horde_Nls::getCharset(), 'inline');
+ $message_html->setDescription(_("HTML Version of Story"));
+
+ /* Add the two parts as multipart/alternative. */
+ $basepart = new MIME_Part('multipart/alternative');
+ $basepart->addPart($message_text);
+ $basepart->addPart($message_html);
+
+ return $basepart;
+
+ case 'text':
+ /* This is just a plain text story. */
+ $message_text = new MIME_Part('text/plain');
+ $message_text->setContents($message_text->replaceEOL($story['story_desc'] . "\n\n" . $story['story_body']));
+ $message_text->setCharset(Horde_Nls::getCharset());
+
+ return $message_text;
+ }
+ }
+
+ /**
+ * Attempts to return a concrete Jonah_Driver instance based on $driver.
+ *
+ * @param string $driver The type of concrete Jonah_Driver subclass to
+ * return. The is based on the storage driver
+ * ($driver). The code is dynamically included.
+ *
+ * @param array $params A hash containing any additional configuration or
+ * connection parameters a subclass might need.
+ *
+ * @return mixed The newly created concrete Jonah_Driver instance, or false
+ * on an error.
+ */
+ function factory($driver = null, $params = null)
+ {
+ if ($driver === null) {
+ $driver = $GLOBALS['conf']['news']['storage']['driver'];
+ }
+ $driver = basename($driver);
+
+ if ($params === null) {
+ $params = Horde::getDriverConfig(array('news', 'storage'), $driver);
+ }
+
+ $class = 'Jonah_Driver_' . $driver;
+ if (!class_exists($class, false)) {
+ include dirname(__FILE__) . '/Driver/' . $driver . '.php';
+ }
+ if (class_exists($class)) {
+ return new $class($params);
+ } else {
+ return PEAR::raiseError(sprintf(_("No such backend \"%s\" found"), $driver));
+ }
+ }
+
+ /**
+ * Stubs for the tag functions. If supported by the backend, these need
+ * to be implemented in the concrete Jonah_Driver_* class.
+ */
+ function writeTags($resource_id, $channel_id, $tags)
+ {
+ return PEAR::raiseError(_("Tag support not enabled in backend."));
+ }
+
+ function readTags($resource_id)
+ {
+ return PEAR::raiseError(_("Tag support not enabled in backend."));
+ }
+
+ function listTagInfo($tags = array(), $channel_id = null)
+ {
+ return PEAR::raiseError(_("Tag support not enabled in backend."));
+ }
+
+ function searchTagsById($ids, $max = 10, $from = 0, $channel_id = array(),
+ $order = JONAH_ORDER_PUBLISHED)
+ {
+ return PEAR::raiseError(_("Tag support not enabled in backend."));
+ }
+
+ function getTagNames($ids)
+ {
+ return PEAR::raiseError(_("Tag support not enabled in backend."));
+ }
+
+ function getIdBySlug($channel)
+ {
+ return $this->_getIdBySlug($channel);
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Jonah storage implementation for PHP's PEAR database abstraction layer.
+ *
+ * Required values for $params:<pre>
+ * 'phptype' The database type (e.g. 'pgsql', 'mysql', etc.).
+ * 'charset' The database's internal charset.</pre>
+ *
+ * Required by some database implementations:<pre>
+ * 'hostspec' The hostname of the database server.
+ * 'protocol' The communication protocol ('tcp', 'unix', etc.).
+ * 'database' The name of the database.
+ * 'username' The username with which to connect to the database.
+ * 'password' The password associated with 'username'.
+ * 'options' Additional options to pass to the database.
+ * 'tty' The TTY on which to connect to the database.
+ * 'port' The port on which to connect to the database.</pre>
+ *
+ * The table structure can be created by the scripts/db/jonah_news.sql
+ * script. The needed tables are jonah_channels and jonah_stories.
+ *
+ * $Horde: jonah/lib/Driver/sql.php,v 1.10 2010/02/01 10:32:04 jan Exp $
+ *
+ * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (BSD). If you did not
+ * did not receive this file, see http://cvs.horde.org/co.php/jonah/LICENSE.
+ *
+ * @author Marko Djukic <marko@oblo.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Jan Schneider <jan@horde.org>
+ * @author Ben Klang <ben@alkaloid.net>
+ * @package Jonah
+ */
+class Jonah_Driver_sql extends Jonah_Driver {
+
+ /**
+ * Handle for the current database connection.
+ *
+ * @var DB
+ */
+ var $_db;
+
+ /**
+ * Boolean indicating whether or not we're connected to the SQL server.
+ *
+ * @var boolean
+ */
+ var $_connected = false;
+
+ /**
+ * Saves a channel to the backend.
+ *
+ * @param array $info The channel to add.
+ * Must contain a combination of the following
+ * entries:
+ * <pre>
+ * 'channel_id' If empty a new channel is being added, otherwise one
+ * is being edited.
+ * 'channel_name' The headline.
+ * 'channel_desc' A description of this channel.
+ * 'channel_type' Whether internal or external.
+ * 'channel_interval' If external then interval at which to refresh.
+ * 'channel_link' The link to the source.
+ * 'channel_url' The url from where to fetch the story list.
+ * 'channel_image' A channel image.
+ * </pre>
+ *
+ * @return int|PEAR_Error The channel ID on success, PEAR_Error on
+ * failure.
+ */
+ function saveChannel(&$info)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ if (empty($info['channel_id'])) {
+ $info['channel_id'] = $this->_db->nextId('jonah_channels');
+ if (is_a($info['channel_id'], 'PEAR_Error')) {
+ Horde::logMessage($info['channel_id'], __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $info['channel_id'];
+ }
+ $sql = 'INSERT INTO jonah_channels' .
+ ' (channel_id, channel_name, channel_type, channel_desc, channel_interval, channel_url, channel_link, channel_page_link, channel_story_url, channel_img)' .
+ ' VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
+ $values = array();
+ } else {
+ $sql = 'UPDATE jonah_channels' .
+ ' SET channel_id = ?, channel_name = ?, channel_type = ?, channel_desc = ?, channel_interval = ?, channel_url = ?, channel_link = ?, channel_page_link = ?, channel_story_url = ?, channel_img = ?' .
+ ' WHERE channel_id = ?';
+ $values = array((int)$info['channel_id']);
+ }
+
+ array_unshift($values,
+ (int)$info['channel_id'],
+ Horde_String::convertCharset($info['channel_name'], Horde_Nls::getCharset(), $this->_params['charset']),
+ (int)$info['channel_type'],
+ isset($info['channel_desc']) ? $info['channel_desc'] : null,
+ isset($info['channel_interval']) ? (int)$info['channel_interval'] : null,
+ isset($info['channel_url']) ? $info['channel_url'] : null,
+ isset($info['channel_link']) ? $info['channel_link'] : null,
+ isset($info['channel_page_link']) ? $info['channel_page_link'] : null,
+ isset($info['channel_story_url']) ? $info['channel_story_url'] : null,
+ isset($info['channel_img']) ? $info['channel_img'] : null);
+ Horde::logMessage('SQL Query by Jonah_Driver_sql::saveChannel(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->query($sql, $values);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ }
+
+ return $info['channel_id'];
+ }
+
+ /**
+ * Get a list of stored channels.
+ *
+ * @param integer $type The type of channel to filter for. Possible
+ * values are either JONAH_INTERNAL_CHANNEL
+ * to fetch only a list of internal channels,
+ * or JONAH_EXTERNAL_CHANNEL for only external.
+ * If null both channel types are returned.
+ *
+ * @return mixed An array of channels or PEAR_Error on error.
+ */
+ function getChannels($type = null)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ $wsql = '';
+ if (!is_null($type)) {
+ if (!is_array($type)) {
+ $type = array($type);
+ }
+ for ($i = 0, $i_max = count($type); $i < $i_max; ++$i) {
+ $type[$i] = 'channel_type = ' . (int)$type[$i];
+ }
+ $wsql = 'WHERE ' . implode(' OR ', $type);
+ }
+
+ $sql = sprintf('SELECT channel_id, channel_name, channel_type, channel_updated FROM jonah_channels %s ORDER BY channel_name', $wsql);
+
+ Horde::logMessage('SQL Query by Jonah_Driver_sql::getChannels(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->getAll($sql, DB_FETCHMODE_ASSOC);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ }
+ for ($i = 0; $i < count($result); $i++) {
+ $result[$i]['channel_name'] = Horde_String::convertCharset($result[$i]['channel_name'], $this->_params['charset']);
+ }
+
+ return $result;
+ }
+
+ /**
+ */
+ function _getChannel($channel_id)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ $sql = 'SELECT * FROM jonah_channels WHERE channel_id = ' . (int)$channel_id;
+
+ Horde::logMessage('SQL Query by Jonah_Driver_sql::_getChannel(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->getRow($sql, DB_FETCHMODE_ASSOC);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ } elseif (empty($result)) {
+ return PEAR::raiseError(sprintf(_("Channel id \"%s\" not found."), $channel_id));
+ }
+
+ $result['channel_name'] = Horde_String::convertCharset($result['channel_name'], $this->_params['charset']);
+ if ($result['channel_type'] == JONAH_COMPOSITE_CHANNEL) {
+ $channels = explode(':', $result['channel_url']);
+ if (count($channels)) {
+ $sql = 'SELECT MAX(channel_updated) FROM jonah_channels WHERE channel_id IN (' . implode(',', $channels) . ')';
+ Horde::logMessage('SQL Query by Jonah_Driver_sql::_getChannel(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $updated = $this->_db->getOne($sql);
+ if (is_a($updated, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ } else {
+ $result['channel_updated'] = $updated;
+ $this->_timestampChannel($channel_id, $updated);
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ */
+ function _timestampChannel($channel_id, $timestamp)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ $sql = sprintf('UPDATE jonah_channels SET channel_updated = %s WHERE channel_id = %s',
+ (int)$timestamp,
+ (int)$channel_id);
+ Horde::logMessage('SQL Query by Jonah_Driver_sql::_timestampChannel(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->query($sql);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ }
+ return $result;
+ }
+
+ /**
+ */
+ function _readStory($story_id)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ $sql = 'UPDATE jonah_stories SET story_read = story_read + 1 WHERE story_id = ' . (int)$story_id;
+ Horde::logMessage('SQL Query by Jonah_Driver_sql::_readStory(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->query($sql);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ }
+ return $result;
+ }
+
+ /**
+ */
+ function _deleteChannel($channel_id)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ $sql = 'DELETE FROM jonah_channels WHERE channel_id = ?';
+ $values = array($channel_id);
+
+ Horde::logMessage('SQL Query by Jonah_Driver_sql::deleteChannel(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->query($sql, $values);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param array &$info
+ */
+ function _saveStory(&$info)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ if (empty($info['story_id'])) {
+ $info['story_id'] = $this->_db->nextId('jonah_stories');
+ if (is_a($info['story_id'], 'PEAR_Error')) {
+ Horde::logMessage($info['story_id'], __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $info['story_id'];
+ }
+ $channel = $this->getChannel($info['channel_id']);
+ $permalink = $this->getStoryLink($channel, $info);
+ $sql = 'INSERT INTO jonah_stories (story_id, channel_id, story_title, story_desc, story_body_type, story_body, story_url, story_published, story_updated, story_read, story_permalink) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
+ $values = array($permalink);
+ } else {
+ $sql = 'UPDATE jonah_stories SET story_id = ?, channel_id = ?, story_title = ?, story_desc = ?, story_body_type = ?, story_body = ?, story_url = ?, story_published = ?, story_updated = ?, story_read = ? WHERE story_id = ?';
+ $values = array((int)$info['story_id']);
+ }
+
+ if (empty($info['story_read'])) {
+ $info['story_read'] = 0;
+ }
+
+ /* Deal with any tags */
+ if (!empty($info['story_tags'])) {
+ $tags = explode(',', $info['story_tags']);
+ } else {
+ $tags = array();
+ }
+ $this->writeTags($info['story_id'], $info['channel_id'], $tags);
+
+ array_unshift($values,
+ (int)$info['story_id'],
+ (int)$info['channel_id'],
+ Horde_String::convertCharset($info['story_title'], Horde_Nls::getCharset(), $this->_params['charset']),
+ Horde_String::convertCharset($info['story_desc'], Horde_Nls::getCharset(), $this->_params['charset']),
+ $info['story_body_type'],
+ isset($info['story_body']) ? Horde_String::convertCharset($info['story_body'], Horde_Nls::getCharset(), $this->_params['charset']) : null,
+ isset($info['story_url']) ? $info['story_url'] : null,
+ isset($info['story_published']) ? (int)$info['story_published'] : null,
+ time(),
+ (int)$info['story_read']);
+
+ Horde::logMessage('SQL Query by Jonah_Driver_sql::_saveStory(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->query($sql, $values);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ }
+
+ $this->_timestampChannel($info['channel_id'], time());
+ return true;
+ }
+
+ /**
+ * Converts the text fields of a story from the backend charset to the
+ * output charset.
+ *
+ * @param array $story A story hash.
+ *
+ * @return array The converted hash.
+ */
+ function _convertFromBackend($story)
+ {
+ $story['story_title'] = Horde_String::convertCharset($story['story_title'], $this->_params['charset'], Horde_Nls::getCharset());
+ $story['story_desc'] = Horde_String::convertCharset($story['story_desc'], $this->_params['charset'], Horde_Nls::getCharset());
+ if (isset($story['story_body'])) {
+ $story['story_body'] = Horde_String::convertCharset($story['story_body'], $this->_params['charset'], Horde_Nls::getCharset());
+ }
+ if (isset($story['story_tags'])) {
+ $story['story_tags'] = Horde_String::convertCharset($story['story_tags'], $this->_params['charset'], Horde_Nls::getCharset());
+ }
+ return $story;
+ }
+
+ /**
+ * Look up a channel ID by its name
+ *
+ * @param string $channel
+ *
+ * @return int Channel ID
+ */
+ function getChannelId($channel)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ $sql = 'SELECT channel_id FROM jonah_channels WHERE channel_slug = ?';
+ $values = array($channel);
+ $result = $this->_db->getOne($sql, $values);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Returns the total number of stories in the specified
+ * channel
+ *
+ * @param int $channel_id The Channel ID
+ *
+ * @return mixed The count || PEAR_Error
+ */
+ function getStoryCount($channel_id)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ $sql = 'SELECT count(*) FROM jonah_stories WHERE channel_id = ?';
+ $result = $this->_db->getOne($sql, $channel_id);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+
+ return (int)$result;
+ }
+
+ /**
+ * Returns a list of stories from the storage backend filtered by
+ * arbitrary criteria.
+ * NOTE: $criteria['channel_id'] MUST be set for this method to work.
+ *
+ * @param string $channel
+ * @param array $criteria
+ *
+ * @return array
+ *
+ * @see Jonah_Driver#getStories
+ */
+ function _getStories($criteria)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ $sql = 'SELECT DISTINCT(tags.story_id) AS id, ' .
+ 'stories.story_author AS author, ' .
+ 'stories.story_title AS title, ' .
+ 'stories.story_desc AS description, ' .
+ 'stories.story_body_type AS body_type, ' .
+ 'stories.story_body AS body, ' .
+ 'stories.story_url AS url, ' .
+ 'stories.story_permalink AS permalink, ' .
+ 'stories.story_published AS published, ' .
+ 'stories.story_updated AS updated, ' .
+ 'stories.story_read AS readcount ' .
+ 'FROM jonah_stories_tags AS tags ' .
+ 'LEFT JOIN jonah_stories AS stories ON ' .
+ 'tags.story_id = stories.story_id ' .
+ 'WHERE stories.channel_id=?';
+ $values = array($criteria['channel_id']);
+
+ // Apply date filtering
+ if (isset($criteria['updated-min'])) {
+ $sql .= ' AND story_updated >= ?';
+ $values[] = $criteria['updated-min']->timestamp();
+ }
+ if (isset($criteria['updated-max'])) {
+ $sql .= ' AND story_updated <= ?';
+ $values[] = $criteria['updated-max']->timestamp();
+ }
+ if (isset($criteria['published-min'])) {
+ $sql .= ' AND story_published >= ?';
+ $values[] = $criteria['published-min']->timestamp();
+ }
+ if (isset($criteria['published-max'])) {
+ $sql .= ' AND story_published <= ?';
+ $values[] = $criteria['published-max']->timestamp();
+ }
+
+ // Apply tag filtering
+ if (isset($criteria['tags'])) {
+ $sql .= ' AND (';
+ $multiple = false;
+ foreach ($criteria['tags'] as $tag) {
+ if ($multiple) {
+ $sql .= ' OR ';
+ }
+ $sql .= 'tags.tag_id = ?';
+ $values[] = $criteria['tagIDs'][$tag];
+ $multiple = true;
+ }
+ $sql .= ')';
+ }
+
+ if (isset($criteria['alltags'])) {
+ $sql .= ' AND (';
+ $multiple = false;
+ foreach ($criteria['alltags'] as $tag) {
+ if ($multiple) {
+ $sql .= ' AND ';
+ }
+ $sql .= 'tags.tag_id = ?';
+ $values[] = $criteria['tagIDs'][$tag];
+ $multiple = true;
+ }
+ $sql .= ')';
+ }
+
+ // Filter by story author
+ if (isset($criteria['author'])) {
+ $sql .= ' AND stories.story_author = ?';
+ $values[] = $criteria['author'];
+ }
+
+ // Filter stories by keyword
+ if (isset($criteria['keywords'])) {
+ foreach ($criteria['keywords'] as $keyword) {
+ $sql .= ' AND stories.story_body LIKE ?';
+ $values[] = '%' . $keyword . '%';
+ }
+ }
+ if (isset($criteria['notkeywords'])) {
+ foreach ($criteria['notkeywords'] as $keyword) {
+ $sql .= ' AND stories.story_body NOT LIKE ?';
+ $values[] = '%' . $keyword . '%';
+ }
+ }
+
+ Horde::logMessage('SQL Query by Jonah_Driver_sql::_getStories(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $results = $this->_db->getAll($sql, $values, DB_FETCHMODE_ASSOC);
+ if (is_a($results, 'PEAR_Error')) {
+ return $results;
+ }
+
+ return $results;
+ }
+
+ function _getIdBySlug($slug)
+ {
+ return $slug;
+ }
+
+ /**
+ * Returns the most recent or all stories from a channel.
+ * This method is deprecated.
+ *
+ * @param integer $channel_id The news channel to get stories from.
+ * @param integer $max The maximum number of stories to get.
+ * @param integer $from The number of the story to start with.
+ * @param integer $date The timestamp of the date to start with.
+ * @param boolean $unreleased Whether to return not yet released stories.
+ * @param integer $order How to order the results for internal
+ * channels. Possible values are the
+ * JONAH_ORDER_* constants.
+ *
+ * @return array The specified number (or less, if there are fewer) of
+ * stories from the given channel.
+ */
+ function _legacyGetStories($channel_id, $max, $from = 0, $date = null,
+ $unreleased = false, $order = JONAH_ORDER_PUBLISHED)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ $sql = 'SELECT * FROM jonah_stories WHERE channel_id = ?';
+ $values = array((int)$channel_id);
+
+ if ($unreleased) {
+ if ($date !== null) {
+ $sql .= ' AND story_published <= ?';
+ $values[] = $date;
+ }
+ } else {
+ if ($date === null) {
+ $date = time();
+ } else {
+ $date = max($date, time());
+ }
+ $sql .= ' AND story_published <= ?';
+ $values[] = $date;
+ }
+
+ switch ($order) {
+ case JONAH_ORDER_PUBLISHED:
+ $sql .= ' ORDER BY story_published DESC';
+ break;
+ case JONAH_ORDER_READ:
+ $sql .= ' ORDER BY story_read DESC';
+ break;
+ case JONAH_ORDER_COMMENTS:
+ //@TODO
+ break;
+ }
+
+ if (!is_null($max)) {
+ $sql = $this->_db->modifyLimitQuery($sql, (int)$from, (int)$max, $values);
+ }
+
+ Horde::logMessage('SQL Query by Jonah_Driver_sql::_legacyGetStories(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->getAll($sql, $values, DB_FETCHMODE_ASSOC);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ for ($i = 0; $i < count($result); $i++) {
+ $result[$i] = $this->_convertFromBackend($result[$i]);
+ if (empty($result[$i]['story_permalink'])) {
+ $this->_addPermalink($result[$i]);
+ }
+ $tags = $this->readTags($result[$i]['story_id']);
+ if (is_a($tags, 'PEAR_Error')) {
+ return $tags;
+ }
+ $result[$i]['story_tags'] = $tags;
+ }
+ return $result;
+ }
+
+ /**
+ */
+ function _getStory($story_id, $read = false)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ $sql = 'SELECT * FROM jonah_stories WHERE story_id = ?';
+ $values = array((int)$story_id);
+
+ Horde::logMessage('SQL Query by Jonah_Driver_sql::_getStory(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->getRow($sql, $values, DB_FETCHMODE_ASSOC);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ } elseif (empty($result)) {
+ return PEAR::raiseError(sprintf(_("Story id \"%s\" not found."), $story_id));
+ }
+ $result['story_tags'] = $this->readTags($story_id);
+ $result = $this->_convertFromBackend($result);
+ if (empty($result['story_permalink'])) {
+ $this->_addPermalink($result);
+ }
+ if ($read) {
+ $this->_readStory($story_id);
+ }
+
+ return $result;
+ }
+
+ /**
+ */
+ function _getStoryByUrl($channel_id, $story_url)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ $sql = 'SELECT * FROM jonah_stories' .
+ ' WHERE channel_id = ? AND story_url = ?';
+ $values = array((int)$channel_id, $story_url);
+
+ Horde::logMessage('SQL Query by Jonah_Driver_sql::_getStoryByUrl(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->getRow($sql, $values, DB_FETCHMODE_ASSOC);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ } elseif (empty($result)) {
+ return PEAR::raiseError(sprintf(_("Story URL \"%s\" not found."), $story_url));
+ }
+ $result = $this->_convertFromBackend($result);
+ if (empty($result['story_permalink'])) {
+ $this->_addPermalink($result);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Adds a missing permalink to a story.
+ *
+ * @param array $story A story hash.
+ */
+ function _addPermalink(&$story)
+ {
+ $channel = $this->getChannel($story['channel_id']);
+ if (is_a($channel, 'PEAR_Error')) {
+ return;
+ }
+ $sql = 'UPDATE jonah_stories SET story_permalink = ? WHERE story_id = ?';
+ $values = array($this->getStoryLink($channel, $story), $story['story_id']);
+ Horde::logMessage('SQL Query by Jonah_Driver_sql::_addPermalink(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->query($sql, $values);
+ if (!is_a($result, 'PEAR_Error')) {
+ $story['story_permalink'] = $values[0];
+ }
+ }
+
+ /**
+ * Gets the latest released story from a given internal channel
+ *
+ * @param int $channel_id The channel id.
+ *
+ * @return int The story id.
+ */
+ function getLatestStoryId($channel_id)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ $sql = 'SELECT story_id FROM jonah_stories' .
+ ' WHERE channel_id = ? AND story_published <= ?' .
+ ' ORDER BY story_updated DESC';
+ $values = array((int)$channel_id, time());
+
+ Horde::logMessage('SQL Query by Jonah_Driver_sql::getLatestStoryId(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->getRow($sql, $values, DB_FETCHMODE_ASSOC);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ } elseif (empty($result)) {
+ return PEAR::raiseError(sprintf(_("Channel \"%s\" not found."), $channel_id));
+ }
+
+ return $result['story_id'];
+ }
+
+ /**
+ */
+ function deleteStory($channel_id, $story_id)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ $sql = 'DELETE FROM jonah_stories' .
+ ' WHERE channel_id = ? AND story_id = ?';
+ $values = array((int)$channel_id, (int)$story_id);
+
+ Horde::logMessage('SQL Query by Jonah_Driver_sql::deleteStory(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->query($sql, $values);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result->getMessage(), __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ }
+
+ $sql = 'DELETE FROM jonah_stories_tags ' .
+ 'WHERE channel_id = ? AND story_id = ?';
+ $result = $this->_db->query($sql, $values);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result->getMessage(), __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ }
+
+ return true;
+ }
+
+ /**
+ * Write out the tags for a specific resource.
+ *
+ * @param int $resource_id The story we are tagging.
+ * @param int $channel_id The channel id for the story we are tagging
+ * @param array $tags An array of tags.
+ *
+ * @return mixed True | PEAR_Error
+ */
+ function writeTags($resource_id, $channel_id, $tags)
+ {
+ global $conf;
+
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+ // First, make sure all tag names exist in the DB.
+ $tagkeys = array();
+ $insert = $this->_db->prepare('INSERT INTO jonah_tags (tag_id, tag_name) VALUES(?, ?)');
+ $query = $this->_db->prepare('SELECT tag_id FROM jonah_tags WHERE tag_name = ?');
+ foreach ($tags as $tag) {
+ $tag = Horde_String::lower(trim($tag));
+ $results = $this->_db->execute($query, $this->_db->escapeSimple($tag));
+ if (is_a($results, 'PEAR_Error')) {
+ return $results;
+ } elseif ($results->numRows() == 0) {
+ $id = $this->_db->nextId('jonah_tags');
+ $result = $this->_db->execute($insert, array($id, $tag));
+ $tagkeys[] = $id;
+ } else {
+ $row = $results->fetchRow(DB_FETCHMODE_ASSOC);
+ $tagkeys[] = $row['tag_id'];
+ }
+ }
+
+ // Free our resources.
+ $this->_db->freePrepared($insert, true);
+ $this->_db->freePrepared($query, true);
+
+ $sql = 'DELETE FROM jonah_stories_tags WHERE story_id = ' . (int)$resource_id;
+ $query = $this->_db->prepare('INSERT INTO jonah_stories_tags (story_id, channel_id, tag_id) VALUES(?, ?, ?)');
+
+ Horde::logMessage('SQL query by Jonah_Driver_sql::writeTags: ' . $sql,
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ $this->_db->query($sql);
+ foreach ($tagkeys as $key) {
+ $this->_db->execute($query, array($resource_id, $channel_id, $key));
+ }
+ $this->_db->freePrepared($query, true);
+
+ /* @TODO We should clear at least any of our cached counts */
+ return true;
+ }
+
+ /**
+ * Retrieve the tags for a specified resource.
+ *
+ * @param int $resource_id The resource to get tags for.
+ *
+ * @return mixed An array of tags | PEAR_Error
+ */
+ function readTags($resource_id)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+ $sql = 'SELECT jonah_tags.tag_id, tag_name FROM jonah_tags INNER JOIN jonah_stories_tags ON jonah_stories_tags.tag_id = jonah_tags.tag_id WHERE jonah_stories_tags.story_id = ?';
+
+ Horde::logMessage('SQL query by Jonah_Driver_sql::readTags ' . $sql,
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ $tags = $this->_db->getAssoc($sql, false, array($resource_id), false);
+ return $tags;
+ }
+
+ /**
+ * Retrieve the list of used tag_names, tag_ids and the total number
+ * of resources that are linked to that tag.
+ *
+ * @param array $tags An optional array of tag_ids. If omitted, all tags
+ * will be included.
+ *
+ * @param array $channel_id An optional array of channel_ids.
+ *
+ * @return mixed An array containing tag_name, and total | PEAR_Error
+ */
+ function listTagInfo($tags = array(), $channel_id = null)
+ {
+ require_once 'Horde/Cache.php';
+
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ if (!is_array($channel_id) && is_numeric($channel_id)) {
+ $channel_id = array($channel_id);
+ }
+ $cache = $GLOBALS['injector']->getInstance('Horde_Cache');
+ $cache_key = 'jonah_tags_' . md5(serialize($tags) . md5(serialize($channel_id)));
+ $cache_value = $cache->get($cache_key, $GLOBALS['conf']['cache']['default_lifetime']);
+ if ($cache_value) {
+ return unserialize($cache_value);
+ }
+
+ $haveWhere = false;
+ $sql = 'SELECT tn.tag_id, tag_name, COUNT(tag_name) total FROM jonah_tags as tn INNER JOIN jonah_stories_tags as t ON t.tag_id = tn.tag_id';
+ if (count($tags)) {
+ $sql .= ' WHERE tn.tag_id IN (' . implode(',', $tags) . ')';
+ $haveWhere = true;
+ }
+ if (!is_null($channel_id)) {
+ if (!$haveWhere) {
+ $sql .= ' WHERE';
+ } else {
+ $sql .= ' AND';
+ }
+ $channels = array();
+ foreach ($channel_id as $cid) {
+ $c = $this->_getChannel($cid);
+ if ($c['channel_type'] == JONAH_COMPOSITE_CHANNEL) {
+ $channels = array_merge($channels, explode(':', $c['channel_url']));
+ }
+ }
+ $channel_id = array_merge($channel_id, $channels);
+ $sql .= ' t.channel_id IN (' . implode(', ', $channel_id) . ')';
+ }
+ $sql .= ' GROUP BY tn.tag_id, tag_name ORDER BY total DESC;';
+ $results = $this->_db->getAssoc($sql,true, array(), DB_FETCHMODE_ASSOC, false);
+ $cache->set($cache_key, serialize($results));
+ return $results;
+ }
+
+ /**
+ * Search for resources matching the specified criteria
+ *
+ * @param array $ids An array of tag_ids to search for. Note that
+ * these are AND'd together.
+ * @param integer $max The maximum number of stories to get. If
+ * null, all stories will be returned.
+ * @param integer $from The number of the story to start with.
+ * @param array $channel Limit the result set to resources
+ * present in these channels
+ * @param integer $order How to order the results for internal
+ * channels. Possible values are the
+ * JONAH_ORDER_* constants.
+ *
+ * @return mixed Array of stories| PEAR_Error
+ */
+ function searchTagsById($ids, $max = 10, $from = 0, $channel_id = array(),
+ $order = JONAH_ORDER_PUBLISHED)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+ if (!is_array($ids) || !count($ids)) {
+ $stories[] = array();
+ } else {
+ $stories = array();
+ $sql = 'SELECT DISTINCT s.story_id, s.channel_id FROM jonah_stories'
+ . ' as s, jonah_stories_tags as t';
+ for ($i = 0; $i < count($ids); $i++) {
+ $sql .= ', jonah_stories_tags as t' . $i;
+ }
+ $sql .= ' WHERE s.story_id = t.story_id';
+ for ($i = 0 ; $i < count($ids); $i++) {
+ $sql .= ' AND t' . $i . '.tag_id = ' . $ids[$i] . ' AND t'
+ . $i . '.story_id = t.story_id';
+ }
+
+ /* Limit to particular channels if requested */
+ if (count($channel_id) > 0) {
+ // Have to find out if we are a composite channel or not.
+ $channels = array();
+ foreach ($channel_id as $cid) {
+ $c = $this->_getChannel($cid);
+ if ($c['channel_type'] == JONAH_COMPOSITE_CHANNEL) {
+ $temp = explode(':', $c['channel_url']);
+ // Save a map of channels that are from composites.
+ foreach ($temp as $t) {
+ $cchannels[$t] = $cid;
+ }
+ $channels = array_merge($channels, $temp);
+ }
+ }
+ $channels = array_merge($channel_id, $channels);
+ $timestamp = time();
+ $sql .= ' AND t.channel_id IN (' . implode(', ', $channels)
+ . ') AND s.story_published IS NOT NULL AND '
+ . 's.story_published < ' . $timestamp;
+ }
+
+ switch ($order) {
+ case JONAH_ORDER_PUBLISHED:
+ $sql .= ' ORDER BY story_published DESC';
+ break;
+ case JONAH_ORDER_READ:
+ $sql .= ' ORDER BY story_read DESC';
+ break;
+ case JONAH_ORDER_COMMENTS:
+ //@TODO
+ break;
+ }
+
+ /* Instantiate the channel object outside the loop if we
+ * are only limiting to one channel. */
+ if (count($channel_id) == 1) {
+ $channel = $this->getChannel($channel_id[0]);
+ }
+ Horde::logMessage('SQL query by Jonah_Driver_sql::searchTags: ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $results = $this->_db->limitQuery($sql, $from, $max);
+ if (is_a($results, 'PEAR_Error')) {
+ return $results;
+ }
+
+ for ($i = 0; $i < $results->numRows(); $i++) {
+ $row = $results->fetchRow();
+ $story = $this->_getStory($row[0], false);
+ if (count($channel_id > 1)) {
+ // Make sure we get the correct channel info for composites
+ if (!empty($cchannels[$story['channel_id']])) {
+ $channel = $this->getChannel($cchannels[$story['channel_id']]);
+ } else {
+ $channel = $this->getChannel($story['channel_id']);
+ }
+ }
+
+ /* Format story link. */
+ $story['story_link'] = $this->getStoryLink($channel, $story);
+ $story = array_merge($story, $channel);
+ /* Format dates. */
+ $date_format = $GLOBALS['prefs']->getValue('date_format');
+ $story['story_updated_date'] = strftime($date_format, $story['story_updated']);
+ if (!empty($story['story_published'])) {
+ $story['story_published_date'] = strftime($date_format, $story['story_published']);
+ }
+
+ $stories[] = $story;
+ }
+ }
+
+ return $stories;
+ }
+
+ /**
+ * Search for articles matching specific tag name(s).
+ *
+ * @see Jonah_Driver_sql::searchTagsById()
+ */
+ function searchTags($names, $max = 10, $from = 0, $channel_id = array(),
+ $order = JONAH_ORDER_PUBLISHED)
+ {
+ $ids = $this->getTagIds($names);
+ if (is_a($ids, 'PEAR_Error')) {
+ return $ids;
+ }
+ return $this->searchTagsById(array_values($ids), $max, $from, $channel_id, $order);
+ }
+
+
+ /**
+ * Return a set of tag names given the tag_ids.
+ *
+ * @param array $ids An array of tag_ids to get names for.
+ *
+ * @return mixed An array of tag names | PEAR_Error.
+ */
+ function getTagNames($ids)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+ $sql = 'SELECT t.tag_name FROM jonah_tags as t WHERE t.tag_id IN(';
+ $needComma = false;
+ foreach ($ids as $id) {
+ $sql .= ($needComma ? ',' : '') . '\'' . $id . '\'';
+ $needComma = true;
+ }
+ $sql .= ')';
+ $tags = $this->_db->getCol($sql);
+ return $tags;
+ }
+
+ /**
+ * Return a set of tag_ids, given the tag name
+ *
+ * @param array $names An array of names to search for
+ *
+ * @return mixed An array of tag_name => tag_ids | PEAR_Error
+ */
+ function getTagIds($names)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+ $sql = 'SELECT t.tag_name, t.tag_id FROM jonah_tags as t WHERE t.tag_name IN(';
+ $needComma = false;
+ foreach ($names as $name) {
+ $sql .= ($needComma ? ',' : '') . '\'' . $name . '\'';
+ $needComma = true;
+ }
+ $sql .= ')';
+ $tags = $this->_db->getAssoc($sql);
+ return $tags;
+ }
+
+ /**
+ * Attempts to open a persistent connection to the SQL server.
+ *
+ * @return boolean True on success; PEAR_Error on failure.
+ */
+ function _connect()
+ {
+ if ($this->_connected) {
+ return true;
+ }
+
+ Horde::assertDriverConfig($this->_params, 'news',
+ array('phptype', 'charset'),
+ 'jonah news SQL');
+
+ if (!isset($this->_params['database'])) {
+ $this->_params['database'] = '';
+ }
+ if (!isset($this->_params['username'])) {
+ $this->_params['username'] = '';
+ }
+ if (!isset($this->_params['hostspec'])) {
+ $this->_params['hostspec'] = '';
+ }
+
+ /* Connect to the SQL server using the supplied parameters. */
+ require_once 'DB.php';
+ $this->_db = &DB::connect($this->_params,
+ array('persistent' => !empty($this->_params['persistent'])));
+ if (is_a($this->_db, 'PEAR_Error')) {
+ return $this->_db;
+ }
+
+ // Set DB portability options.
+ switch ($this->_db->phptype) {
+ case 'mssql':
+ $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM);
+ break;
+ default:
+ $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS);
+ }
+
+ $this->_connected = true;
+ return true;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Jonah_FeedParser.
+ *
+ * Copyright 2000-2009 The Horde Project (http://www.horde.org/)
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * $Horde: jonah/lib/FeedParser.php,v 1.23 2009/06/10 05:24:47 slusarz Exp $
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @package Jonah
+ */
+class Jonah_FeedParser {
+
+ /**
+ * XML parser resource.
+ *
+ * @var resource
+ */
+ var $parser;
+
+ /**
+ * The current parent tag - CHANNEL, STORY, etc.
+ *
+ * @var string
+ */
+ var $parent = '';
+
+ /**
+ * The current child tag - TITLE, DESCRIPTION, URL, etc.
+ *
+ * @var string
+ */
+ var $child = '';
+
+ /**
+ * All the attributes of the channel description.
+ *
+ * @var array
+ */
+ var $channel;
+
+ /**
+ * All the attributes of the channel image.
+ *
+ * @var array
+ */
+ var $image;
+
+ /**
+ * All the attributes of the current story being parsed.
+ *
+ * @var array
+ */
+ var $story;
+
+ /**
+ * All the attributes of the current item being parsed.
+ *
+ * @var array
+ */
+ var $item;
+
+ /**
+ * The array that all the parsed information gets dumped into.
+ *
+ * @var array
+ */
+ var $structure;
+
+ /**
+ * What kind of feed are we parsing?
+ *
+ * @var string
+ */
+ var $format = 'rss';
+
+ /**
+ * Error string.
+ *
+ * @var string
+ */
+ var $error;
+
+ /**
+ * Feed charset.
+ *
+ * @var string
+ */
+ var $charset;
+
+ /**
+ * Constructs a new Jonah_FeedParser parser object.
+ */
+ function Jonah_FeedParser($charset)
+ {
+ $this->channel = array();
+ $this->image = array();
+ $this->item = array();
+ $this->story = array();
+ $this->charset = $charset;
+ }
+
+ /**
+ * Initialize the XML parser.
+ */
+ function init()
+ {
+ // Check that the charset is supported by the XML parser.
+ $allowed_charsets = array('us-ascii', 'iso-8859-1', 'utf-8', 'utf-16');
+ if (!in_array($this->charset, $allowed_charsets)) {
+ $this->charset = 'utf-8';
+ }
+
+ // Create the XML parser.
+ $this->parser = xml_parser_create($this->charset);
+ xml_set_object($this->parser, $this);
+ xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false);
+ xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
+ xml_set_element_handler($this->parser, 'startElement', 'endElement');
+ xml_set_character_data_handler($this->parser, 'characterData');
+ xml_set_default_handler($this->parser, 'defaultHandler');
+
+ // Disable processing instructions and external entities.
+ xml_set_processing_instruction_handler($this->parser, '');
+ xml_set_external_entity_ref_handler($this->parser, '');
+ }
+
+ /**
+ * Clean up any existing data - reset to a state where we can
+ * cleanly open a new file.
+ */
+ function cleanup()
+ {
+ $this->channel = array();
+ $this->image = array();
+ $this->item = array();
+ $this->story = array();
+ $this->structure = array();
+ }
+
+ /**
+ * Actually do the parsing. Separated from the constructor just in
+ * case you want to set any other options on the parser, load
+ * initial data, whatever.
+ *
+ * @param $data The XML feed data to parse.
+ */
+ function parse($data)
+ {
+ $this->init();
+
+ // Sanity checks.
+ if (!$this->parser) {
+ $this->error = 'Could not find xml parser handle';
+ return false;
+ }
+
+ // Parse.
+ if (!@xml_parse($this->parser, $data)) {
+ $this->error = sprintf('XML error: %s at line %d', xml_error_string(xml_get_error_code($this->parser)), xml_get_current_line_number($this->parser));
+ return false;
+ }
+
+ // Clean up.
+ xml_parser_free($this->parser);
+
+ return true;
+ }
+
+ /**
+ * Start collecting data about a new element.
+ */
+ function startElement($parser, $name, $attribs)
+ {
+ $name = Horde_String::upper($name);
+ $attribs = array_change_key_case($attribs, CASE_LOWER);
+
+ switch ($name) {
+ case 'FEED':
+ $this->format = 'atom';
+
+ case 'CHANNEL':
+ case 'IMAGE':
+ case 'ITEM':
+ case 'ENTRY':
+ case 'TEXTINPUT':
+ $this->parent = $name;
+ break;
+
+ case 'LINUXTODAY':
+ case 'UUTISET':
+ $this->parent = 'channel';
+ break;
+
+ case 'UUTINEN':
+ $this->parent = 'item';
+ break;
+
+ case 'STORYLIST':
+ $this->structure['type'] = 'storylist';
+ break;
+
+ case 'RATING':
+ case 'DESCRIPTION':
+ case 'WIDTH':
+ case 'HEIGHT':
+ case 'LANGUAGE':
+ case 'MANAGINGEDITOR':
+ case 'WEBMASTER':
+ case 'COPYRIGHT':
+ case 'LASTBUILDDATE':
+ case 'AUTHOR':
+ case 'TOPIC':
+ case 'COMMENTS':
+ // Userland story format.
+ case 'POSTTIME':
+ case 'CHANNELTITLE':
+ case 'CHANNELURL':
+ case 'USERLANDCHANNELID':
+ case 'STORYTEXT':
+ $this->child = Horde_String::lower($name);
+ break;
+
+ case 'LINK':
+ $this->child = 'link';
+ if ($this->format == 'atom' && !empty($attribs['href'])) {
+ if ($this->parent == 'FEED') {
+ $target = &$this->channel;
+ } else {
+ $target = &$this->item;
+ }
+
+ // For now, make the alternate link, which is most likely to
+ // point to the HTML copy of the article, the default one - or,
+ // if there isn't yet a link and the rel is empty, use it.
+ if ((empty($target['link']) && empty($attribs['rel'])) ||
+ (isset($attribs['rel']) && $attribs['rel'] == 'alternate')) {
+ $target['link'] = $attribs['href'];
+ }
+
+ // Store all links named by their rel.
+ if (!empty($attribs['rel'])) {
+ $rel = 'link-' . $attribs['rel'];
+ if (isset($target[$rel])) {
+ if (!is_array($target[$rel])) {
+ $target[$rel] = array($target[$rel]);
+ }
+ $target[$rel][] = $attribs['href'];
+ } else {
+ $target[$rel] = $attribs['href'];
+ }
+ }
+ }
+ break;
+
+ case 'NAME':
+ if ($this->child != 'author') {
+ $this->child = Horde_String::lower($name);
+ }
+ break;
+
+ // Atom feed entry body.
+ case 'CONTENT':
+ $this->child = 'body';
+ if (isset($attribs['type']) && ($attribs['type'] == 'text/html' || $attribs['type'] == 'html')) {
+ $this->item['body_type'] = 'html';
+ }
+ break;
+
+ // Atom feed entry summary.
+ case 'SUMMARY':
+ $this->child = 'description';
+ break;
+
+ // If we're inside an ITEM, consider this a LINK.
+ case 'URL':
+ if ($this->parent == 'item') {
+ $this->child = 'link';
+ } else {
+ $this->child = 'url';
+ }
+ break;
+
+ // For My.Userland channels, let these be STORY tags;
+ // otherwise, map them to ITEMs.
+ case 'STORY':
+ if ($this->parent == 'storylist') {
+ $this->parent = 'story';
+ } else {
+ $this->parent = 'item';
+ }
+ break;
+
+ // Nonstandard bits that we want to map to standard bits.
+ case 'TITLE':
+ case 'OTSIKKO':
+ $this->child = 'title';
+ break;
+
+ case 'PUBLISHED':
+ case 'PUBDATE':
+ case 'TIME':
+ case 'PVM':
+ $this->child = 'pubdate';
+ break;
+
+ case 'MODIFIED':
+ case 'UPDATED':
+ $this->child = 'moddate';
+ break;
+
+ // More Atom dates.
+ case 'CREATED':
+ case 'ISSUED':
+ $this->child = Horde_String::lower($name);
+ break;
+
+ case 'RDF:RDF':
+ case 'RSS':
+ $this->child = 'junk';
+ break;
+
+ // For Yahoo's media namespace extensions
+ // see http://search.yahoo.com/mrss
+ // Specs say that elements other than content may be
+ // either children of media:content OR siblings, so
+ // we don't nest these elements.
+ case 'MEDIA:CONTENT':
+ $this->child = 'media:content';
+ foreach ($attribs as $aname => $avalue) {
+ $this->item[$this->child][$aname] = $avalue;
+ }
+ break;
+ case 'MEDIA:THUMBNAIL':
+ $this->child = 'media:thumbnail';
+ foreach ($attribs as $aname => $avalue) {
+ $this->item[$this->child][$aname] = $avalue;
+ }
+ break;
+ case 'MEDIA:TITLE':
+ $this->child = 'media:title';
+ break;
+ case 'MEDIA:DESCRIPTION':
+ $this->child = 'media:description';
+ foreach ($attribs as $aname => $avalue) {
+ $this->item[$this->child][$aname] = $avalue;
+ }
+ // Ensure we have a default
+ $this->item[$this->child]['value'] = '';
+ break;
+ case 'MEDIA:GROUP':
+ $this->child='media:group';
+ break;
+ case 'MEDIA:KEYWORDS':
+ $this->child='media:keywords';
+ break;
+
+ default:
+ if ($this->format == 'atom' && in_array($this->child, array('body', 'description'))) {
+ if (!isset($this->item[$this->child])) {
+ $this->item[$this->child] = '';
+ }
+
+ $tag = Horde_String::lower($name);
+ switch ($tag) {
+ case 'br':
+ case 'hr':
+ $this->item[$this->child] .= '<' . $tag . '/>';
+ break;
+
+ default:
+ $this->item[$this->child] .= '<' . $tag;
+ foreach ($attribs as $aname => $avalue) {
+ $this->item[$this->child] .= ' ' . $aname . '="' . $avalue . '"';
+ }
+ $this->item[$this->child] .= '>';
+ break;
+ }
+ } else {
+ $this->child = 'junk';
+ }
+ break;
+ }
+ }
+
+ /**
+ * Handle the ends of XML elements - wrap up whatever we've been
+ * putting together and store it for safekeeping.
+ */
+ function endElement($parser, $name)
+ {
+ $name = Horde_String::upper($name);
+ switch ($name) {
+ case 'CHANNEL':
+ case 'FEED':
+ $this->format = 'atom';
+ $this->structure['channel'] = $this->channel;
+ break;
+
+ case 'IMAGE':
+ $this->structure['image'] = $this->image;
+ break;
+
+ case 'STORY':
+ if ($this->parent == 'storylist') {
+ $this->structure['stories'][] = $this->story;
+ $this->story = array();
+ } else {
+ $this->structure['items'][] = $this->item;
+ $this->item = array();
+ }
+ break;
+
+ case 'TEXTINPUT':
+ $this->item['textinput'] = true;
+ // No break here; continue to the next case.
+
+ case 'UUTINEN':
+ case 'ITEM':
+ case 'ENTRY':
+ $this->structure['items'][] = $this->item;
+ $this->item = array();
+ break;
+ default:
+ if ($this->format == 'atom' && in_array($this->child, array('body', 'description'))) {
+ if (!isset($this->item[$this->child])) {
+ $this->item[$this->child] = '';
+ }
+
+ $tag = Horde_String::lower($name);
+ switch ($tag) {
+ case 'br':
+ case 'hr':
+ break;
+
+ default:
+ $this->item[$this->child] .= '</' . $tag . '>';
+ break;
+ }
+ }
+
+ }
+ }
+
+ /**
+ * The handler for character data encountered in the XML file.
+ */
+ function characterData($parser, $data)
+ {
+ if (preg_match('|\S|', $data)) {
+ switch ($this->parent) {
+ case 'CHANNEL':
+ case 'FEED':
+ if (!isset($this->channel[$this->child])) {
+ $this->channel[$this->child] = '';
+ }
+ $this->channel[$this->child] = $data;
+ break;
+
+ case 'IMAGE':
+ if (!isset($this->image[$this->child])) {
+ $this->image[$this->child] = '';
+ }
+ $this->image[$this->child] .= $data;
+ break;
+
+ case 'STORY':
+ if (!isset($this->story[$this->child])) {
+ $this->story[$this->child] = '';
+ }
+ $this->story[$this->child] .= $data;
+ break;
+
+ default:
+ switch ($this->child) {
+ case 'media:description':
+ $this->item[$this->child]['value'] = $data;
+ break;
+
+ default:
+
+ if (!isset($this->item[$this->child])) {
+ $this->item[$this->child] = '';
+ }
+ $this->item[$this->child] .= $data;
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Handles things that we don't recognize. A no-op.
+ */
+ function defaultHandler($parser, $data)
+ {
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * @package Jonah
+ */
+
+/**
+ * Horde_Form
+ */
+require_once 'Horde/Form.php';
+
+/**
+ * Horde_Form_Action
+ */
+require_once 'Horde/Form/Action.php';
+
+/**
+ * This class extends Horde_Form to provide the form to add/edit
+ * feeds.
+ *
+ * $Horde: jonah/lib/Forms/Feed.php,v 1.16 2009/06/10 05:24:48 slusarz Exp $
+ *
+ * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (BSD). If you
+ * did not receive this file, see http://cvs.horde.org/co.php/jonah/LICENSE.
+ *
+ * @author Marko Djukic <marko@oblo.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @package Jonah
+ */
+class FeedForm extends Horde_Form
+{
+ /**
+ */
+ function FeedForm(&$vars)
+ {
+ $channel_id = $vars->get('channel_id');
+ $editing = (!empty($channel_id));
+
+ parent::Horde_Form($vars, ($editing ? _("Edit Feed") : _("New Feed")));
+
+ $this->addHidden('', 'channel_id', 'int', false);
+ $this->addHidden('', 'old_channel_type', 'text', false);
+
+ $select_type =& $this->addVariable(_("Type"), 'channel_type', 'enum', true, false, null, array(Jonah_News::getAvailableTypes()));
+ $select_type->setDefault(Jonah_News::getDefaultType());
+ $select_type->setHelp('feed-type');
+ $select_type->setAction(Horde_Form_Action::factory('submit'));
+
+ $this->addVariable(_("Name"), 'channel_name', 'text', true);
+ $this->addVariable(_("Extra information for this feed type"), 'extra_info', 'header', false);
+ }
+
+ /**
+ */
+ function getInfo(&$vars, &$info)
+ {
+ parent::getInfo($vars, $info);
+ if ($vars->get('channel_type') == JONAH_COMPOSITE_CHANNEL &&
+ is_array($vars->get('subchannels'))) {
+ $info['channel_url'] = implode(':', $vars->get('subchannels'));
+ }
+ }
+
+ /**
+ */
+ function setExtraFields($type = null, $channel_id = null)
+ {
+ if (is_null($type)) {
+ $type = Jonah_News::getDefaultType();
+ }
+
+ switch ($type) {
+ case JONAH_INTERNAL_CHANNEL:
+ $this->addVariable(_("Description"), 'channel_desc', 'text', false);
+ $this->addVariable(
+ _("Channel Slug"), 'channel_slug', 'text', true, false,
+ sprintf(_("Slugs allows direct access to this channel's content by visiting: %s. <br /> Slug names may contain only letters, numbers or the _ (underscore) character."),
+ Horde::applicationUrl('slugname', true)),
+ array('/^[a-zA-Z1-9_]*$/'));
+
+ $this->addVariable(_("Include full story content in syndicated feeds?"), 'channel_full_feed', 'boolean', false);
+ $this->addVariable(_("Channel URL if not the default one. %c gets replaced by the feed ID."), 'channel_link', 'text', false);
+ $this->addVariable(_("Channel URL for further pages, if not the default one. %c gets replaced by the feed ID, %n by the story offset."), 'channel_page_link', 'text', false);
+ $this->addVariable(_("Story URL if not the default one. %c gets replaced by the feed ID, %s by the story ID."), 'channel_story_url', 'text', false);
+ break;
+
+ case JONAH_EXTERNAL_CHANNEL:
+ $interval = Jonah_News::getIntervalLabel();
+ $v = &$this->addVariable(_("Caching"), 'channel_interval', 'enum', false, false, _("The interval before stories in this feed are rechecked for updates. If none, then stories will always be refetched from the source."), array($interval));
+ $v->setDefault('86400');
+ $this->addVariable(_("Source URL"), 'channel_url', 'text', true, false, _("The url to use to fetch the stories, for example 'http://www.example.com/stories.rss'"));
+ $this->addVariable(_("Link"), 'channel_link', 'text', false);
+ $this->addVariable(_("Image"), 'channel_img', 'text', false);
+ break;
+
+ case JONAH_AGGREGATED_CHANNEL:
+ $this->addHidden('', 'channel_url', 'text', false);
+ $interval = Jonah_News::getIntervalLabel();
+ $this->addVariable(_("Description"), 'channel_desc', 'text', false);
+ $v = &$this->addVariable(_("Caching"), 'channel_interval', 'enum', false, false, _("The interval before stories aggregated into this feeds are rechecked for updates. If none, then stories will always be refetched from the sources."), array($interval));
+ $v->setDefault('86400');
+ if (!empty($channel_id)) {
+ $edit_url = Horde::applicationUrl('channels/aggregate.php');
+ $edit_url = Horde_Util::addParameter($edit_url, 'channel_id', $channel_id);
+ $edit_url = Horde_Util::addParameter($edit_url, 'channel_id', $channel_id);
+ $this->addVariable(_("Source URLs"), 'channel_urls', 'link', false, false, null, array(array('text' => _("Edit aggregated feeds"), 'url' => $edit_url)));
+ }
+ break;
+
+ case JONAH_COMPOSITE_CHANNEL:
+ global $news;
+ $channels = $news->getChannels(JONAH_INTERNAL_CHANNEL);
+ $enum = array();
+ foreach ($channels as $channel) {
+ $enum[$channel['channel_id']] = $channel['channel_name'];
+ }
+ $this->addVariable(_("Description"), 'channel_desc', 'text', false);
+ $this->addVariable(_("Channel URL if not the default one. %c gets replaced by the feed ID, %n by the story offset."), 'channel_page_link', 'text', false);
+ $this->addVariable(_("Story URL if not the default one. %c gets replaced by the feed ID, %s by the story ID."), 'channel_story_url', 'text', false);
+ $v = &$this->addVariable(_("Composite feeds"), 'subchannels', 'multienum', false, false, '', array($enum));
+ if (!empty($channel_id)) {
+ $channel = $news->getChannel($channel_id);
+ $v->setDefault(explode(':', $channel['channel_url']));
+ }
+ break;
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * @package Jonah
+ */
+
+/**
+ * Horde_Form
+ */
+require_once 'Horde/Form.php';
+
+/**
+ * Horde_Form_Action
+ */
+require_once 'Horde/Form/Action.php';
+
+/**
+ * This class extends Horde_Form to provide the form to add/edit
+ * stories.
+ *
+ * $Horde: jonah/lib/Forms/Story.php,v 1.14 2009/02/16 16:47:03 chuck Exp $
+ *
+ * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (BSD). If you
+ * did not receive this file, see http://cvs.horde.org/co.php/jonah/LICENSE.
+ *
+ * @author Marko Djukic <marko@oblo.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @package Jonah
+ */
+class StoryForm extends Horde_Form
+{
+ /**
+ */
+ function StoryForm(&$vars)
+ {
+ parent::Horde_Form($vars, $vars->get('story_id') ? _("Edit Story") : _("Add New Story"));
+
+ $this->setButtons(_("Save"), true);
+ $this->addHidden('', 'channel_id', 'int', false);
+ $this->addHidden('', 'story_id', 'int', false);
+ $this->addHidden('', 'story_read', 'int', false);
+ $this->addVariable(_("Story Title (Headline)"), 'story_title', 'text', true);
+ $this->addVariable(_("Short Description"), 'story_desc', 'longtext', true, false, null, array(2, 80));
+ $this->addVariable(_("Publish Now?"), 'publish_now', 'boolean', false);
+
+ $published = $vars->get('story_published');
+ if ($published) {
+ $date_params = array(min(date('Y', $published), date('Y')),
+ max(date('Y', $published), date('Y') + 10));
+ } else {
+ $date_params = array();
+ }
+
+ $d = &$this->addVariable(_("Or publish on this date:"), 'publish_date', 'monthdayyear', false, false, null, $date_params);
+ $d->setDefault($published);
+
+ $t = &$this->addVariable('', 'publish_time', 'hourminutesecond', false);
+ $t->setDefault($published);
+
+ $v = &$this->addVariable(_("Story body type"), 'story_body_type', 'enum', false, false, null, array(Jonah::getBodyTypes()));
+ $v->setAction(Horde_Form_Action::factory('submit'));
+ $v->setOption('trackchange', true);
+
+ /* If no body type specified, default to one. */
+ $body_type = $vars->get('story_body_type');
+ if (empty($body_type)) {
+ $body_type = Jonah::getDefaultBodyType();
+ $vars->set('story_body_type', $body_type);
+ }
+
+ /* Set up the fields according to what the type of body requested. */
+ if ($body_type == 'text') {
+ $this->addVariable(_("Full Story Text"), 'story_body', 'longtext', false, false, null, array(15, 80));
+ } elseif ($body_type == 'richtext') {
+ $this->addVariable(_("Full Story Text"), 'story_body', 'longtext', false, false, null, array(20, 80, array('rte')));
+ }
+
+ $this->addVariable(_("Tags"), 'story_tags', 'text', false, false, _("Enter keywords to tag this story, separated by commas"));
+ /* Only show URL insertion if it has been enabled in config. */
+ if (in_array('links', $GLOBALS['conf']['news']['story_types'])) {
+ $this->addVariable(_("Story URL"), 'story_url', 'text', false, false, _("If you enter a URL without a full story text, clicking on the story will send the reader straight to the URL, otherwise it will be shown at the end of the full story."));
+ }
+ }
+
+ /**
+ */
+ function getInfo(&$vars, &$info)
+ {
+ parent::getInfo($vars, $info);
+
+ /* Build release date. */
+ if (!empty($info['publish_now'])) {
+ $info['story_published'] = time();
+ } elseif (!empty($info['publish_date'])) {
+ $info['story_published'] = mktime(
+ (int)$info['publish_time']['hour'],
+ (int)$info['publish_time']['minute'],
+ 0,
+ date('n', $info['publish_date']),
+ date('j', $info['publish_date']),
+ date('Y', $info['publish_date']));
+ } else {
+ $info['story_published'] = null;
+ }
+
+ unset($info['publish_now']);
+ unset($info['publish_date']);
+ unset($info['publish_time']);
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * @package Jonah
+ */
+
+/**
+ * Internal Jonah channel.
+ */
+define('JONAH_INTERNAL_CHANNEL', 0);
+
+/**
+ * External channel.
+ */
+define('JONAH_EXTERNAL_CHANNEL', 1);
+
+/**
+ * Aggregated channel.
+ */
+define('JONAH_AGGREGATED_CHANNEL', 2);
+
+/**
+ * Composite channel.
+ */
+define('JONAH_COMPOSITE_CHANNEL', 3);
+
+/**
+ */
+define('JONAH_ORDER_PUBLISHED', 0);
+define('JONAH_ORDER_READ', 1);
+define('JONAH_ORDER_COMMENTS', 2);
+
+
+/**
+ * Jonah Base Class.
+ *
+ * $Horde: jonah/lib/Jonah.php,v 1.141 2009/11/24 04:15:37 chuck Exp $
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Eric Rechlin <eric@hpcalc.org>
+ * @package Jonah
+ */
+class Jonah {
+
+ /**
+ */
+ function _readURL($url)
+ {
+ global $conf;
+
+ $options['method'] = 'GET';
+ $options['timeout'] = 5;
+ $options['allowRedirects'] = true;
+
+ if (!empty($conf['http']['proxy']['proxy_host'])) {
+ $options = array_merge($options, $conf['http']['proxy']);
+ }
+
+ require_once 'HTTP/Request.php';
+ $http = new HTTP_Request($url, $options);
+ @$http->sendRequest();
+ if ($http->getResponseCode() != 200) {
+ return PEAR::raiseError(sprintf(_("Could not open %s."), $url));
+ }
+
+ $result = array('body' => $http->getResponseBody());
+ $content_type = $http->getResponseHeader('Content-Type');
+ if (preg_match('/.*;\s?charset="?([^"]*)/', $content_type, $match)) {
+ $result['charset'] = $match[1];
+ } elseif (preg_match('/<\?xml[^>]+encoding=["\']?([^"\'\s?]+)[^?].*?>/i', $result['body'], $match)) {
+ $result['charset'] = $match[1];
+ }
+
+ return $result;
+ }
+
+ /**
+ * Returns a drop-down select box to choose which view to display.
+ *
+ * @param name Name to assign to select box.
+ * @param selected Currently selected item. (optional)
+ * @param onchange JavaScript onchange code. (optional)
+ *
+ * @return string Generated select box code
+ */
+ function buildViewWidget($name, $selected = 'standard', $onchange = '')
+ {
+ require JONAH_BASE . '/config/templates.php';
+
+ if ($onchange) {
+ $onchange = ' onchange="' . $onchange . '"';
+ }
+
+ $html = '<select name="' . $name . '"' . $onchange . '>' . "\n";
+ foreach ($templates as $key => $tinfo) {
+ $select = ($selected == $key) ? ' selected="selected"' : '';
+ $html .= '<option value="' . $key . '"' . $select . '>' . $tinfo['name'] . "</option>\n";
+ }
+ return $html . '</select>';
+ }
+
+ /**
+ */
+ function getChannelTypeLabel($type)
+ {
+ switch ($type) {
+ case JONAH_INTERNAL_CHANNEL:
+ return _("Local Feed");
+
+ case JONAH_EXTERNAL_CHANNEL:
+ return _("External Feed");
+
+ case JONAH_AGGREGATED_CHANNEL:
+ return _("Aggregated Feed");
+
+ case JONAH_COMPOSITE_CHANNEL:
+ return _("Composite Feed");
+ }
+ }
+
+ /**
+ */
+ function checkPermissions($filter, $permission = Horde_Perms::READ, $in = null)
+ {
+ if (Horde_Auth::isAdmin('jonah:admin', $permission)) {
+ if (empty($in)) {
+ // Calls with no $in parameter are checking whether this user
+ // has permission. Since this user is an admin, they always
+ // have permission. If the $in parameter is an empty array,
+ // the method is expected to return an array too.
+ return is_array($in) ? array() : true;
+ } else {
+ return $in;
+ }
+ }
+
+ global $perms;
+
+ $out = array();
+
+ switch ($filter) {
+ case 'internal_channels':
+ case 'external_channels':
+ if (empty($in) || !$perms->exists('jonah:news:' . $filter . ':' . $in)) {
+ return $perms->hasPermission('jonah:news:' . $filter, Horde_Auth::getAuth(), $permission);
+ } elseif (!is_array($in)) {
+ return $perms->hasPermission('jonah:news:' . $filter . ':' . $in, Horde_Auth::getAuth(), $permission);
+ } else {
+ foreach ($in as $key => $val) {
+ if ($perms->hasPermission('jonah:news:' . $filter . ':' . $val, Horde_Auth::getAuth(), $permission)) {
+ $out[$key] = $val;
+ }
+ }
+ }
+ break;
+
+ case 'channels':
+ foreach ($in as $key => $val) {
+ $perm_name = Jonah::typeToPermName($val['channel_type']);
+ if ($perms->hasPermission('jonah:news:' . $perm_name, Horde_Auth::getAuth(), $permission) ||
+ $perms->hasPermission('jonah:news:' . $perm_name . ':' . $val['channel_id'], Horde_Auth::getAuth(), $permission)) {
+ $out[$key] = $in[$key];
+ }
+ }
+ break;
+
+ default:
+ return $perms->hasPermission($filter, Horde_Auth::getAuth(), Horde_Perms::EDIT);
+ }
+
+ return $out;
+ }
+
+ /**
+ */
+ function typeToPermName($type)
+ {
+ if ($type == JONAH_INTERNAL_CHANNEL) {
+ return 'internal_channels';
+ } elseif ($type == JONAH_EXTERNAL_CHANNEL) {
+ return 'external_channels';
+ }
+ }
+
+ /**
+ * Returns an array of configured body types from Jonah's $conf array.
+ *
+ * @return array An array of body types.
+ */
+ function getBodyTypes()
+ {
+ static $types = array();
+ if (!empty($types)) {
+ return $types;
+ }
+
+ if (in_array('richtext', $GLOBALS['conf']['news']['story_types'])) {
+ $types['richtext'] = _("Rich Text");
+ }
+
+ /* Other than checking if text is enabled, it is inserted by default if
+ * no other body type has been enabled in the config. */
+ if (in_array('text', $GLOBALS['conf']['news']['story_types']) ||
+ empty($types)) {
+ $types['text'] = _("Text");
+ }
+
+ return $types;
+ }
+
+ /**
+ * Tries to figure out a default body type. Used when none has been
+ * specified and a types is needed to fall back on to.
+ *
+ * @return string A default type.
+ */
+ function getDefaultBodyType()
+ {
+ $types = Jonah::getBodyTypes();
+ if (isset($types['text'])) {
+ return 'text';
+ } elseif (isset($types['richtext'])) {
+ return 'richtext';
+ }
+ /* The two most common body types have not been found, so just return
+ * the first one that is in the array. */
+ $tmp = array_keys($types);
+ return array_shift($tmp);
+ }
+
+ /**
+ * Build Jonah's list of menu items.
+ */
+ function getMenu($returnType = 'object')
+ {
+ global $registry, $conf;
+
+ $menu = new Horde_Menu();
+
+ /* If authorized, show admin links. */
+ if (Jonah::checkPermissions('jonah:news', Horde_Perms::EDIT)) {
+ $menu->addArray(array('url' => Horde::applicationUrl('channels/index.php'), 'text' => _("_Feeds"), 'icon' => 'jonah.png'));
+ }
+ foreach ($conf['news']['enable'] as $channel_type) {
+ if (Jonah::checkPermissions($channel_type, Horde_Perms::EDIT)) {
+ $menu->addArray(array('url' => Horde::applicationUrl('channels/edit.php'), 'text' => _("New Feed"), 'icon' => 'new.png'));
+ break;
+ }
+ }
+ if ($channel_id = Horde_Util::getFormData('channel_id')) {
+ $news = Jonah_News::factory();
+ $channel = $news->getChannel($channel_id);
+ if ($channel['channel_type'] == JONAH_INTERNAL_CHANNEL &&
+ Jonah::checkPermissions(Jonah::typeToPermName($channel['channel_type']), Horde_Perms::EDIT, $channel_id)) {
+ $menu->addArray(array('url' => Horde::applicationUrl('stories/edit.php?channel_id=' . (int)$channel_id), 'text' => _("_New Story"), 'icon' => 'new.png'));
+ }
+ }
+
+ if ($returnType == 'object') {
+ return $menu;
+ } else {
+ return $menu->render();
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * @package Jonah
+ */
+
+/** Horde_Array */
+require_once 'Horde/Array.php';
+
+/**
+ * Jonah_News:: is the main class for handling news headlines for Jonah both
+ * from internal Jonah generated news sources and external channels.
+ *
+ * $Horde: jonah/lib/News.php,v 1.164 2010/02/01 10:32:05 jan Exp $
+ *
+ * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (BSD). If you did not
+ * did not receive this file, see http://cvs.horde.org/co.php/jonah/LICENSE.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Marko Djukic <marko@oblo.com>
+ * @author Jan Schneider <jan@horde.org>
+ * @package Jonah
+ */
+class Jonah_News {
+
+ /**
+ * Hash containing connection parameters.
+ *
+ * @var array
+ */
+ var $_params = array();
+
+ /**
+ * Constructs a new News storage object.
+ *
+ * @param array $params A hash containing connection parameters.
+ */
+ function Jonah_News($params = array())
+ {
+ $this->_params = $params;
+ }
+
+ /**
+ */
+ function deleteChannel(&$info)
+ {
+ return $this->_deleteChannel($info['channel_id']);
+ }
+
+ /**
+ * Fetches the requested channel, while actually passing on the request to
+ * the backend _getChannel() function to do the real work.
+ *
+ * @param integer $channel_id The channel id to fetch.
+ *
+ * @return array|PEAR_Error The channel details as an array or a
+ * PEAR_Error if not valid or not found.
+ */
+ function getChannel($channel_id)
+ {
+ static $channel = array();
+
+ /* We need a non empty channel id. */
+ if (empty($channel_id)) {
+ return PEAR::raiseError(_("Missing channel id."));
+ }
+
+ /* Cache the fetching of channels. */
+ if (!isset($channel[$channel_id])) {
+ $channel[$channel_id] = $this->_getChannel($channel_id);
+ if (!is_a($channel[$channel_id], 'PEAR_Error')) {
+ if (empty($channel[$channel_id]['channel_link'])) {
+ $channel[$channel_id]['channel_official'] = Horde_Util::addParameter(Horde::applicationUrl('delivery/html.php', true, -1), 'channel_id', $channel_id, false);
+ } else {
+ $channel[$channel_id]['channel_official'] = str_replace(array('%25c', '%c'), array('%c', $channel_id), $channel[$channel_id]['channel_link']);
+ }
+ }
+ }
+
+ return $channel[$channel_id];
+ }
+
+ /**
+ * Checks if the channel is editable by first checking if the $channel_id
+ * returns a valid channel array and then whether the channel type is
+ * JONAH_INTERNAL_CHANNEL, which is the only one to allow stories to be
+ * added.
+ *
+ * @param integer $channel_id The channel id to check.
+ *
+ * @return array|PEAR_Error The channel details as an array or a
+ * PEAR_Error if not editable.
+ */
+ function isChannelEditable($channel_id)
+ {
+ /* Check if this channel id returns a valid channel. */
+ $channel = $this->getChannel($channel_id);
+ if (is_a($channel, 'PEAR_Error')) {
+ return $channel;
+ }
+
+ /* Check if the channel type allows adding of stories. */
+ if ($channel['channel_type'] != JONAH_INTERNAL_CHANNEL) {
+ return PEAR::raiseError(sprintf(_("Feed \"%s\" is not authored on this system."), $channel['channel_name']));
+ }
+
+ return $channel;
+ }
+
+ /**
+ * Returns the most recent or all stories from a channel.
+ *
+ * @param integer $channel_id The news channel to get stories from.
+ * @param integer $max The maximum number of stories to get. If
+ * null, all stories will be returned.
+ * @param integer $from The number of the story to start with.
+ * @param boolean $refresh Force a refresh of stories in case this is
+ * an external channel.
+ * @param integer $date The timestamp of the date to start with.
+ * @param boolean $unreleased Return stories that have not yet been
+ * published?
+ * Defaults to false - only published stories.
+ * @param integer $order How to order the results for internal
+ * channels. Possible values are the
+ * JONAH_ORDER_* constants.
+ *
+ * @return array The specified number (or less, if there are fewer) of
+ * stories from the given channel.
+ */
+ function getStories($channel_id, $max = 10, $from = 0, $refresh = false,
+ $date = null, $unreleased = false,
+ $order = JONAH_ORDER_PUBLISHED)
+ {
+ $channel = $this->getChannel($channel_id);
+ if (is_a($channel, 'PEAR_Error')) {
+ Horde::logMessage($channel, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return array();
+ }
+
+ /* Fetch the stories according to channel type, using a
+ * template method pattern for each type. */
+ $funcs = array(
+ JONAH_INTERNAL_CHANNEL => '_getInternalStories',
+ JONAH_EXTERNAL_CHANNEL => '_getExternalStories',
+ JONAH_AGGREGATED_CHANNEL => '_getAggregatedStories',
+ JONAH_COMPOSITE_CHANNEL => '_getCompositeStories',
+ );
+
+ $func = $funcs[$channel['channel_type']];
+ return $this->$func($channel, $max, $from, $refresh, $date, $unreleased, $order);
+ }
+
+ /**
+ */
+ function _getInternalStories($channel, $max = 10, $from = 0, $refresh = false,
+ $date = null, $unreleased = false,
+ $order = JONAH_ORDER_PUBLISHED)
+ {
+ return $GLOBALS['jonah_driver']->legacyGetStories($channel, $max, $from, $refresh, $date, $unreleased, $order);
+ }
+
+ /**
+ */
+ function _getExternalStories($channel, $max = 10, $from = 0, $refresh = false,
+ $date = null, $unreleased = false,
+ $order = JONAH_ORDER_PUBLISHED)
+ {
+ if ($refresh) {
+ $channel['channel_interval'] = -1;
+ }
+ $stories = $this->fetchExternalStories($channel['channel_id'], $channel['channel_url'], $channel['channel_interval']);
+ array_walk($stories, array($this, '_escapeExternalStories'), $channel);
+ if (!is_null($max)) {
+ $stories = array_slice($stories, $from, $max);
+ }
+ return $stories;
+ }
+
+ /**
+ */
+ function _escapeExternalStories(&$story, $key, $channel)
+ {
+ $story = array_merge($channel, $story);
+ $story['story_link'] = Horde::externalUrl($story['story_url']);
+ }
+
+ /**
+ */
+ function _getAggregatedStories($channel, $max = 10, $from = 0, $refresh = false,
+ $date = null, $unreleased = false,
+ $order = JONAH_ORDER_PUBLISHED)
+ {
+ switch ($order) {
+ case JONAH_ORDER_PUBLISHED:
+ $sort = 'story_published';
+ break;
+
+ case JONAH_ORDER_READ:
+ $sort = 'story_read';
+ break;
+
+ case JONAH_ORDER_COMMENTS:
+ //@TODO
+ break;
+ }
+
+ if ($refresh) {
+ $channel['channel_interval'] = -1;
+ }
+
+ $stories = array();
+ $date_format = $GLOBALS['prefs']->getValue('date_format');
+ $channels = explode(':', $channel['channel_url']);
+ foreach ($channels as $id) {
+ $channel_data = $this->getChannel($id);
+ if (is_a($channel_data, 'PEAR_Error')) {
+ continue;
+ }
+ $externals = $this->fetchExternalStories(null, $channel_data['channel_url'], $channel['channel_interval']);
+ if (!is_array($externals)) {
+ continue;
+ }
+
+ foreach ($externals as $external) {
+ $info = array();
+
+ /* Check if we have seen this story already. */
+ $story = $this->_getStoryByUrl($channel['channel_id'], $external['story_url']);
+ if (is_array($story)) {
+ /* Check if the story is unchanged. */
+ if ($this->getChecksum($story) == $this->getChecksum($external)) {
+ $story['story_updated_date'] = strftime($date_format, $story['story_updated']);
+ $story['story_link'] = Horde::externalUrl($story['story_url']);
+ $stories[] = array_merge($channel_data, $story);
+ continue;
+ }
+
+ $info['story_id'] = $story['story_id'];
+ }
+
+ $info['channel_id'] = $channel['channel_id'];
+ $info['story_title'] = $external['story_title'];
+ $info['story_desc'] = $external['story_desc'];
+ $info['story_url'] = $external['story_url'];
+ $info['story_link'] = Horde::externalUrl($external['story_url']);
+ $info['story_body'] = isset($external['story_body']) ? $external['story_body'] : null;
+ $info['story_body_type'] = isset($external['story_body_type']) ? $external['story_body_type'] : 'text';
+ $info['story_published'] = $external['story_published'];
+ $info['story_updated'] = $external['story_updated'];
+
+ $this->saveStory($info);
+
+ $info['story_updated_date'] = strftime($date_format, $external['story_updated']);
+ $stories[] = array_merge($channel_data, $info);
+ }
+ }
+
+ Horde_Array::arraySort($stories, $sort, 1);
+ if (!is_null($max)) {
+ $stories = array_slice($stories, $from, $max);
+ }
+
+ return $stories;
+ }
+
+ /**
+ */
+ function _getCompositeStories($channel, $max = 10, $from = 0, $refresh = false,
+ $date = null, $unreleased = false,
+ $order = JONAH_ORDER_PUBLISHED)
+ {
+ switch ($order) {
+ case JONAH_ORDER_PUBLISHED:
+ $sort = 'story_published';
+ break;
+
+ case JONAH_ORDER_READ:
+ $sort = 'story_read';
+ break;
+
+ case JONAH_ORDER_COMMENTS:
+ //@TODO
+ break;
+ }
+
+ $stories = array();
+ $channels = explode(':', $channel['channel_url']);
+ foreach ($channels as $subchannel) {
+ $stories = array_merge($stories, $this->getStories($subchannel, null, 0, $refresh, $date));
+ }
+ foreach ($stories as $key => $story) {
+ $stories[$key]['story_link'] = $this->getStoryLink($channel, $story);
+ }
+ Horde_Array::arraySort($stories, $sort, 1);
+ if (!is_null($max)) {
+ $stories = array_slice($stories, $from, $max);
+ }
+ return $stories;
+ }
+
+ /**
+ */
+ function saveStory(&$info)
+ {
+ /* Used for checking whether to send out delivery or not. */
+ if (empty($info['story_published'])) {
+ /* Story is not being released. */
+ $deliver = false;
+ } elseif (empty($info['story_id'])) {
+ /* Story is new. */
+ $deliver = true;
+ } else {
+ /* Story is old, has it been released already? */
+ $oldstory = $this->getStory(null, $info['story_id']);
+ if ((empty($oldstory['story_published']) ||
+ $oldstory['story_published'] > $oldstory['story_updated']) &&
+ $info['story_published'] <= time()) {
+ $deliver = true;
+ } else {
+ $deliver = false;
+ }
+ }
+
+ /* First save to the backend. */
+ $result = $this->_saveStory($info);
+ if (is_a($result, 'PEAR_Error') || !$deliver) {
+ /* Return here also if editing, do not bother doing deliveries for
+ * an edited story. */
+ return $result;
+ }
+ }
+
+ /**
+ */
+ function getStory($channel_id, $story_id, $read = false)
+ {
+ $channel = null;
+ if ($channel_id) {
+ $channel = $this->getChannel($channel_id);
+ if (is_a($channel, 'PEAR_Error')) {
+ return $channel;
+ }
+ if ($channel['channel_type'] == JONAH_EXTERNAL_CHANNEL) {
+ return $this->_getExternalStory($channel, $story_id);
+ }
+ }
+
+ $story = $this->_getStory($story_id, $read);
+ if (is_a($story, 'PEAR_Error')) {
+ return $story;
+ }
+
+ /* Format story link. */
+ $story['story_link'] = $this->getStoryLink($channel, $story);
+
+ /* Format dates. */
+ $date_format = $GLOBALS['prefs']->getValue('date_format');
+ $story['story_updated_date'] = strftime($date_format, $story['story_updated']);
+ if (!empty($story['story_published'])) {
+ $story['story_published_date'] = strftime($date_format, $story['story_published']);
+ }
+
+ return $story;
+ }
+
+ /**
+ * Returns the official link to a story.
+ *
+ * @param array $channel A channel hash.
+ * @param array $story A story hash.
+ *
+ * @return string The story link.
+ */
+ function getStoryLink($channel, $story)
+ {
+ if ((empty($story['story_url']) || !empty($story['story_body'])) &&
+ !empty($channel['channel_story_url'])) {
+ $url = $channel['channel_story_url'];
+ } else {
+ $url = Horde_Util::addParameter(Horde::applicationUrl('stories/view.php', true, -1), array('channel_id' => '%c', 'story_id' => '%s'), null, false);
+ }
+ return str_replace(array('%25c', '%25s', '%c', '%s'),
+ array('%c', '%s', $channel['channel_id'], $story['story_id']),
+ $url);
+ }
+
+ /**
+ */
+ function getChecksum($story)
+ {
+ return md5($story['story_title'] . $story['story_desc']);
+ }
+
+ /**
+ * Fetches a story list from an external channel, caching the data
+ * and only actually fetching if there is no valid cache.
+ *
+ * @param integer $channel_id The channel id to fetch.
+ * @param string $url The url from where to fetch the story list.
+ * @param integer $interval The interval in seconds since the last fetch
+ * after which the cache will be considered
+ * expired and an actual fetch will be done.
+ *
+ * @return array An array of available stories.
+ */
+ function fetchExternalStories($channel_id, $url, $interval)
+ {
+ require_once 'Horde/Cache.php';
+ require_once 'Horde/Serialize.php';
+
+ $cache = $GLOBALS['injector']->getInstance('Horde_Cache');
+ $timestamp = time();
+ if (is_a($cache, 'Horde_Cache') && ($stories = $cache->get($url, $interval))) {
+ $stories = Horde_Serialize::unserialize($stories, Horde_Serialize::UTF7_BASIC, Horde_Nls::getCharset());
+ } else {
+ $stories = Jonah_News::_fetchExternalStories($url, $timestamp);
+ $cache->set($url, Horde_Serialize::serialize($stories, Horde_Serialize::UTF7_BASIC, Horde_Nls::getCharset()));
+ }
+
+ /* If the stories from cache return the same timestamp as
+ * $timestamp it means that the cache has been refreshed. */
+ if ($channel_id !== null) {
+ if ($stories['timestamp'] == $timestamp) {
+ $this->_timestampChannel($channel_id, $timestamp);
+ }
+ }
+
+ unset($stories['timestamp']);
+ return $stories;
+ }
+
+ /**
+ */
+ function getIntervalLabel($seconds = null)
+ {
+ $interval = array(1 => _("none"),
+ 1800 => _("30 mins"),
+ 3600 => _("1 hour"),
+ 7200 => _("2 hours"),
+ 14400 => _("4 hours"),
+ 28800 => _("8 hours"),
+ 43200 => _("12 hours"),
+ 86400 => _("24 hours"));
+
+ if ($seconds === null) {
+ return $interval;
+ } else {
+ return $interval[$seconds];
+ }
+ }
+
+ /**
+ * Returns the available channel types based on what was set in the
+ * configuration.
+ *
+ * @return array The available news channel types.
+ */
+ function getAvailableTypes()
+ {
+ if (isset($types)) {
+ return $types;
+ }
+
+ static $types = array();
+
+ if (empty($GLOBALS['conf']['news']['enable'])) {
+ return $types;
+ }
+ if (in_array('external', $GLOBALS['conf']['news']['enable'])) {
+ $types[JONAH_EXTERNAL_CHANNEL] = _("External Feed");
+ }
+ if (in_array('internal', $GLOBALS['conf']['news']['enable'])) {
+ $types[JONAH_INTERNAL_CHANNEL] = _("Local Feed");
+ }
+ if (in_array('aggregated', $GLOBALS['conf']['news']['enable'])) {
+ $types[JONAH_AGGREGATED_CHANNEL] = _("Aggregated Feed");
+ }
+ if (in_array('composite', $GLOBALS['conf']['news']['enable'])) {
+ $types[JONAH_COMPOSITE_CHANNEL] = _("Composite Feed");
+ }
+
+ return $types;
+ }
+
+ /**
+ * Returns the default channel type based on what was set in the
+ * configuration.
+ *
+ * @return integer The default news channel type.
+ */
+ function getDefaultType()
+ {
+ if (in_array('external', $GLOBALS['conf']['news']['enable'])) {
+ return JONAH_EXTERNAL_CHANNEL;
+ } else {
+ return JONAH_INTERNAL_CHANNEL;
+ }
+ }
+
+ /**
+ * Returns the stories of a channel rendered with the specified template.
+ *
+ * @param integer $channel_id The news channel to get stories from.
+ * @param string $tpl The name of the template to use.
+ * @param integer $max The maximum number of stories to get. If
+ * null, all stories will be returned.
+ * @param integer $from The number of the story to start with.
+ * @param integer $order How to sort the results for internal channels
+ * Possible values are the JONAH_ORDER_*
+ * constants.
+ *
+ * @return string The rendered story listing.
+ */
+ function renderChannel($channel_id, $tpl, $max = 10, $from = 0, $order = JONAH_ORDER_PUBLISHED)
+ {
+ $channel = $this->getChannel($channel_id);
+ if (is_a($channel, 'PEAR_Error')) {
+ return sprintf(_("Error fetching feed: %s"), $channel->getMessage());
+ }
+
+ include JONAH_BASE . '/config/templates.php';
+ $escape = !isset($templates[$tpl]['escape']) ||
+ !empty($templates[$tpl]['escape']);
+ $template = new Horde_Template();
+
+ if ($escape) {
+ $channel['channel_name'] = htmlspecialchars($channel['channel_name']);
+ $channel['channel_desc'] = htmlspecialchars($channel['channel_desc']);
+ }
+ $template->set('channel', $channel, true);
+
+ /* Get one story more than requested to see if there are more
+ * stories. */
+ if ($max !== null) {
+ $stories = $this->getStories($channel_id, $max + 1, $from, false, time(), false, $order);
+ if (is_a($stories, 'PEAR_Error')) {
+ return $stories->getMessage();
+ }
+ } else {
+ $stories = $this->getStories($channel_id, null, 0, false, time(), false, $order);
+ if (is_a($stories, 'PEAR_Error')) {
+ return $stories->getMessage();
+ }
+ $max = count($stories);
+ }
+
+ if (!$stories) {
+ $template->set('error', _("No stories are currently available."), true);
+ $template->set('stories', false, true);
+ $template->set('image', false, true);
+ $template->set('form', false, true);
+ } else {
+ /* Escape. */
+ if ($escape) {
+ array_walk($stories, array($this, '_escapeStories'));
+ }
+
+ /* Process story summaries. */
+ array_walk($stories, array($this, '_escapeStoryDescriptions'));
+
+ $template->set('error', false, true);
+ $template->set('story_marker', Horde::img('story_marker.png'));
+ $template->set('image', false, true);
+ $template->set('form', false, true);
+ if ($from) {
+ $template->set('previous', max(0, $from - $max), true);
+ } else {
+ $template->set('previous', false, true);
+ }
+ if ($from && !empty($channel['channel_page_link'])) {
+ $template->set('previous_link',
+ str_replace(
+ array('%25c', '%25n', '%c', '%n'),
+ array('%c', '%n', $channel['channel_id'], max(0, $from - $max)),
+ $channel['channel_page_link']),
+ true);
+ } else {
+ $template->set('previous_link', false, true);
+ }
+ $more = count($stories) > $max;
+ if ($more) {
+ $template->set('next', $from + $max, true);
+ array_pop($stories);
+ } else {
+ $template->set('next', false, true);
+ }
+ if ($more && !empty($channel['channel_page_link'])) {
+ $template->set('next_link',
+ str_replace(
+ array('%25c', '%25n', '%c', '%n'),
+ array('%c', '%n', $channel['channel_id'], $from + $max),
+ $channel['channel_page_link']),
+ true);
+ } else {
+ $template->set('next_link', false, true);
+ }
+
+ $template->set('stories', $stories, true);
+ }
+
+ return $template->parse($templates[$tpl]['template']);
+ }
+
+ /**
+ */
+ function _escapeStories(&$value, $key)
+ {
+ $value['story_title'] = htmlspecialchars($value['story_title']);
+ $value['story_desc'] = htmlspecialchars($value['story_desc']);
+ if (isset($value['story_link'])) {
+ $value['story_link'] = htmlspecialchars($value['story_link']);
+ }
+ if (empty($value['story_body_type']) || $value['story_body_type'] != 'richtext') {
+ $value['story_body'] = htmlspecialchars($value['story_body']);
+ }
+ }
+
+ /**
+ */
+ function _escapeStoryDescriptions(&$value, $key)
+ {
+ $value['story_desc'] = nl2br($value['story_desc']);
+ }
+
+ /**
+ * Returns the provided story as a MIME part.
+ *
+ * @param array $story A data array representing a story.
+ *
+ * @return MIME_Part The MIME message part containing the story parts.
+ */
+ function &getStoryAsMessage(&$story)
+ {
+ require_once 'Horde/MIME/Part.php';
+
+ /* Add the story to the message based on the story's body type. */
+ switch ($story['story_body_type']) {
+ case 'richtext':
+ /* Get a plain text version of a richtext story. */
+ $body_html = $story['story_body'];
+ $body_text = Horde_Text_Filter::filter($body_html, 'html2text');
+
+ /* Add description. */
+ $body_html = '<p>' . Horde_Text_Filter::filter($story['story_desc'], 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::MICRO, 'charset' => Horde_Nls::getCharset(), 'class' => null, 'callback' => null)) . "</p>\n" . $body_html;
+ $body_text = Horde_String::wrap(' ' . $story['story_desc'], 70) . "\n\n" . $body_text;
+
+ /* Add the text version of the story to the base message. */
+ $message_text = new MIME_Part('text/plain');
+ $message_text->setCharset(Horde_Nls::getCharset());
+ $message_text->setContents($message_text->replaceEOL($body_text));
+ $message_text->setDescription(_("Plaintext Version of Story"));
+
+ /* Add an HTML version of the story to the base message. */
+ $message_html = new MIME_Part('text/html', Horde_String::wrap($body_html),
+ Horde_Nls::getCharset(), 'inline');
+ $message_html->setDescription(_("HTML Version of Story"));
+
+ /* Add the two parts as multipart/alternative. */
+ $basepart = new MIME_Part('multipart/alternative');
+ $basepart->addPart($message_text);
+ $basepart->addPart($message_html);
+
+ return $basepart;
+
+ case 'text':
+ /* This is just a plain text story. */
+ $message_text = new MIME_Part('text/plain');
+ $message_text->setContents($message_text->replaceEOL($story['story_desc'] . "\n\n" . $story['story_body']));
+ $message_text->setCharset(Horde_Nls::getCharset());
+
+ return $message_text;
+ }
+ }
+
+ /**
+ * Attempts to return a concrete Jonah_News instance based on $driver.
+ *
+ * @param string $driver The type of concrete Jonah_News subclass to
+ * return. The is based on the storage driver
+ * ($driver). The code is dynamically included.
+ *
+ * @param array $params A hash containing any additional configuration or
+ * connection parameters a subclass might need.
+ *
+ * @return mixed The newly created concrete Jonah_News instance, or false
+ * on an error.
+ */
+ function factory($driver = null, $params = null)
+ {
+ if ($driver === null) {
+ $driver = $GLOBALS['conf']['news']['storage']['driver'];
+ }
+ $driver = basename($driver);
+
+ if ($params === null) {
+ $params = Horde::getDriverConfig(array('news', 'storage'), $driver);
+ }
+
+ $class = 'Jonah_News_' . $driver;
+ if (!class_exists($class, false)) {
+ include dirname(__FILE__) . '/News/' . $driver . '.php';
+ }
+ if (class_exists($class)) {
+ return new $class($params);
+ } else {
+ return PEAR::raiseError(sprintf(_("No such backend \"%s\" found"), $driver));
+ }
+ }
+
+ /**
+ * Get the full body of an external feed story.
+ *
+ * @access private
+ *
+ * @param array $channel The channel the story belongs to.
+ * @param integer $story_id The id of the story in the channel.
+ *
+ * @return array The story array.
+ */
+ function _getExternalStory($channel, $story_id)
+ {
+ $stories = $this->fetchExternalStories($channel['channel_id'], $channel['channel_url'], $channel['channel_interval']);
+ if (isset($stories[$story_id])) {
+ return $stories[$story_id];
+ }
+
+ return PEAR::raiseError(sprintf(_("Story \"%s\" not found in \"%s\"."), $story_id, $channel['channel_title']));
+ }
+
+ /**
+ * This function is called if cached data isn't available and is what
+ * actually does the URL fetching and XML parsing to get the story list.
+ * It is only called if the cached data has expired or didn't exist.
+ *
+ * @access private
+ *
+ * @param string $url The url from where to fetch the story list.
+ * @param integer $timestamp Timestamp of this fetch.
+ *
+ * @return array An array of available stories.
+ */
+ function _fetchExternalStories($url, $timestamp)
+ {
+ $xml = Jonah::_readURL($url);
+ if (is_a($xml, 'PEAR_Error')) {
+ Horde::logMessage($xml, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return array('timestamp' => $timestamp);
+ }
+
+ /* Parse the feed. */
+ require_once JONAH_BASE . '/lib/FeedParser.php';
+ $charset = empty($xml['charset']) ? 'utf-8' : $xml['charset'];
+ $parser = new Jonah_FeedParser($charset);
+ if (!$parser->parse($xml['body'])) {
+ if (isset($GLOBALS['notification'])) {
+ $GLOBALS['notification']->push(sprintf(_("Error parsing external feed from %s: %s"), $url, $parser->error), 'warning');
+ } else {
+ Horde::logMessage(sprintf("Error parsing external feed from %s: %s", $url, $parser->error), __FILE__, __LINE__, PEAR_LOG_ERR);
+ }
+ }
+ $stories = $parser->structure;
+ $parser->cleanup();
+
+ /* Set the passed timestamp. */
+ $items = array('timestamp' => $timestamp);
+ if (!empty($stories['items'])) {
+ foreach ($stories['items'] as $key => $story) {
+ $items[$key]['story_id'] = $key;
+ $items[$key]['story_title'] = isset($story['title']) ? Horde_String::convertCharset($story['title'], 'utf-8') : _("[No title]");
+ $items[$key]['story_desc'] = isset($story['description']) ? Horde_String::convertCharset($story['description'], 'utf-8') : '';
+ $items[$key]['story_url'] = isset($story['link']) ? $story['link'] : '';
+
+ /* Set the body, and filter it if it's HTML */
+ $items[$key]['story_body'] = isset($story['body']) ? Horde_String::convertCharset($story['body'], 'utf-8') : null;
+ $items[$key]['story_body_type'] = isset($story['body_type']) ? Horde_String::convertCharset($story['body_type'], 'utf-8') : 'text';
+ if ($items[$key]['story_body_type'] == 'html') {
+ $items[$key]['story_body'] = Horde_Text_Filter::filter($items[$key]['story_body'], 'xss');
+ }
+
+ if (isset($story['pubdate']) && $pubdate_dt = strtotime($story['pubdate'])) {
+ $items[$key]['story_published'] = $pubdate_dt;
+ } else {
+ $items[$key]['story_published'] = $timestamp;
+ }
+
+ if (isset($story['moddate']) && $moddate_dt = strtotime($story['moddate'])) {
+ $items[$key]['story_updated'] = $moddate_dt;
+ } else {
+ $items[$key]['story_updated'] = $timestamp;
+ }
+
+ // Media related
+ if (isset($story['media:content']) && is_array($story['media:content'])) {
+ $items[$key]['story_media_content_url'] = $story['media:content']['url'];
+ $items[$key]['story_media_content_type'] = empty($story['media:content']['type']) ? null : $story['media:content']['type'];
+ $items[$key]['story_media_title'] = isset($story['media:title']) ? $story['media:title'] : '';
+ $items[$key]['story_media_description'] = isset($story['media:description']['value']) ? $story['media:description']['value'] : '';
+ $items[$key]['story_media_description_type'] = isset($story['media:description']['type']) ? $story['media:description']['type'] : '';
+ $items[$key]['story_media_thumbnail_url'] = (isset($story['media:thumbnail']['url']) ? $story['media:thumbnail']['url'] : (isset($story['media:content']['url']) ? $story['media:content']['url'] : ''));
+ }
+ }
+ }
+
+ return $items;
+ }
+
+ /**
+ * Stubs for the tag functions. If supported by the backend, these need
+ * to be implemented in the concrete Jonah_News_* class.
+ */
+ function writeTags($resource_id, $channel_id, $tags)
+ {
+ return PEAR::raiseError(_("Tag support not enabled in backend."));
+ }
+
+ function readTags($resource_id)
+ {
+ return PEAR::raiseError(_("Tag support not enabled in backend."));
+ }
+
+ function listTagInfo($tags = array(), $channel_id = null)
+ {
+ return PEAR::raiseError(_("Tag support not enabled in backend."));
+ }
+
+ function searchTagsById($ids, $max = 10, $from = 0, $channel_id = array(),
+ $order = JONAH_ORDER_PUBLISHED)
+ {
+ return PEAR::raiseError(_("Tag support not enabled in backend."));
+ }
+
+ function getTagNames($ids)
+ {
+ return PEAR::raiseError(_("Tag support not enabled in backend."));
+ }
+
+
+}
--- /dev/null
+<?php
+/**
+ * Jonah storage implementation for PHP's PEAR database abstraction layer.
+ *
+ * Required values for $params:<pre>
+ * 'phptype' The database type (e.g. 'pgsql', 'mysql', etc.).
+ * 'charset' The database's internal charset.</pre>
+ *
+ * Required by some database implementations:<pre>
+ * 'hostspec' The hostname of the database server.
+ * 'protocol' The communication protocol ('tcp', 'unix', etc.).
+ * 'database' The name of the database.
+ * 'username' The username with which to connect to the database.
+ * 'password' The password associated with 'username'.
+ * 'options' Additional options to pass to the database.
+ * 'tty' The TTY on which to connect to the database.
+ * 'port' The port on which to connect to the database.</pre>
+ *
+ * The table structure can be created by the scripts/db/jonah_news.sql
+ * script. The needed tables are jonah_channels and jonah_stories.
+ *
+ * $Horde: jonah/lib/News/sql.php,v 1.119 2010/02/01 10:32:04 jan Exp $
+ *
+ * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (BSD). If you did not
+ * did not receive this file, see http://cvs.horde.org/co.php/jonah/LICENSE.
+ *
+ * @author Marko Djukic <marko@oblo.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Jan Schneider <jan@horde.org>
+ * @package Jonah
+ */
+class Jonah_News_sql extends Jonah_News {
+
+ /**
+ * Handle for the current database connection.
+ *
+ * @var DB
+ */
+ var $_db;
+
+ /**
+ * Boolean indicating whether or not we're connected to the SQL server.
+ *
+ * @var boolean
+ */
+ var $_connected = false;
+
+ /**
+ * Saves a channel to the backend.
+ *
+ * @param array $info The channel to add.
+ * Must contain a combination of the following
+ * entries:
+ * <pre>
+ * 'channel_id' If empty a new channel is being added, otherwise one
+ * is being edited.
+ * 'channel_name' The headline.
+ * 'channel_desc' A description of this channel.
+ * 'channel_type' Whether internal or external.
+ * 'channel_interval' If external then interval at which to refresh.
+ * 'channel_link' The link to the source.
+ * 'channel_url' The url from where to fetch the story list.
+ * 'channel_image' A channel image.
+ * </pre>
+ *
+ * @return int|PEAR_Error The channel ID on success, PEAR_Error on
+ * failure.
+ */
+ function saveChannel(&$info)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ if (empty($info['channel_id'])) {
+ $info['channel_id'] = $this->_db->nextId('jonah_channels');
+ if (is_a($info['channel_id'], 'PEAR_Error')) {
+ Horde::logMessage($info['channel_id'], __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $info['channel_id'];
+ }
+ $sql = 'INSERT INTO jonah_channels' .
+ ' (channel_id, channel_name, channel_slug, channel_type, channel_full_feed, channel_desc, channel_interval, channel_url, channel_link, channel_page_link, channel_story_url, channel_img)' .
+ ' VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
+ $values = array();
+ } else {
+ $sql = 'UPDATE jonah_channels' .
+ ' SET channel_id = ?, channel_name = ?, channel_slug = ?, channel_type = ?, channel_full_feed = ?, channel_desc = ?, channel_interval = ?, channel_url = ?, channel_link = ?, channel_page_link = ?, channel_story_url = ?, channel_img = ?' .
+ ' WHERE channel_id = ?';
+ $values = array((int)$info['channel_id']);
+ }
+
+ array_unshift($values,
+ (int)$info['channel_id'],
+ Horde_String::convertCharset($info['channel_name'], Horde_Nls::getCharset(), $this->_params['charset']),
+ isset($info['channel_slug']) ? $info['channel_slug'] : '',
+ (int)$info['channel_type'],
+ (int)$info['channel_full_feed'],
+ isset($info['channel_desc']) ? $info['channel_desc'] : null,
+ isset($info['channel_interval']) ? (int)$info['channel_interval'] : null,
+ isset($info['channel_url']) ? $info['channel_url'] : null,
+ isset($info['channel_link']) ? $info['channel_link'] : null,
+ isset($info['channel_page_link']) ? $info['channel_page_link'] : null,
+ isset($info['channel_story_url']) ? $info['channel_story_url'] : null,
+ isset($info['channel_img']) ? $info['channel_img'] : null);
+ Horde::logMessage('SQL Query by Jonah_News_sql::saveChannel(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->query($sql, $values);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ }
+
+ return $info['channel_id'];
+ }
+
+ /**
+ * Get a list of stored channels.
+ *
+ * @param integer $type The type of channel to filter for. Possible
+ * values are either JONAH_INTERNAL_CHANNEL
+ * to fetch only a list of internal channels,
+ * or JONAH_EXTERNAL_CHANNEL for only external.
+ * If null both channel types are returned.
+ *
+ * @return mixed An array of channels or PEAR_Error on error.
+ */
+ function getChannels($type = null)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ $wsql = '';
+ if (!is_null($type)) {
+ if (!is_array($type)) {
+ $type = array($type);
+ }
+ for ($i = 0, $i_max = count($type); $i < $i_max; ++$i) {
+ $type[$i] = 'channel_type = ' . (int)$type[$i];
+ }
+ $wsql = 'WHERE ' . implode(' OR ', $type);
+ }
+
+ $sql = sprintf('SELECT channel_id, channel_name, channel_type, channel_updated FROM jonah_channels %s ORDER BY channel_name', $wsql);
+
+ Horde::logMessage('SQL Query by Jonah_News_sql::getChannels(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->getAll($sql, DB_FETCHMODE_ASSOC);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ }
+ for ($i = 0; $i < count($result); $i++) {
+ $result[$i]['channel_name'] = Horde_String::convertCharset($result[$i]['channel_name'], $this->_params['charset']);
+ }
+
+ return $result;
+ }
+
+ /**
+ */
+ function _getChannel($channel_id)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ $sql = 'SELECT * FROM jonah_channels WHERE channel_id = ' . (int)$channel_id;
+
+ Horde::logMessage('SQL Query by Jonah_News_sql::_getChannel(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->getRow($sql, DB_FETCHMODE_ASSOC);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ } elseif (empty($result)) {
+ return PEAR::raiseError(sprintf(_("Channel id \"%s\" not found."), $channel_id));
+ }
+
+ $result['channel_name'] = Horde_String::convertCharset($result['channel_name'], $this->_params['charset']);
+ if ($result['channel_type'] == JONAH_COMPOSITE_CHANNEL) {
+ $channels = explode(':', $result['channel_url']);
+ if (count($channels)) {
+ $sql = 'SELECT MAX(channel_updated) FROM jonah_channels WHERE channel_id IN (' . implode(',', $channels) . ')';
+ Horde::logMessage('SQL Query by Jonah_News_sql::_getChannel(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $updated = $this->_db->getOne($sql);
+ if (is_a($updated, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ } else {
+ $result['channel_updated'] = $updated;
+ $this->_timestampChannel($channel_id, $updated);
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ */
+ function _timestampChannel($channel_id, $timestamp)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ $sql = sprintf('UPDATE jonah_channels SET channel_updated = %s WHERE channel_id = %s',
+ (int)$timestamp,
+ (int)$channel_id);
+ Horde::logMessage('SQL Query by Jonah_News_sql::_timestampChannel(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->query($sql);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ }
+ return $result;
+ }
+
+ /**
+ */
+ function _readStory($story_id)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ $sql = 'UPDATE jonah_stories SET story_read = story_read + 1 WHERE story_id = ' . (int)$story_id;
+ Horde::logMessage('SQL Query by Jonah_News_sql::_readStory(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->query($sql);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ }
+ return $result;
+ }
+
+ /**
+ */
+ function _deleteChannel($channel_id)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ $sql = 'DELETE FROM jonah_channels WHERE channel_id = ?';
+ $values = array($channel_id);
+
+ Horde::logMessage('SQL Query by Jonah_News_sql::deleteChannel(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->query($sql, $values);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param array &$info
+ */
+ function _saveStory(&$info)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ if (empty($info['story_id'])) {
+ $info['story_id'] = $this->_db->nextId('jonah_stories');
+ if (is_a($info['story_id'], 'PEAR_Error')) {
+ Horde::logMessage($info['story_id'], __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $info['story_id'];
+ }
+ $channel = $this->getChannel($info['channel_id']);
+ $permalink = $this->getStoryLink($channel, $info);
+ $sql = 'INSERT INTO jonah_stories (story_id, channel_id, story_title, story_desc, story_body_type, story_body, story_url, story_published, story_updated, story_read, story_permalink, story_author) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
+ $values = array($permalink, Horde_Auth::getAuth());
+ } else {
+ $sql = 'UPDATE jonah_stories SET story_id = ?, channel_id = ?, story_title = ?, story_desc = ?, story_body_type = ?, story_body = ?, story_url = ?, story_published = ?, story_updated = ?, story_read = ? WHERE story_id = ?';
+ $values = array((int)$info['story_id']);
+ }
+
+ if (empty($info['story_read'])) {
+ $info['story_read'] = 0;
+ }
+
+ /* Deal with any tags */
+ if (!empty($info['story_tags'])) {
+ $tags = explode(',', $info['story_tags']);
+ } else {
+ $tags = array();
+ }
+ $this->writeTags($info['story_id'], $info['channel_id'], $tags);
+
+ array_unshift($values,
+ (int)$info['story_id'],
+ (int)$info['channel_id'],
+ Horde_String::convertCharset($info['story_title'], Horde_Nls::getCharset(), $this->_params['charset']),
+ Horde_String::convertCharset($info['story_desc'], Horde_Nls::getCharset(), $this->_params['charset']),
+ $info['story_body_type'],
+ isset($info['story_body']) ? Horde_String::convertCharset($info['story_body'], Horde_Nls::getCharset(), $this->_params['charset']) : null,
+ isset($info['story_url']) ? $info['story_url'] : null,
+ isset($info['story_published']) ? (int)$info['story_published'] : null,
+ time(),
+ (int)$info['story_read']);
+
+ Horde::logMessage('SQL Query by Jonah_News_sql::_saveStory(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->query($sql, $values);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ }
+
+ $this->_timestampChannel($info['channel_id'], time());
+ return true;
+ }
+
+ /**
+ * Converts the text fields of a story from the backend charset to the
+ * output charset.
+ *
+ * @param array $story A story hash.
+ *
+ * @return array The converted hash.
+ */
+ function _convertFromBackend($story)
+ {
+ $story['story_title'] = Horde_String::convertCharset($story['story_title'], $this->_params['charset'], Horde_Nls::getCharset());
+ $story['story_desc'] = Horde_String::convertCharset($story['story_desc'], $this->_params['charset'], Horde_Nls::getCharset());
+ if (isset($story['story_body'])) {
+ $story['story_body'] = Horde_String::convertCharset($story['story_body'], $this->_params['charset'], Horde_Nls::getCharset());
+ }
+ if (isset($story['story_tags'])) {
+ $story['story_tags'] = Horde_String::convertCharset($story['story_tags'], $this->_params['charset'], Horde_Nls::getCharset());
+ }
+ return $story;
+ }
+
+ /**
+ * Returns the most recent or all stories from a channel.
+ *
+ * @param integer $channel_id The news channel to get stories from.
+ * @param integer $max The maximum number of stories to get.
+ * @param integer $from The number of the story to start with.
+ * @param integer $date The timestamp of the date to start with.
+ * @param boolean $unreleased Whether to return not yet released stories.
+ * @param integer $order How to order the results for internal
+ * channels. Possible values are the
+ * JONAH_ORDER_* constants.
+ *
+ * @return array The specified number (or less, if there are fewer) of
+ * stories from the given channel.
+ */
+ function _getStories($channel_id, $max, $from = 0, $date = null,
+ $unreleased = false, $order = JONAH_ORDER_PUBLISHED)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ $sql = 'SELECT * FROM jonah_stories WHERE channel_id = ?';
+ $values = array((int)$channel_id);
+
+ if ($unreleased) {
+ if ($date !== null) {
+ $sql .= ' AND story_published <= ?';
+ $values[] = $date;
+ }
+ } else {
+ if ($date === null) {
+ $date = time();
+ } else {
+ $date = max($date, time());
+ }
+ $sql .= ' AND story_published <= ?';
+ $values[] = $date;
+ }
+
+ switch ($order) {
+ case JONAH_ORDER_PUBLISHED:
+ $sql .= ' ORDER BY story_published DESC';
+ break;
+ case JONAH_ORDER_READ:
+ $sql .= ' ORDER BY story_read DESC';
+ break;
+ case JONAH_ORDER_COMMENTS:
+ //@TODO
+ break;
+ }
+
+ if (!is_null($max)) {
+ $sql = $this->_db->modifyLimitQuery($sql, (int)$from, (int)$max, $values);
+ }
+
+ Horde::logMessage('SQL Query by Jonah_News_sql::_getStories(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->getAll($sql, $values, DB_FETCHMODE_ASSOC);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ for ($i = 0; $i < count($result); $i++) {
+ $result[$i] = $this->_convertFromBackend($result[$i]);
+ if (empty($result[$i]['story_permalink'])) {
+ $this->_addPermalink($result[$i]);
+ }
+ $tags = $this->readTags($result[$i]['story_id']);
+ if (is_a($tags, 'PEAR_Error')) {
+ return $tags;
+ }
+ $result[$i]['story_tags'] = $tags;
+ }
+ return $result;
+ }
+
+ /**
+ */
+ function _getStory($story_id, $read = false)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ $sql = 'SELECT * FROM jonah_stories WHERE story_id = ?';
+ $values = array((int)$story_id);
+
+ Horde::logMessage('SQL Query by Jonah_News_sql::_getStory(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->getRow($sql, $values, DB_FETCHMODE_ASSOC);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ } elseif (empty($result)) {
+ return PEAR::raiseError(sprintf(_("Story id \"%s\" not found."), $story_id));
+ }
+ $result['story_tags'] = $this->readTags($story_id);
+ $result = $this->_convertFromBackend($result);
+ if (empty($result['story_permalink'])) {
+ $this->_addPermalink($result);
+ }
+ if ($read) {
+ $this->_readStory($story_id);
+ }
+
+ return $result;
+ }
+
+ /**
+ */
+ function _getStoryByUrl($channel_id, $story_url)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ $sql = 'SELECT * FROM jonah_stories' .
+ ' WHERE channel_id = ? AND story_url = ?';
+ $values = array((int)$channel_id, $story_url);
+
+ Horde::logMessage('SQL Query by Jonah_News_sql::_getStoryByUrl(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->getRow($sql, $values, DB_FETCHMODE_ASSOC);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ } elseif (empty($result)) {
+ return PEAR::raiseError(sprintf(_("Story URL \"%s\" not found."), $story_url));
+ }
+ $result = $this->_convertFromBackend($result);
+ if (empty($result['story_permalink'])) {
+ $this->_addPermalink($result);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Adds a missing permalink to a story.
+ *
+ * @param array $story A story hash.
+ */
+ function _addPermalink(&$story)
+ {
+ $channel = $this->getChannel($story['channel_id']);
+ if (is_a($channel, 'PEAR_Error')) {
+ return;
+ }
+ $sql = 'UPDATE jonah_stories SET story_permalink = ? WHERE story_id = ?';
+ $values = array($this->getStoryLink($channel, $story), $story['story_id']);
+ Horde::logMessage('SQL Query by Jonah_News_sql::_addPermalink(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->query($sql, $values);
+ if (!is_a($result, 'PEAR_Error')) {
+ $story['story_permalink'] = $values[0];
+ }
+ }
+
+ /**
+ * Gets the latest released story from a given internal channel
+ *
+ * @param int $channel_id The channel id.
+ *
+ * @return int The story id.
+ */
+ function getLatestStoryId($channel_id)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ $sql = 'SELECT story_id FROM jonah_stories' .
+ ' WHERE channel_id = ? AND story_published <= ?' .
+ ' ORDER BY story_updated DESC';
+ $values = array((int)$channel_id, time());
+
+ Horde::logMessage('SQL Query by Jonah_News_sql::getLatestStoryId(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->getRow($sql, $values, DB_FETCHMODE_ASSOC);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ } elseif (empty($result)) {
+ return PEAR::raiseError(sprintf(_("Channel \"%s\" not found."), $channel_id));
+ }
+
+ return $result['story_id'];
+ }
+
+ /**
+ */
+ function deleteStory($channel_id, $story_id)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ $sql = 'DELETE FROM jonah_stories' .
+ ' WHERE channel_id = ? AND story_id = ?';
+ $values = array((int)$channel_id, (int)$story_id);
+
+ Horde::logMessage('SQL Query by Jonah_News_sql::deleteStory(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $result = $this->_db->query($sql, $values);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result->getMessage(), __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ }
+
+ $sql = 'DELETE FROM jonah_stories_tags ' .
+ 'WHERE channel_id = ? AND story_id = ?';
+ $result = $this->_db->query($sql, $values);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result->getMessage(), __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ }
+
+ return true;
+ }
+
+ /**
+ * Write out the tags for a specific resource.
+ *
+ * @param int $resource_id The story we are tagging.
+ * @param int $channel_id The channel id for the story we are tagging
+ * @param array $tags An array of tags.
+ *
+ * @return mixed True | PEAR_Error
+ */
+ function writeTags($resource_id, $channel_id, $tags)
+ {
+ global $conf;
+
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+ // First, make sure all tag names exist in the DB.
+ $tagkeys = array();
+ $insert = $this->_db->prepare('INSERT INTO jonah_tags (tag_id, tag_name) VALUES(?, ?)');
+ $query = $this->_db->prepare('SELECT tag_id FROM jonah_tags WHERE tag_name = ?');
+ foreach ($tags as $tag) {
+ $tag = Horde_String::lower(trim($tag));
+ $results = $this->_db->execute($query, $this->_db->escapeSimple($tag));
+ if (is_a($results, 'PEAR_Error')) {
+ return $results;
+ } elseif ($results->numRows() == 0) {
+ $id = $this->_db->nextId('jonah_tags');
+ $result = $this->_db->execute($insert, array($id, $tag));
+ $tagkeys[] = $id;
+ } else {
+ $row = $results->fetchRow(DB_FETCHMODE_ASSOC);
+ $tagkeys[] = $row['tag_id'];
+ }
+ }
+
+ // Free our resources.
+ $this->_db->freePrepared($insert, true);
+ $this->_db->freePrepared($query, true);
+
+ $sql = 'DELETE FROM jonah_stories_tags WHERE story_id = ' . (int)$resource_id;
+ $query = $this->_db->prepare('INSERT INTO jonah_stories_tags (story_id, channel_id, tag_id) VALUES(?, ?, ?)');
+
+ Horde::logMessage('SQL query by Jonah_News_sql::writeTags: ' . $sql,
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ $this->_db->query($sql);
+ foreach ($tagkeys as $key) {
+ $this->_db->execute($query, array($resource_id, $channel_id, $key));
+ }
+ $this->_db->freePrepared($query, true);
+
+ /* @TODO We should clear at least any of our cached counts */
+ return true;
+ }
+
+ /**
+ * Retrieve the tags for a specified resource.
+ *
+ * @param int $resource_id The resource to get tags for.
+ *
+ * @return mixed An array of tags | PEAR_Error
+ */
+ function readTags($resource_id)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+ $sql = 'SELECT jonah_tags.tag_id, tag_name FROM jonah_tags INNER JOIN jonah_stories_tags ON jonah_stories_tags.tag_id = jonah_tags.tag_id WHERE jonah_stories_tags.story_id = ?';
+
+ Horde::logMessage('SQL query by Jonah_News_sql::readTags ' . $sql,
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ $tags = $this->_db->getAssoc($sql, false, array($resource_id), false);
+ return $tags;
+ }
+
+ /**
+ * Retrieve the list of used tag_names, tag_ids and the total number
+ * of resources that are linked to that tag.
+ *
+ * @param array $tags An optional array of tag_ids. If omitted, all tags
+ * will be included.
+ *
+ * @param array $channel_id An optional array of channel_ids.
+ *
+ * @return mixed An array containing tag_name, and total | PEAR_Error
+ */
+ function listTagInfo($tags = array(), $channel_id = null)
+ {
+ require_once 'Horde/Cache.php';
+
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+
+ if (!is_array($channel_id) && is_numeric($channel_id)) {
+ $channel_id = array($channel_id);
+ }
+ $cache = $GLOBALS['injector']->getInstance('Horde_Cache');
+ $cache_key = 'jonah_tags_' . md5(serialize($tags) . md5(serialize($channel_id)));
+ $cache_value = $cache->get($cache_key, $GLOBALS['conf']['cache']['default_lifetime']);
+ if ($cache_value) {
+ return unserialize($cache_value);
+ }
+
+ $haveWhere = false;
+ $sql = 'SELECT tn.tag_id, tag_name, COUNT(tag_name) total FROM jonah_tags as tn INNER JOIN jonah_stories_tags as t ON t.tag_id = tn.tag_id';
+ if (count($tags)) {
+ $sql .= ' WHERE tn.tag_id IN (' . implode(',', $tags) . ')';
+ $haveWhere = true;
+ }
+ if (!is_null($channel_id)) {
+ if (!$haveWhere) {
+ $sql .= ' WHERE';
+ } else {
+ $sql .= ' AND';
+ }
+ $channels = array();
+ foreach ($channel_id as $cid) {
+ $c = $this->_getChannel($cid);
+ if ($c['channel_type'] == JONAH_COMPOSITE_CHANNEL) {
+ $channels = array_merge($channels, explode(':', $c['channel_url']));
+ }
+ }
+ $channel_id = array_merge($channel_id, $channels);
+ $sql .= ' t.channel_id IN (' . implode(', ', $channel_id) . ')';
+ }
+ $sql .= ' GROUP BY tn.tag_id, tag_name ORDER BY total DESC;';
+ $results = $this->_db->getAssoc($sql,true, array(), DB_FETCHMODE_ASSOC, false);
+ $cache->set($cache_key, serialize($results));
+ return $results;
+ }
+
+ /**
+ * Search for resources matching the specified criteria
+ *
+ * @param array $ids An array of tag_ids to search for. Note that
+ * these are AND'd together.
+ * @param integer $max The maximum number of stories to get. If
+ * null, all stories will be returned.
+ * @param integer $from The number of the story to start with.
+ * @param array $channel Limit the result set to resources
+ * present in these channels
+ * @param integer $order How to order the results for internal
+ * channels. Possible values are the
+ * JONAH_ORDER_* constants.
+ *
+ * @return mixed Array of stories| PEAR_Error
+ */
+ function searchTagsById($ids, $max = 10, $from = 0, $channel_id = array(),
+ $order = JONAH_ORDER_PUBLISHED)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+ if (!is_array($ids) || !count($ids)) {
+ $stories[] = array();
+ } else {
+ $stories = array();
+ $sql = 'SELECT DISTINCT s.story_id, s.channel_id FROM jonah_stories'
+ . ' as s, jonah_stories_tags as t';
+ for ($i = 0; $i < count($ids); $i++) {
+ $sql .= ', jonah_stories_tags as t' . $i;
+ }
+ $sql .= ' WHERE s.story_id = t.story_id';
+ for ($i = 0 ; $i < count($ids); $i++) {
+ $sql .= ' AND t' . $i . '.tag_id = ' . $ids[$i] . ' AND t'
+ . $i . '.story_id = t.story_id';
+ }
+
+ /* Limit to particular channels if requested */
+ if (count($channel_id) > 0) {
+ // Have to find out if we are a composite channel or not.
+ $channels = array();
+ foreach ($channel_id as $cid) {
+ $c = $this->_getChannel($cid);
+ if ($c['channel_type'] == JONAH_COMPOSITE_CHANNEL) {
+ $temp = explode(':', $c['channel_url']);
+ // Save a map of channels that are from composites.
+ foreach ($temp as $t) {
+ $cchannels[$t] = $cid;
+ }
+ $channels = array_merge($channels, $temp);
+ }
+ }
+ $channels = array_merge($channel_id, $channels);
+ $timestamp = time();
+ $sql .= ' AND t.channel_id IN (' . implode(', ', $channels)
+ . ') AND s.story_published IS NOT NULL AND '
+ . 's.story_published < ' . $timestamp;
+ }
+
+ switch ($order) {
+ case JONAH_ORDER_PUBLISHED:
+ $sql .= ' ORDER BY story_published DESC';
+ break;
+ case JONAH_ORDER_READ:
+ $sql .= ' ORDER BY story_read DESC';
+ break;
+ case JONAH_ORDER_COMMENTS:
+ //@TODO
+ break;
+ }
+
+ /* Instantiate the channel object outside the loop if we
+ * are only limiting to one channel. */
+ if (count($channel_id) == 1) {
+ $channel = $this->getChannel($channel_id[0]);
+ }
+ Horde::logMessage('SQL query by Jonah_News_sql::searchTags: ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $results = $this->_db->limitQuery($sql, $from, $max);
+ if (is_a($results, 'PEAR_Error')) {
+ return $results;
+ }
+
+ for ($i = 0; $i < $results->numRows(); $i++) {
+ $row = $results->fetchRow();
+ $story = $this->_getStory($row[0], false);
+ if (count($channel_id > 1)) {
+ // Make sure we get the correct channel info for composites
+ if (!empty($cchannels[$story['channel_id']])) {
+ $channel = $this->getChannel($cchannels[$story['channel_id']]);
+ } else {
+ $channel = $this->getChannel($story['channel_id']);
+ }
+ }
+
+ /* Format story link. */
+ $story['story_link'] = $this->getStoryLink($channel, $story);
+ $story = array_merge($story, $channel);
+ /* Format dates. */
+ $date_format = $GLOBALS['prefs']->getValue('date_format');
+ $story['story_updated_date'] = strftime($date_format, $story['story_updated']);
+ if (!empty($story['story_published'])) {
+ $story['story_published_date'] = strftime($date_format, $story['story_published']);
+ }
+
+ $stories[] = $story;
+ }
+ }
+
+ return $stories;
+ }
+
+ /**
+ * Search for articles matching specific tag name(s).
+ *
+ * @see Jonah_News_sql::searchTagsById()
+ */
+ function searchTags($names, $max = 10, $from = 0, $channel_id = array(),
+ $order = JONAH_ORDER_PUBLISHED)
+ {
+ $ids = $this->getTagIds($names);
+ if (is_a($ids, 'PEAR_Error')) {
+ return $ids;
+ }
+ return $this->searchTagsById(array_values($ids), $max, $from, $channel_id, $order);
+ }
+
+
+ /**
+ * Return a set of tag names given the tag_ids.
+ *
+ * @param array $ids An array of tag_ids to get names for.
+ *
+ * @return mixed An array of tag names | PEAR_Error.
+ */
+ function getTagNames($ids)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+ $sql = 'SELECT t.tag_name FROM jonah_tags as t WHERE t.tag_id IN(';
+ $needComma = false;
+ foreach ($ids as $id) {
+ $sql .= ($needComma ? ',' : '') . '\'' . $id . '\'';
+ $needComma = true;
+ }
+ $sql .= ')';
+ $tags = $this->_db->getCol($sql);
+ return $tags;
+ }
+
+ /**
+ * Return a set of tag_ids, given the tag name
+ *
+ * @param array $names An array of names to search for
+ *
+ * @return mixed An array of tag_name => tag_ids | PEAR_Error
+ */
+ function getTagIds($names)
+ {
+ if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+ return $result;
+ }
+ $sql = 'SELECT t.tag_name, t.tag_id FROM jonah_tags as t WHERE t.tag_name IN(';
+ $needComma = false;
+ foreach ($names as $name) {
+ $sql .= ($needComma ? ',' : '') . '\'' . $name . '\'';
+ $needComma = true;
+ }
+ $sql .= ')';
+ $tags = $this->_db->getAssoc($sql);
+ return $tags;
+ }
+
+ /**
+ * Attempts to open a persistent connection to the SQL server.
+ *
+ * @return boolean True on success; PEAR_Error on failure.
+ */
+ function _connect()
+ {
+ if ($this->_connected) {
+ return true;
+ }
+
+ Horde::assertDriverConfig($this->_params, 'news',
+ array('phptype', 'charset'),
+ 'jonah news SQL');
+
+ if (!isset($this->_params['database'])) {
+ $this->_params['database'] = '';
+ }
+ if (!isset($this->_params['username'])) {
+ $this->_params['username'] = '';
+ }
+ if (!isset($this->_params['hostspec'])) {
+ $this->_params['hostspec'] = '';
+ }
+
+ /* Connect to the SQL server using the supplied parameters. */
+ require_once 'DB.php';
+ $this->_db = &DB::connect($this->_params,
+ array('persistent' => !empty($this->_params['persistent'])));
+ if (is_a($this->_db, 'PEAR_Error')) {
+ return $this->_db;
+ }
+
+ // Set DB portability options.
+ switch ($this->_db->phptype) {
+ case 'mssql':
+ $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM);
+ break;
+ default:
+ $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS);
+ }
+
+ $this->_connected = true;
+ return true;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Jonah base inclusion file.
+ *
+ * $Horde: jonah/lib/base.php,v 1.63 2009/10/20 09:17:26 jan Exp $
+ *
+ * This file brings in all of the dependencies that every Jonah script
+ * will need, and sets up objects that all scripts use.
+ */
+
+// Check for a prior definition of HORDE_BASE (perhaps by an
+// auto_prepend_file definition for site customization).
+if (!defined('HORDE_BASE')) {
+ define('HORDE_BASE', dirname(__FILE__) . '/../..');
+}
+
+// Load the Horde Framework core, and set up inclusion paths.
+require_once HORDE_BASE . '/lib/core.php';
+
+// Registry.
+if (Horde_Util::nonInputVar('session_control') == 'none') {
+ $registry = Horde_Registry::singleton(Horde_Registry::SESSION_NONE);
+} elseif (Horde_Util::nonInputVar('session_control') == 'readonly') {
+ $registry = Horde_Registry::singleton(Horde_Registry::SESSION_READONLY);
+} else {
+ $registry = Horde_Registry::singleton();
+}
+
+try {
+ $registry->pushApp('jonah', !defined('AUTH_HANDLER'));
+} catch (Horde_Exception $e) {
+ if ($e->getCode() == 'permission_denied') {
+ Horde::authenticationFailureRedirect();
+ }
+ Horde::fatal($e, __FILE__, __LINE__, false);
+}
+$conf = &$GLOBALS['conf'];
+define('JONAH_TEMPLATES', $registry->get('templates'));
+
+/* Notification system. */
+$notification = &Horde_Notification::singleton();
+$notification->attach('status');
+
+/* Find the base file path of Jonah. */
+if (!defined('JONAH_BASE')) {
+ define('JONAH_BASE', dirname(__FILE__) . '/..');
+}
+
+/* Jonah base library. */
+require_once JONAH_BASE . '/lib/Jonah.php';
+
+/* Instantiate Jonah storage */
+require_once JONAH_BASE . '/lib/Driver.php';
+$GLOBALS['jonah_driver'] = Jonah_Driver::factory();
+
+// Start compression.
+if (!Horde_Util::nonInputVar('no_compress')) {
+ Horde::compressOutput();
+}
--- /dev/null
+<?php define('JONAH_VERSION', 'H4 (1.0-cvs)') ?>
--- /dev/null
+Deny from all
--- /dev/null
+<?xml version='1.0'?>
+<!-- $Horde: jonah/locale/en_US/help.xml,v 1.3 2006/01/07 19:41:26 chuck Exp $ -->
+<help>
+
+<entry id="Overview">
+ <title>Overview</title>
+ <heading>Introduction</heading>
+ <para>
+ This is an application for displaying news and other data from various sources. You can subscribe to the information you are interested in, so you only see what you want to.
+ </para>
+</entry>
+
+<entry id="feed-type">
+ <title>Feed Types</title>
+ <heading>Internal Feeds</heading>
+ <para>
+ Feeds that are managed and provided by this system.
+ </para>
+
+ <heading>External Feeds</heading>
+ <para>
+ Feeds that are managed and provided by external systems, e.g. other
+ websites.
+ </para>
+
+ <heading>Aggregated Feeds</heading>
+ <para>
+ Feeds that get their content from multiple external feeds. Internal
+ feeds can also be integrated through the RSS delivery.
+ </para>
+
+ <heading>Composite Feeds</heading>
+ <para>
+ Feeds that get their content from multiple internal feeds.
+ </para>
+</entry>
+</help>
--- /dev/null
+<?xml version='1.0'?>
+<!-- $Horde: jonah/locale/es_ES/help.xml,v 1.1 2008/03/21 00:28:28 jan Exp $ -->
+<help>
+
+<entry id="Overview">
+ <title>Introducción</title>
+ <heading>Introducción</heading>
+ <para>Ésta es una aplicación para mostrar noticias y otros datos desde varios orígenes. Puede suscribirse a la información en la que esté interesado de forma que sólo vea lo que quiera ver.</para>
+</entry>
+
+<entry id="feed-type">
+ <title>Tipos de suscripciones</title>
+ <heading>Suscripciones internas</heading>
+ <para>Suscripciones gestionadas y proporcionadas por este sistema.</para>
+
+ <heading>Suscripciones externas</heading>
+ <para>Suscripciones gestionadas y proporcionadas por sistemas externos, por ejemplo, otras sedes web.</para>
+
+ <heading>Suscripciones asociadas</heading>
+ <para>Suscripciones que obtienen su contenido de varias suscripciones externas. Las suscripciones internas también se pueden integrar con la distribución RSS.</para>
+
+ <heading>Suscripciones compuestas</heading>
+ <para>Suscripciones que obtienen su contenido de varias suscripciones internas.</para>
+</entry>
+</help>
--- /dev/null
+<?xml version="1.0"?>
+<!-- $Horde: jonah/locale/fi_FI/help.xml,v 1.2 2005/03/16 15:30:25 jan Exp $ -->
+<help>
+ <entry id="Overview" md5="54ee98f8dbd02ccb30a2591bf2547def" state="uptodate">
+ <title>Yleiskuva</title>
+ <heading>Esittely</heading>
+ <para>
+ Tämän ohjelman avulla voidaan esittää uutisia ja muita tietoja monesta eri lähteestä. Sinun on mahdollista tilata vain ne kanavat, joissa on sinua kiinnostavia tietoja ja siten saat vain sinua kiinnostavia tietoja.
+ </para>
+</entry>
+ <entry id="channel-type" md5="7ce7889043201876ac3b9014798b23cd" state="uptodate">
+ <title>Kanavatyypit</title>
+ <heading>Sisäiset kanavat</heading>
+ <para>
+ Kanavat, joita hoidetaan sisäisesti tämän ohjelmiston kautta.
+ </para>
+
+ <heading>Ulkoiset kanavat</heading>
+ <para>
+ Kanavat, joita hoitaa ja joita tarjoaa jokin ulkopuolinen taho kuten esimerkiksi jokin toinen www-sivusto.
+ </para>
+
+ <heading>Yhdistetyt kanavat</heading>
+ <para>
+ Kanavat, joiden sisältö on yhdistelmä monesta ulkopuolisesta kanavasta. Sisäiset kanavat voidaan myös liittää tähän kanvaan käyttäen RSS-jakelua.
+ </para>
+
+ <heading>Kootut kanavat</heading>
+ <para>
+ Kanavat, joiden sisältö koostuu monesta sisäisestä kanavasta.
+ </para>
+</entry>
+</help>
--- /dev/null
+Deny from all
--- /dev/null
+see horde/po/README
\ No newline at end of file
--- /dev/null
+# German translations for Jonah.
+# Copyright 2002-2009 The Horde Project
+# This file is distributed under the same license as the Jonah package.
+# Jan Schneider, 2002-2008.
+#
+# story: Beitrag
+msgid ""
+msgstr ""
+"Project-Id-Version: Jonah 1.0-cvs\n"
+"Report-Msgid-Bugs-To: dev@lists.horde.org\n"
+"POT-Creation-Date: 2008-09-18 11:24+0200\n"
+"PO-Revision-Date: 2008-09-18 12:06+0200\n"
+"Last-Translator: Jan Schneider\n"
+"Language-Team: i18n@lists.horde.org\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: lib/Block/news_popular.php:75 lib/Block/news_popular.php:78
+msgid " - Most read stories"
+msgstr " - Am häufigsten gelesene Beiträge"
+
+#: lib/Block/delivery.php:48
+#, php-format
+msgid "\"%s\" stories in HTML"
+msgstr "\"%s\"-Beiträge als HTML"
+
+#: lib/News.php:439 lib/Driver.php:338
+msgid "1 hour"
+msgstr "1 Stunde"
+
+#: lib/News.php:443 lib/Driver.php:342
+msgid "12 hours"
+msgstr "12 Stunden"
+
+#: lib/News.php:440 lib/Driver.php:339
+msgid "2 hours"
+msgstr "2 Stunden"
+
+#: lib/News.php:444 lib/Driver.php:343
+msgid "24 hours"
+msgstr "24 Stunden"
+
+#: lib/News.php:438 lib/Driver.php:337
+msgid "30 mins"
+msgstr "30 Minuten"
+
+#: lib/News.php:441 lib/Driver.php:340
+msgid "4 hours"
+msgstr "4 Stunden"
+
+#: lib/News.php:442 lib/Driver.php:341
+msgid "8 hours"
+msgstr "8 Stunden"
+
+#: stories/share.php:86
+msgid "A link to the story"
+msgstr "Einen Link zum Beitrag"
+
+#: channels/aggregate.php:59
+msgid "Add"
+msgstr "Hinzufügen"
+
+#: content.php:37
+msgid "Add Content"
+msgstr "Inhalt hinzufügen"
+
+#: lib/Forms/Story.php:37
+msgid "Add New Story"
+msgstr "Neuen Beitrag hinzufügen"
+
+#: channels/index.php:65
+msgid "Add story"
+msgstr "Beitrag hinzufügen"
+
+#: lib/api.php:90
+msgid "Administrator"
+msgstr "Administrator"
+
+#: lib/News.php:477 lib/Jonah.php:113
+msgid "Aggregated Feed"
+msgstr "Gesammelter Feed"
+
+#: channels/aggregate.php:58
+#, php-format
+msgid "Aggregated channels for channel \"%s\""
+msgstr "Gesammelte Kanäle für Kanal \"%s\""
+
+#: channels/delete.php:56
+msgid "All stories created in this channel will be lost!"
+msgstr "Alle Beiträge, die für diesen Kanal erstellt wurden, gehen verloren!"
+
+#: stories/results.php:114
+#, php-format
+msgid "All stories tagged with %s"
+msgstr "Alle mit %s getaggte Beiträge"
+
+#: channels/index.php:35
+#, php-format
+msgid "An error occurred fetching channels: %s"
+msgstr "Beim Empfangen dieses Kanals ist ein Fehler aufgetreten: %s"
+
+#: channels/delete.php:58
+msgid "Any cached stories for this channel will be lost!"
+msgstr "Alle zwischengespeicherten Beiträge für diesen Kanal gehen verloren!"
+
+#: lib/Forms/Feed.php:89 lib/Forms/Feed.php:100
+msgid "Caching"
+msgstr "Caching"
+
+#: stories/pdf.php:44
+msgid "Cannot generate PDFs of remote stories."
+msgstr "Von externen Beiträgen können keine PDF-Dateien erzeugt werden."
+
+#: lib/Driver/sql.php:651 lib/News/sql.php:511
+#, php-format
+msgid "Channel \"%s\" not found."
+msgstr "Kanal \"%s\" nicht gefunden."
+
+#: channels/aggregate.php:61
+msgid "Channel Name"
+msgstr "Kanalname"
+
+#: lib/Forms/Feed.php:77
+msgid "Channel Slug"
+msgstr "Kanalkurzname"
+
+#: lib/Forms/Feed.php:83
+msgid ""
+"Channel URL for further pages, if not the default one. %c gets replaced by "
+"the feed ID, %n by the story offset."
+msgstr ""
+"Kanal-URL für weiterführende Seiten, falls nicht die Standard-URL. %c wird "
+"durch die Feed-ID und %s durch die Beitragsnummer ersetzt."
+
+#: lib/Forms/Feed.php:118
+msgid ""
+"Channel URL if not the default one. %c gets replaced by the feed ID, %n by "
+"the story offset."
+msgstr ""
+"Kanal-URL, falls nicht die Standard-URL. %c wird durch die Feed-ID und %s "
+"durch die Beitragsnummer ersetzt."
+
+#: lib/Forms/Feed.php:82
+#, php-format
+msgid "Channel URL if not the default one. %c gets replaced by the feed ID."
+msgstr ""
+"Kanal-URL, falls nicht die Standard-URL. %c wird durch die Feed-ID ersetzt."
+
+#: channels/delete.php:76
+msgid "Channel has not been deleted."
+msgstr "Der Kanal wurde nicht entfernt."
+
+#: lib/Driver/sql.php:175 lib/News/sql.php:175
+#, php-format
+msgid "Channel id \"%s\" not found."
+msgstr "Kanal-ID \"%s\" nicht gefunden."
+
+#: stories/index.php:54
+msgid "Channel refreshed."
+msgstr "Kanal aktualisiert."
+
+#: templates/channels/index.html:13
+msgid "Close Search"
+msgstr "Suche beenden"
+
+#: templates/stories/index.html:27
+msgid "Comments"
+msgstr "Kommentare"
+
+#: config/templates.php.dist:42
+msgid "Compact"
+msgstr "Kompakt"
+
+#: lib/News.php:480 lib/Jonah.php:116
+msgid "Composite Feed"
+msgstr "Zusammengefasster Feed"
+
+#: lib/Forms/Feed.php:120
+msgid "Composite feeds"
+msgstr "Zusammengefasste Feeds"
+
+#: lib/Jonah.php:62
+#, php-format
+msgid "Could not open %s."
+msgstr "%s konnte nicht gelesen werden."
+
+#: lib/Block/latest.php:49
+msgid "Count reads of the latest story when this block is displayed"
+msgstr ""
+"Den neuesten Beitrag als gelesen markieren, wenn der Block angezeigt wird?"
+
+#: lib/Block/story.php:49
+msgid "Count reads of this story when this block is displayed"
+msgstr "Diesen Beitrag als gelesen markieren, wenn der Block angezeigt wird?"
+
+#: stories/index.php:124 stories/results.php:116
+msgid "Date"
+msgstr "Datum"
+
+#: stories/delete.php:63 stories/delete.php:68 channels/delete.php:51
+#: channels/delete.php:62
+msgid "Delete"
+msgstr "Löschen"
+
+#: channels/delete.php:48
+#, php-format
+msgid "Delete News Channel \"%s\"?"
+msgstr "Nachrichtenkanal \"%s\" löschen?"
+
+#: stories/delete.php:59
+#, php-format
+msgid "Delete News Story \"%s\"?"
+msgstr "Nachrichtenbeitrag \"%s\" löschen?"
+
+#: channels/index.php:49
+msgid "Delete channel"
+msgstr "Kanal löschen"
+
+#: stories/index.php:100 stories/results.php:94
+msgid "Delete story"
+msgstr "Beitrag löschen"
+
+#: lib/Forms/Feed.php:75 lib/Forms/Feed.php:99 lib/Forms/Feed.php:117
+msgid "Description"
+msgstr "Beschreibung"
+
+#: stories/delete.php:63 channels/delete.php:51
+msgid "Do not delete"
+msgstr "Nicht löschen"
+
+#: lib/Forms/Feed.php:40
+msgid "Edit Feed"
+msgstr "Feed bearbeiten"
+
+#: lib/Forms/Story.php:37
+msgid "Edit Story"
+msgstr "Beitrag bearbeiten"
+
+#: lib/Forms/Feed.php:106
+msgid "Edit aggregated feeds"
+msgstr "Gesammelte Feeds bearbeiten"
+
+#: channels/index.php:44
+msgid "Edit channel"
+msgstr "Kanal bearbeiten"
+
+#: channels/aggregate.php:25
+#, php-format
+msgid "Edit channel \"%s\""
+msgstr "Kanal \"%s\" bearbeiten"
+
+#: stories/index.php:94 stories/results.php:87
+msgid "Edit story"
+msgstr "Beitrag bearbeiten"
+
+#: lib/Forms/Story.php:79
+msgid "Enter keywords to tag this story, separated by commas"
+msgstr "Kommagetrennte Stichwörter für diesen Beitrag"
+
+#: lib/News.php:519 lib/Driver.php:370
+#, php-format
+msgid "Error fetching feed: %s"
+msgstr "Fehler beim Lesen des Feeds: %s"
+
+#: stories/share.php:66 stories/pdf.php:15 stories/view.php:25
+#: stories/view.php:35 lib/Block/latest.php:91 lib/Block/story.php:91
+#, php-format
+msgid "Error fetching story: %s"
+msgstr "Fehler beim Herunterladen des Beitrags: %s"
+
+#: lib/News.php:758
+#, php-format
+msgid "Error parsing external feed from %s: %s"
+msgstr "Fehler bei der Verarbeitung des externen Feeds von %s: %s"
+
+#: lib/api.php:96
+msgid "External Channels"
+msgstr "Externe Kanäle"
+
+#: lib/News.php:471 lib/Jonah.php:110
+msgid "External Feed"
+msgstr "Externer Feed"
+
+#: lib/Forms/Feed.php:51
+msgid "Extra information for this feed type"
+msgstr "Zusätzliche Informationen für diesen Feedtyp"
+
+#: lib/Block/news_popular.php:32 lib/Block/news.php:3 lib/Block/news.php:30
+#: lib/Block/story.php:42
+msgid "Feed"
+msgstr "Feed"
+
+#: lib/News.php:105
+#, php-format
+msgid "Feed \"%s\" is not authored on this system."
+msgstr "Der Feed \"%s\" wird nicht auf diesem System erzeugt."
+
+#: channels/edit.php:56
+msgid "Feed type changed."
+msgstr "Feedtyp geändert."
+
+#: channels/index.php:93 lib/Block/delivery.php:3 lib/Block/delivery.php:25
+msgid "Feeds"
+msgstr "Feeds"
+
+#: lib/Block/news.php:54
+msgid "First Story"
+msgstr "Erster Beitrag"
+
+#: stories/share.php:78
+msgid "From"
+msgstr "Von"
+
+#: lib/Forms/Story.php:74 lib/Forms/Story.php:76
+msgid "Full Story Text"
+msgstr "Vollständiger Beitragstext"
+
+#: delivery/html.php:49
+#, php-format
+msgid "HTML Delivery for \"%s\""
+msgstr "HTML-Auslieferung für \"%s\""
+
+#: lib/News.php:658 lib/Driver.php:509
+msgid "HTML Version of Story"
+msgstr "HTML-Version des Beitrags"
+
+#: lib/Forms/Story.php:82
+msgid ""
+"If you enter a URL without a full story text, clicking on the story will "
+"send the reader straight to the URL, otherwise it will be shown at the end "
+"of the full story."
+msgstr ""
+"Wenn Sie eine URL aber keinen Beitragstext angeben, wird der Leser beim "
+"Klick auf den Beitrag direkt an diese URL weitergeleitet, anderenfalls wird "
+"die URL am Ende des Beitrags angezeigt."
+
+#: channels/aggregate.php:64 lib/Forms/Feed.php:93
+msgid "Image"
+msgstr "Bild"
+
+#: stories/share.php:86
+msgid "Include"
+msgstr "Einfügen"
+
+#: config/templates.php.dist:31
+msgid "Internal"
+msgstr "Intern"
+
+#: lib/api.php:94
+msgid "Internal Channels"
+msgstr "Interne Kanäle"
+
+#: stories/index.php:43 stories/results.php:49
+#, php-format
+msgid "Invalid channel requested. %s"
+msgstr "Ungültiger Kanal angefordert. %s"
+
+#: channels/delete.php:30
+msgid "Invalid channel specified for deletion."
+msgstr "Kein gültiger Kanal zum Löschen ausgewählt."
+
+#: delivery/html.php:43
+msgid "Invalid channel."
+msgstr "Ungültiger Kanal."
+
+#: channels/index.php:87
+msgid "Last Update"
+msgstr "Letzte Aktualisierung"
+
+#: lib/Block/latest.php:3 lib/Block/latest.php:65
+msgid "Latest News"
+msgstr "Neueste Beiträge"
+
+#: channels/aggregate.php:63 lib/Forms/Feed.php:92
+msgid "Link"
+msgstr "Link"
+
+#: lib/News.php:474 lib/Jonah.php:107
+msgid "Local Feed"
+msgstr "Lokaler Feed"
+
+#: channels/index.php:84
+msgid "Manage Feeds"
+msgstr "Feeds verwalten"
+
+#: lib/Block/news_popular.php:52 lib/Block/news.php:49
+msgid "Maximum Stories"
+msgstr "Maximale Beiträge"
+
+#: config/templates.php.dist:20
+msgid "Media"
+msgstr "Multimedia"
+
+#: lib/Block/tree_menu.php:3
+msgid "Menu List"
+msgstr "Menüliste"
+
+#: stories/share.php:87
+msgid "Message"
+msgstr "Nachricht"
+
+#: lib/News.php:66 lib/Driver.php:67
+msgid "Missing channel id."
+msgstr "Kanal-ID fehlt."
+
+#: lib/Block/news_popular.php:3
+msgid "Most Popular Stories"
+msgstr "Populärste Beiträge"
+
+#: content.php:34 templates/content/header.inc:2
+msgid "My News"
+msgstr "Meine Nachrichten"
+
+#: content_edit.php:33
+msgid "My News :: Add Content"
+msgstr "Meine Nachrichten :: Inhalt hinzufügen"
+
+#: channels/index.php:85 lib/Forms/Feed.php:50
+msgid "Name"
+msgstr "Name"
+
+#: lib/Jonah.php:249 lib/Forms/Feed.php:40
+msgid "New Feed"
+msgstr "Neuer Feed"
+
+#: lib/api.php:92
+msgid "News"
+msgstr "Nachrichten"
+
+#: lib/Block/latest.php:33
+msgid "News Source"
+msgstr "Nachrichtenquelle"
+
+#: channels/index.php:25
+msgid "News is not enabled."
+msgstr "Nachrichten sind nicht aktiviert."
+
+#: stories/index.php:51 stories/results.php:57
+msgid "No available stories."
+msgstr "Keine Beiträge verfügbar."
+
+#: stories/index.php:23
+msgid "No channel requested."
+msgstr "Kein Kanal angefragt."
+
+#: lib/Block/latest.php:86
+msgid "No channel specified."
+msgstr "Kein Kanal angegeben."
+
+#: templates/channels/index.html:55
+msgid "No channels are available."
+msgstr "Keine Kanäle sind verfügbar."
+
+#: lib/Block/news_popular.php:91 lib/Block/news.php:92
+msgid "No feed specified."
+msgstr "Kein Feed angegeben."
+
+#: lib/Block/delivery.php:68
+msgid "No feeds are available."
+msgstr "Keine Feeds verfügbar."
+
+#: templates/channels/index.html:51
+msgid "No feeds match"
+msgstr "Keine Treffer"
+
+#: lib/News.php:549 lib/Driver.php:400
+msgid "No stories are currently available."
+msgstr "Zur Zeit sind keine Beiträge verfügbar."
+
+#: lib/Block/story.php:86
+msgid "No story is selected."
+msgstr "Kein Beitrag ausgewählt."
+
+#: lib/News.php:708 lib/Driver.php:559
+#, php-format
+msgid "No such backend \"%s\" found"
+msgstr "Ein Backend namens \"%s\" konnte nicht gefunden werden"
+
+#: stories/results.php:40
+msgid "No tag requested."
+msgstr "Kein Tag angefragt."
+
+#: feed.php:37
+msgid "No valid feed name or ID requested."
+msgstr "Ungültiger Feedname oder Feed-ID angefordert."
+
+#: stories/delete.php:48
+msgid "No valid story requested for deletion."
+msgstr "Kein gültiger Beitrag zum Löschen ausgewählt.<"
+
+#: stories/share.php:25
+msgid "Note"
+msgstr "Notiz"
+
+#: lib/Forms/Story.php:58
+msgid ""
+"Note: this story won't be delivered to distribution lists automatically if "
+"this time is in the future."
+msgstr ""
+"Hinweis: Die Nachricht wird nicht automatisch an Verteilerlisten "
+"ausgeliefert, wenn dieser Zeitpunkt in der Zukunft liegt."
+
+#: lib/Forms/Story.php:55
+msgid "Or publish on this date:"
+msgstr "Oder zu diesem Datum veröffentlichen:"
+
+#: stories/index.php:89 stories/results.php:81
+msgid "PDF version"
+msgstr "PDF-Version"
+
+#: scripts/feed_tester.php:57
+msgid "Parse failed:"
+msgstr "Parsen fehlgeschlagen:"
+
+#: scripts/feed_tester.php:60
+msgid "Parse succeeded, structure is:"
+msgstr "Parsen erfolgreich, Struktur ist:"
+
+#: lib/News.php:653 lib/Driver.php:504
+msgid "Plaintext Version of Story"
+msgstr "Textversion des Beitrags"
+
+#: lib/Forms/Story.php:45
+msgid "Publish Now?"
+msgstr "Jetzt veröffentlichen?"
+
+#: lib/Block/delivery.php:59
+#, php-format
+msgid "RSS Feed of \"%s\""
+msgstr "RSS-Feed für \"%s\""
+
+#: templates/stories/index.html:22
+msgid "Read"
+msgstr "Gelesen"
+
+#: channels/delete.php:54
+msgid "Really delete this News Channel?"
+msgstr "Diesen Nachrichtenkanal wirlich löschen?"
+
+#: stories/delete.php:66
+msgid "Really delete this News Story?"
+msgstr "Diesen Nachrichtenbeitrag wirklich löschen?"
+
+#: stories/index.php:123
+msgid "Refresh Channel"
+msgstr "Kanal aktualisieren"
+
+#: channels/index.php:73
+msgid "Refresh channel"
+msgstr "Kanal aktualisieren"
+
+#: channels/aggregate.php:26
+#, php-format
+msgid "Remove channel \"%s\""
+msgstr "Kanal \"%s\" entfernen"
+
+#: lib/Block/cloud.php:28
+msgid "Results URL"
+msgstr "Ergebnis-URL"
+
+#: lib/Jonah.php:197
+msgid "Rich Text"
+msgstr "Formatierter Text"
+
+#: lib/Forms/Story.php:39
+msgid "Save"
+msgstr "Speichern"
+
+#: channels/index.php:90 templates/channels/index.html:9
+msgid "Search"
+msgstr "Suche"
+
+#: templates/delivery/html.html:13
+msgid "Select a format:"
+msgstr "Wählen Sie ein Format:"
+
+#: stories/share.php:75
+msgid "Send"
+msgstr "Senden"
+
+#: stories/share.php:84
+msgid "Separate multiple email addresses with commas."
+msgstr "Trennen Sie mehrere Emailadressen durch Kommas."
+
+#: stories/share.php:73
+msgid "Share Story"
+msgstr "Beitrag weitergeben"
+
+#: stories/view.php:87
+msgid "Share this story"
+msgstr "Diesen Beitrag weitergeben"
+
+#: lib/Forms/Story.php:44
+msgid "Short Description"
+msgstr "Kurzbeschreibung"
+
+#: lib/Forms/Feed.php:78
+#, php-format
+msgid ""
+"Slugs allows direct access to this channel's content by visiting: %s. <br /> "
+"Slug names may contain only letters, numbers or the _ (underscore) character."
+msgstr ""
+"Kurznamen ermöglichen den direkten Zugriff auf diesen Feed über: %s.<br /"
+">Kurznamen dürfen Buchstaben, Zahlen und Unterstriche enthalten."
+
+#: channels/aggregate.php:62 lib/Forms/Feed.php:91
+msgid "Source URL"
+msgstr "Quellen-URL"
+
+#: lib/Forms/Feed.php:106
+msgid "Source URLs"
+msgstr "Quellen-URLs"
+
+#: config/templates.php.dist:9
+msgid "Standard"
+msgstr "Standard"
+
+#: delivery/rss.php:72 stories/results.php:112
+#, php-format
+msgid "Stories tagged with %s in %s"
+msgstr "Mit %s getaggte Beiträge in %s"
+
+#: stories/index.php:124 stories/results.php:116 lib/Block/story.php:3
+#: lib/Block/story.php:46 lib/Block/story.php:65
+msgid "Story"
+msgstr "Beitrag"
+
+#: lib/News.php:729
+#, php-format
+msgid "Story \"%s\" not found in \"%s\"."
+msgstr "Beitrag \"%s\" nicht in \"%s\" gefunden."
+
+#: stories/share.php:109
+msgid "Story Link"
+msgstr "Beitrags-URL"
+
+#: lib/Forms/Story.php:43
+msgid "Story Title (Headline)"
+msgstr "Beitragstitel (Überschrift)"
+
+#: lib/Forms/Story.php:82
+msgid "Story URL"
+msgstr "Beitrags-URL"
+
+#: lib/Driver/sql.php:597 lib/News/sql.php:457
+#, php-format
+msgid "Story URL \"%s\" not found."
+msgstr "Beitrags-URL \"%s\" nicht gefunden."
+
+#: lib/Forms/Feed.php:84 lib/Forms/Feed.php:119
+#, php-format
+msgid ""
+"Story URL if not the default one. %c gets replaced by the feed ID, %s by the "
+"story ID."
+msgstr ""
+"Beitrags-URL, falls nicht die Standard-URL. %c wird durch die Feed-ID und %s "
+"durch die Beitrags-ID ersetzt."
+
+#: lib/Forms/Story.php:61
+msgid "Story body type"
+msgstr "Art des Beitragstexts"
+
+#: stories/edit.php:33 stories/delete.php:34
+#, php-format
+msgid "Story editing failed: %s"
+msgstr "Bearbeiten des Beitrags fehlgeschlagen: %s"
+
+#: stories/delete.php:83
+msgid "Story has not been deleted."
+msgstr "Der Beitrag wurde nicht gelöscht."
+
+#: lib/Driver/sql.php:565 lib/News/sql.php:425
+#, php-format
+msgid "Story id \"%s\" not found."
+msgstr "Beitrags-ID \"%s\" nicht gefunden."
+
+#: stories/share.php:85
+msgid "Subject"
+msgstr "Betreff"
+
+#: lib/Block/cloud.php:3 lib/Block/cloud.php:35
+msgid "Tag Cloud"
+msgstr "Tagwolke"
+
+#: lib/News.php:816 lib/News.php:821 lib/News.php:826 lib/News.php:832
+#: lib/News.php:837 lib/Driver.php:569 lib/Driver.php:574 lib/Driver.php:579
+#: lib/Driver.php:585 lib/Driver.php:590
+msgid "Tag support not enabled in backend."
+msgstr "Tags sind für dieses Backend nicht aktiviert."
+
+#: lib/Forms/Story.php:79
+msgid "Tags"
+msgstr "Tags"
+
+#: lib/Block/story.php:105 templates/stories/story.html:6
+msgid "Tags: "
+msgstr "Tags: "
+
+#: lib/Jonah.php:204
+msgid "Text"
+msgstr "Text"
+
+#: channels/aggregate.php:100
+#, php-format
+msgid "The channel \"%s\" has been removed."
+msgstr "Der Kanal \"%s\" wurde entfernt."
+
+#: channels/aggregate.php:79
+#, php-format
+msgid "The channel \"%s\" has been saved."
+msgstr "Der Kanal \"%s\" wurde gespeichert."
+
+#: channels/aggregate.php:87 channels/aggregate.php:107
+#, php-format
+msgid "The channel \"%s\" has been updated."
+msgstr "Der Kanal \"%s\" wurde aktualisiert."
+
+#: channels/delete.php:69
+msgid "The channel has been deleted."
+msgstr "Der Kanal wurde gelöscht."
+
+#: stories/share.php:86
+msgid "The complete text of the story"
+msgstr "Den gesamten Nachrichtentext"
+
+#: channels/edit.php:70
+#, php-format
+msgid "The feed \"%s\" has been saved."
+msgstr "Der Feed \"%s\" wurde gespeichert."
+
+#: lib/Forms/Feed.php:100
+msgid ""
+"The interval before stories aggregated into this feeds are rechecked for "
+"updates. If none, then stories will always be refetched from the sources."
+msgstr ""
+"Der Intervall, in dem die Beiträge, die in diesem Feed gesammelt werden, auf "
+"Aktualisierungen überprüft werden sollen. Falls nicht angegeben, werden die "
+"Beiträge bei jedem Zugriff von ihrer Quelle geladen."
+
+#: lib/Forms/Feed.php:89
+msgid ""
+"The interval before stories in this feed are rechecked for updates. If none, "
+"then stories will always be refetched from the source."
+msgstr ""
+"Der Intervall, in dem die Beiträge dieses Feeds auf Aktualisierungen "
+"überprüft werden sollen. Falls nicht angegeben, werden die Beiträge bei "
+"jedem Zugriff von ihrer Quelle geladen."
+
+#: stories/edit.php:61
+#, php-format
+msgid "The story \"%s\" has been saved."
+msgstr "Der Beitrag \"%s\" wurde gespeichert."
+
+#: stories/delete.php:75
+msgid "The story has been deleted."
+msgstr "Der Beitrag wurde gelöscht."
+
+#: stories/share.php:120
+msgid "The story was sent successfully."
+msgstr "Der Beitrag wurde erfolgreich versendet."
+
+#: channels/aggregate.php:62 lib/Forms/Feed.php:91
+msgid ""
+"The url to use to fetch the stories, for example 'http://www.example.com/"
+"stories.rss'"
+msgstr ""
+"Die URL, die benutzt werden soll, um die Beiträge herunterzuladen, zum "
+"Beispiel 'http://www.example.com/stories.rss'."
+
+#: channels/delete.php:67
+#, php-format
+msgid "There was an error deleting the channel: %s"
+msgstr "Beim Löschen des Kanals ist ein Fehler aufgetreten: %s"
+
+#: stories/delete.php:73
+#, php-format
+msgid "There was an error deleting the story: %s"
+msgstr "Beim Löschen des Beitrags ist ein Fehler aufgetreten: %s"
+
+#: channels/aggregate.php:98
+#, php-format
+msgid "There was an error removing the channel: %s"
+msgstr "Beim Entfernen des Kanals ist ein Fehler aufgetreten: %s"
+
+#: channels/aggregate.php:77
+#, php-format
+msgid "There was an error saving the channel: %s"
+msgstr "Beim Speichern des Kanals ist ein Fehler aufgetreten: %s"
+
+#: channels/edit.php:68
+#, php-format
+msgid "There was an error saving the feed: %s"
+msgstr "Beim Speichern des Feeds ist ein Fehler aufgetreten: %s"
+
+#: stories/edit.php:59
+#, php-format
+msgid "There was an error saving the story: %s"
+msgstr "Beim Speichern des Beitrags ist ein Fehler aufgetreten: %s"
+
+#: channels/aggregate.php:85 channels/aggregate.php:105
+#, php-format
+msgid "There was an error updating the channel: %s"
+msgstr "Beim Aktualisieren des Kanals ist ein Fehler aufgetreten: %s"
+
+#: channels/aggregate.php:46
+msgid "This is no aggregated channel."
+msgstr "Dies ist kein gesammelter Kanal."
+
+#: stories/share.php:84
+msgid "To"
+msgstr "An"
+
+#: channels/index.php:86 lib/Forms/Feed.php:45
+msgid "Type"
+msgstr "Typ"
+
+#: config/templates.php.dist:53
+msgid "Ultracompact"
+msgstr "Sehr kompakt"
+
+#: stories/share.php:118
+#, php-format
+msgid "Unable to send story: %s"
+msgstr "Der Beitrag konnte nicht versendet werden: %s"
+
+#: channels/aggregate.php:115
+msgid "Update"
+msgstr "Aktualisierung"
+
+#: lib/Block/news_popular.php:45 lib/Block/news.php:41
+msgid "View"
+msgstr "Anzeigen"
+
+#: stories/edit.php:41 stories/index.php:31 stories/results.php:24
+#: stories/delete.php:42 channels/edit.php:43 channels/index.php:19
+#: channels/delete.php:38 channels/aggregate.php:53
+msgid "You are not authorised for this action."
+msgstr "Sie sind zu dieser Aktion nicht autorisiert."
+
+#: channels/edit.php:72
+msgid "You can now edit the sub-feeds."
+msgstr "Sie können jetzt die Unter-Feeds bearbeiten."
+
+#: lib/News.php:771
+msgid "[No title]"
+msgstr "[Keine Überschrift]"
+
+#: lib/Jonah.php:245
+msgid "_Feeds"
+msgstr "_Feeds"
+
+#: lib/Jonah.php:241
+msgid "_My News"
+msgstr "_Meine Nachrichten"
+
+#: lib/Jonah.php:258
+msgid "_New Story"
+msgstr "_Neuer Beitrag"
+
+#: lib/News.php:437 lib/Driver.php:336
+msgid "none"
+msgstr "keines"
--- /dev/null
+# Spanish translations for jonah package
+# Traducciones al español para el paquete jonah.
+# Copyright 2008-2009 The Horde Project
+# This file is distributed under the same license as the jonah package.
+# Automatically generated, 2008.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Jonah 1.0-cvs\n"
+"Report-Msgid-Bugs-To: dev@lists.horde.org\n"
+"POT-Creation-Date: 2008-03-18 12:03+0100\n"
+"PO-Revision-Date: 2008-03-18 12:03+0100\n"
+"Last-Translator: Manuel P. Ayala <mayala@unex.es>\n"
+"Language-Team: i18n@lists.horde.org\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: lib/Block/news_popular.php:77 lib/Block/news_popular.php:80
+msgid " - Most read stories"
+msgstr " - Historias más leídas"
+
+#: lib/Block/delivery.php:48
+#, php-format
+msgid "\"%s\" stories in HTML"
+msgstr "\"%s\" historias en HTML"
+
+#: lib/Block/delivery_email.php:53
+#, php-format
+msgid "%s by Email"
+msgstr "%s por Email"
+
+#: lists/index.php:121
+#, php-format
+msgid "%s to %s of %s"
+msgstr "%s al %s de %s"
+
+#: lib/News.php:474
+msgid "1 hour"
+msgstr "1 hora"
+
+#: lib/News.php:478
+msgid "12 hours"
+msgstr "12 horas"
+
+#: lib/News.php:475
+msgid "2 hours"
+msgstr "2 horas"
+
+#: lib/News.php:479
+msgid "24 hours"
+msgstr "24 horas"
+
+#: lib/News.php:473
+msgid "30 mins"
+msgstr "30 minutos"
+
+#: lib/News.php:476
+msgid "4 hours"
+msgstr "4 horas"
+
+#: lib/News.php:477
+msgid "8 hours"
+msgstr "8 horas"
+
+#: delivery/email.php:97
+#, php-format
+msgid ""
+"A confirmation message has been sent to %s. Click on the link in that "
+"message to finally subscribe to channel \"%s\"."
+msgstr ""
+"Se ha enviado un mensaje de confirmación a %s. Pulse el vínculo de ese "
+"mensaje para terminar de suscribirse al canal \"%s\"."
+
+#: stories/share.php:86
+msgid "A link to the story"
+msgstr "Vínculo a la historia"
+
+#: channels/aggregate.php:56
+msgid "Add"
+msgstr "Añadir"
+
+#: content.php:34
+msgid "Add Content"
+msgstr "Añadir contenido"
+
+#: lib/Forms/Story.php:37
+msgid "Add New Story"
+msgstr "Añadir historia"
+
+#: channels/index.php:71
+msgid "Add story"
+msgstr "Añadir historia"
+
+#: lib/api.php:118
+msgid "Administrator"
+msgstr "Administrador"
+
+#: lib/News.php:512 lib/Jonah.php:138
+msgid "Aggregated Feed"
+msgstr "Suscripción asociada"
+
+#: channels/aggregate.php:55
+#, php-format
+msgid "Aggregated channels for channel \"%s\""
+msgstr "Canales asociados del canal \"%s\""
+
+#: channels/delete.php:55
+msgid "All stories created in this channel will be lost!"
+msgstr "¡Se perderán todas las historias del canal!"
+
+#: stories/results.php:114
+#, php-format
+msgid "All stories tagged with %s"
+msgstr "Todas las historias etiquetadas como %s"
+
+#: channels/index.php:35
+#, php-format
+msgid "An error occurred fetching channels: %s"
+msgstr "Se ha producido un error al traer los canales: %s"
+
+#: channels/delete.php:57
+msgid "Any cached stories for this channel will be lost!"
+msgstr ""
+"¡Se perderán todas las historias de este canal almacenadas temporalmente!"
+
+#: lib/Forms/Feed.php:83 lib/Forms/Feed.php:94
+msgid "Caching"
+msgstr "Almacenando"
+
+#: stories/pdf.php:44
+msgid "Cannot generate PDFs of remote stories."
+msgstr "No se pueden generar PDFs de historias remotas."
+
+#: lib/Block/delivery_email.php:87
+msgid "Channel"
+msgstr "Canal"
+
+#: lib/News/sql.php:510
+#, php-format
+msgid "Channel \"%s\" not found."
+msgstr "No se ha encontrado el canal \"%s\"."
+
+#: lists/index.php:130
+msgid "Channel Lists Admin"
+msgstr "Administrador de listas de canales"
+
+#: channels/aggregate.php:58
+msgid "Channel Name"
+msgstr "Nombre del canal"
+
+#: lib/Forms/Feed.php:77
+msgid ""
+"Channel URL for further pages, if not the default one. %c gets replaced by "
+"the feed ID, %n by the story offset."
+msgstr ""
+"URL de las páginas siguientes del canal, si no está por omisión. %c se "
+"sustituye por el ID de la suscripción, %n por el desplazamiento de la "
+"historia."
+
+#: lib/Forms/Feed.php:112
+msgid ""
+"Channel URL if not the default one. %c gets replaced by the feed ID, %n by "
+"the story offset."
+msgstr ""
+"URL del canal si no está por omisión. %c se sustituye por el ID de la "
+"suscripción, %n por el desplazamiento de la historia."
+
+#: lib/Forms/Feed.php:76
+#, php-format
+msgid "Channel URL if not the default one. %c gets replaced by the feed ID."
+msgstr ""
+"URL del canal si no está por omisión. %c se sustituye por el ID de la "
+"suscripción."
+
+#: channels/delete.php:75
+msgid "Channel has not been deleted."
+msgstr "No se ha eliminado el canal."
+
+#: lib/News/sql.php:174
+#, php-format
+msgid "Channel id \"%s\" not found."
+msgstr "No se ha encontrado el canal con id \"%s\"."
+
+#: stories/index.php:54
+msgid "Channel refreshed."
+msgstr "Se ha actualizado el canal."
+
+#: templates/stories/index.html:37
+msgid "Comments"
+msgstr "Comentarios"
+
+#: config/.bak/templates.php.dist:42
+msgid "Compact"
+msgstr "Compactar"
+
+#: lib/News.php:515 lib/Jonah.php:141
+msgid "Composite Feed"
+msgstr "Suscripción compuesta"
+
+#: lib/Forms/Feed.php:114
+msgid "Composite feeds"
+msgstr "Suscripciones compuestas"
+
+#: lib/News.php:358
+#, php-format
+msgid "Could not deliver to the %s distribution list. %s"
+msgstr "No se puede enviar a la lista de distribución %s. %s"
+
+#: lib/Jonah.php:87
+#, php-format
+msgid "Could not open %s."
+msgstr "No se puede abrir %s."
+
+#: lib/Block/latest.php:49
+msgid "Count reads of the latest story when this block is displayed"
+msgstr ""
+"Cuando este bloque se muestra se cuentan las lecturas de la última historia"
+
+#: lib/Block/story.php:49
+msgid "Count reads of this story when this block is displayed"
+msgstr "Cuando este bloque se muestra se cuentan las lecturas de esta historia"
+
+#: lists/index.php:110
+msgid "Current Recipients"
+msgstr "Destinatarios actuales"
+
+#: stories/index.php:136 stories/results.php:116
+msgid "Date"
+msgstr "Fecha"
+
+#: stories/delete.php:51 stories/delete.php:56 channels/delete.php:50
+#: channels/delete.php:61
+msgid "Delete"
+msgstr "Eliminar"
+
+#: channels/delete.php:47
+#, php-format
+msgid "Delete News Channel \"%s\"?"
+msgstr "¿Eliminar canal de noticias \"%s\"?"
+
+#: stories/delete.php:47
+#, php-format
+msgid "Delete News Story \"%s\"?"
+msgstr "¿Eliminar historia de noticias \"%s\"?"
+
+#: channels/index.php:49
+msgid "Delete channel"
+msgstr "Eliminar canal"
+
+#: lists/index.php:82
+msgid "Delete recipient"
+msgstr "Eliminar destinatario"
+
+#: stories/index.php:97 stories/results.php:94
+msgid "Delete story"
+msgstr "Eliminar historia"
+
+#: lists/edit.php:57 lists/edit.php:62
+msgid "Delivery"
+msgstr "Entregar"
+
+#: channels/index.php:60
+msgid "Delivery lists"
+msgstr "Listas de entrega"
+
+#: lists/edit.php:50
+#, php-format
+msgid "Delivery of \"%s\" stories"
+msgstr "Entrega de \"%s\" historias"
+
+#: lib/Forms/Feed.php:75 lib/Forms/Feed.php:93 lib/Forms/Feed.php:111
+msgid "Description"
+msgstr "Descripción"
+
+#: stories/delete.php:51 channels/delete.php:50
+msgid "Do not delete"
+msgstr "No eliminar"
+
+#: lib/Forms/Feed.php:40
+msgid "Edit Feed"
+msgstr "Modificar suscripción"
+
+#: lib/Forms/Story.php:37
+msgid "Edit Story"
+msgstr "Modificar historia"
+
+#: lib/Forms/Feed.php:100
+msgid "Edit aggregated feeds"
+msgstr "Modificar suscripciones asociadas"
+
+#: channels/index.php:44
+msgid "Edit channel"
+msgstr "Modificar canal"
+
+#: channels/aggregate.php:25
+#, php-format
+msgid "Edit channel \"%s\""
+msgstr "Modificar el canal \"%s\""
+
+#: stories/index.php:92 stories/results.php:87
+msgid "Edit story"
+msgstr "Modificar historia"
+
+#: lib/Delivery/email.php:180 lib/Delivery/email.php:195
+msgid "Email"
+msgstr "Correo"
+
+#: delivery/email.php:64
+#, php-format
+msgid "Email Delivery for \"%s\""
+msgstr "Entrega de correo de \"%s\""
+
+#: delivery/email.php:78 lib/Block/delivery_email.php:96
+msgid "Email address"
+msgstr "Correo electrónico"
+
+#: lib/News.php:554
+#, php-format
+msgid "Error fetching feed: %s"
+msgstr "Error al traer la suscripción: %s"
+
+#: stories/pdf.php:15 stories/share.php:66 stories/view.php:25
+#: stories/view.php:35 lib/Block/latest.php:91 lib/Block/story.php:91
+#, php-format
+msgid "Error fetching story: %s"
+msgstr "Error al traer la historia: %s"
+
+#: lib/News.php:793
+#, php-format
+msgid "Error parsing external feed from %s: %s"
+msgstr "Error al procesar la suscripción externa de %s: %s"
+
+#: lib/api.php:109
+msgid "External Channels"
+msgstr "Canales externos"
+
+#: lib/News.php:506 lib/Jonah.php:135
+msgid "External Feed"
+msgstr "Suscripción externa"
+
+#: lib/Forms/Feed.php:51
+msgid "Extra information for this feed type"
+msgstr "Información adicional para este tipo de suscripción"
+
+#: lib/Block/news_popular.php:32 lib/Block/story.php:42 lib/Block/news.php:3
+#: lib/Block/news.php:30
+msgid "Feed"
+msgstr "Suscripción"
+
+#: lib/News.php:113
+#, php-format
+msgid "Feed \"%s\" is not authored on this system."
+msgstr "La suscripción \"%s\" no se ha generado en este sistema."
+
+#: channels/edit.php:56
+msgid "Feed type changed."
+msgstr "Se ha cambiado el tipo de suscripción."
+
+#: lib/Block/delivery.php:3 lib/Block/delivery.php:25 channels/index.php:96
+msgid "Feeds"
+msgstr "Suscripciones"
+
+#: lib/Block/news.php:54
+msgid "First Story"
+msgstr "Primera historia"
+
+#: stories/share.php:78
+msgid "From"
+msgstr "De"
+
+#: lib/Forms/Story.php:74 lib/Forms/Story.php:76
+msgid "Full Story Text"
+msgstr "Texto completo de la historia"
+
+#: delivery/html.php:39
+#, php-format
+msgid "HTML Delivery for \"%s\""
+msgstr "Entrega HTML de \"%s\""
+
+#: lib/News.php:693
+msgid "HTML Version of Story"
+msgstr "Versión HTML de la historia"
+
+#: lib/Block/delivery.php:67
+#, php-format
+msgid "Have \"%s\" emailed to you"
+msgstr "Enviarme \"%s\" por correo"
+
+#: lib/Delivery/email.php:72 lib/Delivery/email.php:75
+msgid "Hello"
+msgstr "Hola"
+
+#: lib/Forms/Story.php:82
+msgid ""
+"If you enter a URL without a full story text, clicking on the story will "
+"send the reader straight to the URL, otherwise it will be shown at the end "
+"of the full story."
+msgstr ""
+"Si introduce una URL sin un texto completo de historia, al hacer click en la "
+"historia se enviará al lector directamente a la URL, de otro modo se le "
+"mostrará al final de la historia completa."
+
+#: lib/Forms/Feed.php:87 channels/aggregate.php:61
+msgid "Image"
+msgstr "Imagen"
+
+#: stories/share.php:86
+msgid "Include"
+msgstr "Incluir"
+
+#: config/.bak/templates.php.dist:31
+msgid "Internal"
+msgstr "Internos"
+
+#: lib/api.php:91
+msgid "Internal Channels"
+msgstr "Canales internos"
+
+#: stories/index.php:43 stories/results.php:49
+#, php-format
+msgid "Invalid channel requested. %s"
+msgstr "Se ha solicitado un canal no válido. %s"
+
+#: channels/delete.php:35
+msgid "Invalid channel specified for deletion."
+msgstr "Se ha indicado un canal no válido para eliminar."
+
+#: lists/delete.php:37 lists/edit.php:36 delivery/html.php:33
+#: delivery/email.php:55
+msgid "Invalid channel."
+msgstr "Canal no válido."
+
+#: lists/delete.php:27
+msgid "Invalid request to delete recipient from delivery list."
+msgstr ""
+"Petición no válida de eliminación de destinatario de la lista de "
+"distribución."
+
+#: delivery/email.php:69
+msgid "Join this channel"
+msgstr "Unirse al canal"
+
+#: channels/index.php:91
+msgid "Last Update"
+msgstr "Última actualización"
+
+#: lib/Block/latest.php:3 lib/Block/latest.php:65
+msgid "Latest News"
+msgstr "Últimas Noticias"
+
+#: delivery/email.php:70
+msgid "Leave this channel"
+msgstr "Abandonar este canal"
+
+#: lib/Forms/Feed.php:86 channels/aggregate.php:60
+msgid "Link"
+msgstr "Vínculo"
+
+#: stories/index.php:128
+msgid "Lists"
+msgstr "Listas"
+
+#: lib/News.php:509 lib/Jonah.php:132
+msgid "Local Feed"
+msgstr "Suscripción local"
+
+#: channels/index.php:90
+msgid "Manage Feeds"
+msgstr "Gestionar suscripciones"
+
+#: lib/Block/news_popular.php:53 lib/Block/news.php:49
+msgid "Maximum Stories"
+msgstr "Cantidad máxima de historias"
+
+#: config/.bak/templates.php.dist:20
+msgid "Media"
+msgstr "Medio"
+
+#: lib/Block/tree_menu.php:3
+msgid "Menu List"
+msgstr "Lista del menú"
+
+#: stories/share.php:87
+msgid "Message"
+msgstr "Mensaje"
+
+#: lists/index.php:125
+msgid "Method"
+msgstr "Método"
+
+#: lib/News.php:74
+msgid "Missing channel id."
+msgstr "Falta el id del canal."
+
+#: lib/Block/news_popular.php:3
+msgid "Most Popular Stories"
+msgstr "Historias más populares"
+
+#: content.php:31 templates/content/header.inc:2
+msgid "My News"
+msgstr "Mis noticias"
+
+#: content_edit.php:32
+msgid "My News :: Add Content"
+msgstr "Mis noticias :: Añadir contenido"
+
+#: lists/index.php:125 delivery/email.php:83 lib/Forms/Feed.php:50
+#: lib/Delivery/email.php:197 lib/Block/delivery_email.php:97
+#: channels/index.php:91
+msgid "Name"
+msgstr "Nombre"
+
+#: lib/Forms/Feed.php:40
+msgid "New Feed"
+msgstr "Añadir suscripción"
+
+#: stories/index.php:122 lib/Block/news_popular.php:86
+msgid "New Story"
+msgstr "Nueva historia"
+
+#: lists/index.php:106
+msgid "New recipient"
+msgstr "Nuevo destinatario"
+
+#: lib/api.php:90 lib/api.php:108
+msgid "News"
+msgstr "Noticias"
+
+#: lib/Block/delivery_email.php:29
+msgid "News Channel"
+msgstr "Canal de noticias"
+
+#: lib/Block/latest.php:33
+msgid "News Source"
+msgstr "Origen de noticias"
+
+#: lib/Block/delivery_email.php:3
+msgid "News by Email"
+msgstr "Noticias por correo"
+
+#: lists/index.php:40 channels/index.php:25
+msgid "News is not enabled."
+msgstr "No están activadas las noticias."
+
+#: stories/index.php:51 stories/results.php:57
+msgid "No available stories."
+msgstr "No se dispone de historias."
+
+#: lists/index.php:26 stories/index.php:23
+msgid "No channel requested."
+msgstr "No se ha solicitado un canal."
+
+#: lib/Block/latest.php:86
+msgid "No channel specified."
+msgstr "No se ha especificado un canal."
+
+#: templates/channels/index.html:41
+msgid "No channels are available."
+msgstr "No hay canales disponibles."
+
+#: lib/Block/delivery_email.php:67
+msgid "No channels available."
+msgstr "No hay canales disponibles."
+
+#: lib/Block/news_popular.php:99 lib/Block/news.php:92
+msgid "No feed specified."
+msgstr "No se ha indicado una suscripción."
+
+#: lib/Block/delivery.php:76
+msgid "No feeds are available."
+msgstr "No hay suscripciones disponibles."
+
+#: lists/index.php:79
+msgid "No recipients."
+msgstr "Sin destinatarios."
+
+#: lib/News.php:584
+msgid "No stories are currently available."
+msgstr "Actualment no se dispone de historias."
+
+#: lib/Block/story.php:86
+msgid "No story is selected."
+msgstr "No se ha seleccionado ninguna historia."
+
+#: lib/Delivery.php:215
+#, php-format
+msgid "No such action \"%s\" found"
+msgstr "No se encontró el tipo de acción \"%s\""
+
+#: lib/News.php:743
+#, php-format
+msgid "No such backend \"%s\" found"
+msgstr "No se encontró el motor \"%s\""
+
+#: stories/results.php:40
+msgid "No tag requested."
+msgstr "No se ha solicitado una etiqueta."
+
+#: stories/delete.php:36
+msgid "No valid story requested for deletion."
+msgstr "No se ha solicitado una historia válida para borrar."
+
+#: stories/share.php:25
+msgid "Note"
+msgstr "Nota"
+
+#: lib/Forms/Story.php:58
+msgid ""
+"Note: this story won't be delivered to distribution lists automatically if "
+"this time is in the future."
+msgstr ""
+"Observación: esta historia no se enviará de forma automática a las listas de "
+"distribución si esta hora está en el futuro."
+
+#: lib/Forms/Story.php:55
+msgid "Or publish on this date:"
+msgstr "O publicar en esta fecha:"
+
+#: stories/index.php:87 stories/results.php:81
+msgid "PDF version"
+msgstr "Versión PDF"
+
+#: channels/delete.php:22
+msgid "Permission Denied."
+msgstr "Permiso denegado."
+
+#: lib/News.php:688
+msgid "Plaintext Version of Story"
+msgstr "Versión en texto de la historia"
+
+#: lib/Forms/Story.php:45
+msgid "Publish Now?"
+msgstr "¿Publicar ahora?"
+
+#: lib/Block/delivery.php:59
+#, php-format
+msgid "RSS Feed of \"%s\""
+msgstr "Suscripción RSS de \"%s\""
+
+#: templates/stories/index.html:32
+msgid "Read"
+msgstr "Leer"
+
+#: channels/delete.php:53
+msgid "Really delete this News Channel?"
+msgstr "¿Eliminar realmente este canal de noticias?"
+
+#: stories/delete.php:54
+msgid "Really delete this News Story?"
+msgstr "¿Eliminar realmente esta historia de noticias?"
+
+#: lists/index.php:125
+msgid "Recipient"
+msgstr "Destinatario"
+
+#: lists/edit.php:86
+msgid "Recipient saved."
+msgstr "Se ha guardado el destinatario."
+
+#: lists/delete.php:52
+msgid "Recipient successfully removed."
+msgstr "Se ha eliminado correctamente el destinatario."
+
+#: stories/index.php:135
+msgid "Refresh Channel"
+msgstr "Actualizar canal"
+
+#: channels/index.php:79
+msgid "Refresh channel"
+msgstr "Actualizar canal"
+
+#: lists/delete.php:54
+msgid "Removal of recipient failed."
+msgstr "Ha fallado la eliminación del destinatario."
+
+#: channels/aggregate.php:26
+#, php-format
+msgid "Remove channel \"%s\""
+msgstr "Eliminar el canal \"%s\""
+
+#: delivery/email.php:44
+msgid "Request confirmed."
+msgstr "Solicitud confirmada."
+
+#: lib/Delivery/email.php:69
+#, php-format
+msgid "Request to receive news channel \"%s\""
+msgstr "Solicitar recibir el canal de noticias \"%s\""
+
+#: lib/Delivery/email.php:74
+#, php-format
+msgid "Request to stop receiving news channel \"%s\""
+msgstr "Solicitar dejar de recibir el canal de noticias \"%s\""
+
+#: lib/Block/cloud.php:27
+msgid "Results URL"
+msgstr "URL de resultados"
+
+#: lib/Jonah.php:218
+msgid "Rich Text"
+msgstr "Texto enriquecido"
+
+#: lists/edit.php:52 delivery/email.php:66 lib/Forms/Story.php:39
+#: lib/Block/delivery_email.php:82
+msgid "Save"
+msgstr "Guardar"
+
+#: templates/delivery/html.html:13
+msgid "Select a format:"
+msgstr "Seleccione un formato:"
+
+#: stories/share.php:75
+msgid "Send"
+msgstr "Enviar"
+
+#: stories/share.php:84
+msgid "Separate multiple email addresses with commas."
+msgstr "Separar varias direcciones de correo mediante comas."
+
+#: stories/share.php:73
+msgid "Share Story"
+msgstr "Compartir historia"
+
+#: stories/view.php:87
+msgid "Share this story"
+msgstr "Compartir esta historia"
+
+#: lib/Forms/Story.php:44
+msgid "Short Description"
+msgstr "Descripción corta"
+
+#: lib/Forms/Feed.php:85 channels/aggregate.php:59
+msgid "Source URL"
+msgstr "URL origen"
+
+#: lib/Forms/Feed.php:100
+msgid "Source URLs"
+msgstr "URLs origen"
+
+#: config/.bak/templates.php.dist:9
+msgid "Standard"
+msgstr "Estándar"
+
+#: templates/stories/index.html:9
+msgid "Stories"
+msgstr "Historias"
+
+#: stories/results.php:112 delivery/rss.php:58
+#, php-format
+msgid "Stories tagged with %s in %s"
+msgstr "Historias etiquetadas como %s en %s"
+
+#: stories/index.php:136 stories/results.php:116 lib/Block/story.php:3
+#: lib/Block/story.php:46 lib/Block/story.php:65
+msgid "Story"
+msgstr "Historia"
+
+#: lib/News.php:764
+#, php-format
+msgid "Story \"%s\" not found in \"%s\"."
+msgstr "No se encontró la historia \"%s\" en \"%s\"."
+
+#: stories/share.php:109
+msgid "Story Link"
+msgstr "Vínculo de la historia"
+
+#: lib/Forms/Story.php:43
+msgid "Story Title (Headline)"
+msgstr "Título de la historia (Titular)"
+
+#: lib/Forms/Story.php:82
+msgid "Story URL"
+msgstr "URL de la historia"
+
+#: lib/News/sql.php:456
+#, php-format
+msgid "Story URL \"%s\" not found."
+msgstr "No se encontró la URL \"%s\" de la historia."
+
+#: lib/Forms/Feed.php:78 lib/Forms/Feed.php:113
+#, php-format
+msgid ""
+"Story URL if not the default one. %c gets replaced by the feed ID, %s by the "
+"story ID."
+msgstr ""
+"La URL de la historia no es la URL por omisión. %c se sustituye por el ID de "
+"la suscripción, %s por el ID de la historia."
+
+#: lib/Forms/Story.php:61
+msgid "Story body type"
+msgstr "Tipo de cuerpo de la historia"
+
+#: stories/edit.php:34
+#, php-format
+msgid "Story editing failed: %s"
+msgstr "Falló la modificación de la historia: %s"
+
+#: stories/delete.php:71
+msgid "Story has not been deleted."
+msgstr "No se ha eliminado la historia."
+
+#: lib/News/sql.php:424
+#, php-format
+msgid "Story id \"%s\" not found."
+msgstr "No se encontró el id \"%s\" de historia."
+
+#: stories/share.php:85
+msgid "Subject"
+msgstr "Asunto"
+
+#: lib/News.php:363
+#, php-format
+msgid "Successfully delivered to the %s distribution list."
+msgstr "Se ha enviado correctamente a la lista de distribución %s."
+
+#: lib/Block/cloud.php:3 lib/Block/cloud.php:35
+msgid "Tag Cloud"
+msgstr "Conjunto de etiquetas"
+
+#: lib/News.php:834 lib/News.php:839 lib/News.php:844 lib/News.php:850
+#: lib/News.php:855
+msgid "Tag support not enabled in backend."
+msgstr "El soporte de etiquetas no está habilitado en el motor."
+
+#: lib/Forms/Story.php:79
+msgid "Tags"
+msgstr "Etiquetas"
+
+#: lib/Block/story.php:105 templates/stories/story.html:6
+msgid "Tags: "
+msgstr "Etiquetas: "
+
+#: lib/Jonah.php:225
+msgid "Text"
+msgstr "Texto"
+
+#: channels/aggregate.php:95
+#, php-format
+msgid "The channel \"%s\" has been removed."
+msgstr "Se ha eliminado el canal \"%s\"."
+
+#: channels/aggregate.php:76
+#, php-format
+msgid "The channel \"%s\" has been saved."
+msgstr "Se ha guardado el canal \"%s\"."
+
+#: channels/aggregate.php:84 channels/aggregate.php:102
+#, php-format
+msgid "The channel \"%s\" has been updated."
+msgstr "Se ha actualizado el canal \"%s\"."
+
+#: channels/delete.php:68
+msgid "The channel has been deleted."
+msgstr "Se ha eliminado el canal."
+
+#: stories/share.php:86
+msgid "The complete text of the story"
+msgstr "El texto completo de la historia"
+
+#: channels/edit.php:70
+#, php-format
+msgid "The feed \"%s\" has been saved."
+msgstr "Se ha guardado la suscripción \"%s\"."
+
+#: lib/Forms/Feed.php:94
+msgid ""
+"The interval before stories aggregated into this feeds are rechecked for "
+"updates. If none, then stories will always be refetched from the sources."
+msgstr ""
+"Intervalo antes de que se vuelvan a comprobar las actualizaciones de las "
+"historias asociadas a estas suscripciones. Si es nulo, las historias se "
+"traerán siempre del origen."
+
+#: lib/Forms/Feed.php:83
+msgid ""
+"The interval before stories in this feed are rechecked for updates. If none, "
+"then stories will always be refetched from the source."
+msgstr ""
+"Intervalo antes de que se vuelvan a comprobar las actualizaciones de las "
+"historias de esta suscripción. Si es nulo, las historias se traerán siempre "
+"del origen."
+
+#: stories/edit.php:63
+#, php-format
+msgid "The story \"%s\" has been saved."
+msgstr "Se ha guardado la historia \"%s\"."
+
+#: stories/delete.php:63
+msgid "The story has been deleted."
+msgstr "Se ha eliminado la historia."
+
+#: stories/share.php:120
+msgid "The story was sent successfully."
+msgstr "Se envío correctamente la historia."
+
+#: lib/Forms/Feed.php:85 channels/aggregate.php:59
+msgid ""
+"The url to use to fetch the stories, for example 'http://www.example.com/"
+"stories.rss'"
+msgstr ""
+"Url utilizado para traer las historias, por ejemplo 'http://www.ejemplo.com/"
+"historias.rss'"
+
+#: channels/delete.php:66
+#, php-format
+msgid "There was an error deleting the channel: %s"
+msgstr "Se produjo un error al eliminar el canal: %s"
+
+#: stories/delete.php:61
+#, php-format
+msgid "There was an error deleting the story: %s"
+msgstr "Se produjo un error al eliminar la historia: %s"
+
+#: channels/aggregate.php:93
+#, php-format
+msgid "There was an error removing the channel: %s"
+msgstr "Se produjo un error al eliminar el canal: %s"
+
+#: channels/aggregate.php:74
+#, php-format
+msgid "There was an error saving the channel: %s"
+msgstr "Se produjo un error al guardar el canal: %s"
+
+#: channels/edit.php:68
+#, php-format
+msgid "There was an error saving the feed: %s"
+msgstr "Se produjo un error al guardar la suscripción: %s"
+
+#: stories/edit.php:61
+#, php-format
+msgid "There was an error saving the story: %s"
+msgstr "Se produjo un error al guardar la historia: %s"
+
+#: channels/aggregate.php:82 channels/aggregate.php:100
+#, php-format
+msgid "There was an error updating the channel: %s"
+msgstr "Se produjo un error al actualizar el canal: %s"
+
+#: lib/Delivery/email.php:181
+msgid ""
+"This driver allows the delivery of news stories via email to one or more "
+"recipients."
+msgstr ""
+"Este controlador permite el envío por correo de historias de noticias a uno "
+"o más destinatarios."
+
+#: channels/aggregate.php:43
+msgid "This is no aggregated channel."
+msgstr "Éste no es un canal asociado."
+
+#: stories/share.php:84
+msgid "To"
+msgstr "Para"
+
+#: lib/Forms/Feed.php:45 channels/index.php:91
+msgid "Type"
+msgstr "Tipo"
+
+#: config/.bak/templates.php.dist:53
+msgid "Ultracompact"
+msgstr "Ultracompacto"
+
+#: delivery/email.php:42
+#, php-format
+msgid "Unable to confirm request."
+msgstr "Incapaz de confirmar la solicitud."
+
+#: stories/share.php:118
+#, php-format
+msgid "Unable to send story: %s"
+msgstr "Incapaz de enviar historia: %s"
+
+#: channels/aggregate.php:107
+msgid "Update"
+msgstr "Actualizar"
+
+#: lib/Block/news_popular.php:45 lib/Block/news.php:41
+msgid "View"
+msgstr "Ver"
+
+#: delivery/email.php:67
+msgid "What do you want to do?"
+msgstr "¿Qué desea hacer?"
+
+#: lists/index.php:34 lists/delete.php:45 lists/edit.php:44
+#: stories/index.php:31 stories/delete.php:22 stories/results.php:24
+#: stories/edit.php:42 channels/index.php:19 channels/aggregate.php:50
+#: channels/edit.php:43
+msgid "You are not authorised for this action."
+msgstr "Carece de autorización para realizar esta acción."
+
+#: channels/edit.php:72
+msgid "You can now edit the sub-feeds."
+msgstr "Ahora puede modificar las suscripciones secundarias"
+
+#: lib/Delivery/email.php:72
+#, php-format
+msgid ""
+"You have requested to receive \"%s\" stories by email. Click on this link to "
+"confirm the request: %s"
+msgstr ""
+"Ha solicitado recibir por correo las historias de \"%s\". Pulse este vínculo "
+"para confirmar la petición: %s"
+
+#: lib/Delivery/email.php:75
+#, php-format
+msgid ""
+"You have requested to stop receiving \"%s\" stories by email. Click on this "
+"link to confirm the request: %s"
+msgstr ""
+"Ha solicitado dejar de recibir por correo las historias de \"%s\". Pulse "
+"este vínculo para confirmar la petición: %s"
+
+#: lib/Delivery.php:272
+msgid "You must configure a Horde Datatree backend to use Jonah."
+msgstr "Para usar Jonah tiene que configurar un motor de DataTree."
+
+#: lib/News.php:806
+msgid "[No title]"
+msgstr "[Sin título]"
+
+#: lib/Jonah.php:266
+msgid "_Feeds"
+msgstr "_Suscripciones"
+
+#: lib/Jonah.php:262
+msgid "_My News"
+msgstr ":Mis noticias"
+
+#: lib/Jonah.php:267
+msgid "_New Feed"
+msgstr "_Añadir suscripción"
+
+#: lib/News.php:472
+msgid "none"
+msgstr "ninguno"
--- /dev/null
+# Finnish translation for Jonah.
+# Copyright
+# Leena Heino <liinu@uta.fi>, 2003-2004.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Jonah 0.1-cvs\n"
+"Report-Msgid-Bugs-To: dev@lists.horde.org\n"
+"POT-Creation-Date: 2005-03-14 11:02+0200\n"
+"PO-Revision-Date: 2004-12-10 12:59+0200\n"
+"Last-Translator: Leena Heino <liinu@uta.fi>\n"
+"Language-Team: Finnish <i18n@lists.horde.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-1\n"
+"Content-Transfer-Encoding: 8-bit\n"
+
+#: lib/Block/delivery_email.php:60
+#, php-format
+msgid "%s by Email"
+msgstr "%s Sähköpostitse"
+
+#: lists/index.php:118
+#, php-format
+msgid "%s to %s of %s"
+msgstr "%s - %s / %s"
+
+#: lib/Block/delivery.php:77 delivery/index.php:47
+#, php-format
+msgid "'%s' stories by email"
+msgstr "'%s' artikkelia sähköpostitse"
+
+#: lib/Block/delivery.php:60 delivery/index.php:35
+#, php-format
+msgid "'%s' stories in HTML"
+msgstr "'%s' artikkelia HTML-muodossa"
+
+#: lib/Block/delivery.php:69 delivery/index.php:41
+#, php-format
+msgid "'%s' stories in XML"
+msgstr "'%s' artikkelia XML:llä"
+
+#: lib/News.php:417
+msgid "1 hour"
+msgstr "1 tunti"
+
+#: lib/News.php:421
+msgid "12 hours"
+msgstr "12 tuntia"
+
+#: lib/News.php:418
+msgid "2 hours"
+msgstr "2 tuntia"
+
+#: lib/News.php:422
+msgid "24 hours"
+msgstr "24 tuntia"
+
+#: lib/News.php:416
+msgid "30 mins"
+msgstr "30 minuuttia"
+
+#: lib/News.php:419
+msgid "4 hours"
+msgstr "4 tuntia"
+
+#: lib/News.php:420
+msgid "8 hours"
+msgstr "8 tuntia"
+
+#: delivery/email.php:95
+#, php-format
+msgid ""
+"A confirmation message has been sent to %s. Click on the link in that "
+"message to finally subscribe to channel \"%s\"."
+msgstr ""
+"Varmistusviesti on lähetetty osoitteeseen %s. Napsauta viestissä olevaan "
+"linkkiä tilataksesi kanavan \"%s\"."
+
+#: stories/share.php:104
+msgid "A link to the story"
+msgstr "Linkki artikkeliin"
+
+#: channels/aggregate.php:54
+msgid "Add"
+msgstr "Lisää"
+
+#: lib/Form/EditChannel.php:32
+msgid "Add New Channel"
+msgstr "Lisää uusi kanava"
+
+#: stories/edit.php:57
+msgid "Add New Story"
+msgstr "Lisää uusi artikkeli"
+
+#: stories/view.php:56
+msgid "Add a comment"
+msgstr "Lisää kommentti"
+
+#: channels/index.php:68
+msgid "Add story"
+msgstr "Lisää artikkeli"
+
+#: lib/Jonah.php:127 lib/News.php:455
+msgid "Aggregated"
+msgstr "Yhdistelty"
+
+#: channels/aggregate.php:53
+#, php-format
+msgid "Aggregated channels for channel \"%s\""
+msgstr "Yhdistetyt kanavat kanavalle \"%s\""
+
+#: channels/delete.php:54
+msgid "All stories created in this channel will be lost!"
+msgstr "Kaikki tälle kanavalle luodut artikkelit tuhoutuvat!"
+
+#: channels/index.php:33
+#, php-format
+msgid "An error occurred fetching channels: %s"
+msgstr "Taphtui virhe haettaessa kanavia: %s"
+
+#: channels/delete.php:56
+msgid "Any cached stories for this channel will be lost!"
+msgstr "Kaikki tämän kanavan välimuistissa olevat artikkelit tuhoutuvat!"
+
+#: channels/index.php:91
+msgid "Available Channels"
+msgstr "Saatavissaolevat kanavat"
+
+#: lib/Block/delivery.php:35
+#, php-format
+msgid "Available Channels from %s"
+msgstr "Saatavissaolevat kanavat %s:stä"
+
+#: delivery/index.php:29
+#, php-format
+msgid "Available Channels from %s (%s)"
+msgstr "Saatavissaolevat kanavat %s:stä (%s)"
+
+#: lib/Block/delivery.php:51
+msgid "Available News Channels"
+msgstr "Saatavissaolevat uutiskanavat"
+
+#: lib/Form/EditChannel.php:70 lib/Form/EditChannel.php:81
+msgid "Caching"
+msgstr "Haetaan välimuistiin"
+
+#: config/prefs.php.dist:18
+msgid "Change your content display options."
+msgstr "Voit muuttaa näkymään liittyviä asetuksia."
+
+#: lib/Block/delivery_email.php:83
+msgid "Channel"
+msgstr "kanavat"
+
+#: stories/index.php:114
+#, php-format
+msgid "Channel '%s'"
+msgstr "Kanava '%s'"
+
+#: lib/News.php:110
+#, php-format
+msgid "Channel '%s' is not an internally authored channel."
+msgstr "Kanava '%s' ei pidetä itse yllä."
+
+#: content.php:77
+msgid "Channel Listing"
+msgstr "Kanavan listaus"
+
+#: lists/index.php:128
+msgid "Channel Lists Admin"
+msgstr "Kanavalistauksien ylläpitäjä"
+
+#: templates/subscribe/column_headers.inc:7 lib/Form/EditChannel.php:43
+#: channels/aggregate.php:56
+msgid "Channel Name"
+msgstr "Kanavan nimi"
+
+#: subscribe.php:134
+msgid "Channel Subscriptions"
+msgstr "Kanavan tilaukset"
+
+#: subscribe.php:127
+msgid "Channel display failed."
+msgstr "Kanavan näyttäminen epäonnistui."
+
+#: channels/delete.php:76
+msgid "Channel has not been deleted."
+msgstr "Kanavaa ei ole poistettu."
+
+#: lib/News/sql.php:158
+#, php-format
+msgid "Channel id '%s' not found."
+msgstr "Kanavaa id '%s' ei löydy."
+
+#: stories/index.php:51
+msgid "Channel refreshed."
+msgstr "Kanava päivitetty."
+
+#: subscribe.php:86
+msgid "Channel subscription editing failed."
+msgstr "Kanavan tilaustietojen muokkaus epäonnistui."
+
+#: subscribe.php:41
+msgid "Channel subscription failed."
+msgstr "Kanavan tilaus epäonnistui."
+
+#: channels/edit.php:56
+msgid "Channel type changed."
+msgstr "Kanavan tyyppi on vaihdettu."
+
+#: subscribe.php:73
+msgid "Channel unsubscription failed."
+msgstr "Kanavan tilauksen lopettaminen."
+
+#: config/prefs.php.dist:11
+msgid "Choose which news channels you are subscribed to."
+msgstr "Voit valita ne uutiskanavat jotka haluat tilata."
+
+#: templates/stories/index.html:37
+msgid "Comments"
+msgstr "Kommentit"
+
+#: config/templates.php.dist:40
+msgid "Compact"
+msgstr "Pienennetty"
+
+#: lib/Jonah.php:130 lib/News.php:458
+msgid "Composite"
+msgstr "Yhdistelmä"
+
+#: lib/Form/EditChannel.php:100
+msgid "Composite channels"
+msgstr "Yhdistelmä kanavat"
+
+#: lib/Block/news.php:3
+msgid "Content Channel"
+msgstr "Sisältökanava"
+
+#: lib/News.php:281
+#, php-format
+msgid "Could not deliver to the %s distribution list. %s"
+msgstr "Ei voitu toimittaa %s jakelulistalle. %s"
+
+#: delivery/index.php:23
+#, php-format
+msgid "Could not get channel list. %s"
+msgstr "Kanavalistausta ei saatu. %s"
+
+#: lib/Jonah.php:71
+#, php-format
+msgid "Could not open %s."
+msgstr "Ei voitu aukaista %s."
+
+#: lists/index.php:107
+msgid "Current Recipients"
+msgstr "Nykyiset vastaanottajat"
+
+#: stories/delete.php:50 stories/delete.php:55 channels/delete.php:49
+#: channels/delete.php:60
+msgid "Delete"
+msgstr "Poista"
+
+#: channels/delete.php:46
+#, php-format
+msgid "Delete News Channel \"%s\"?"
+msgstr "Poista uutiskanava \"%s\"?"
+
+#: stories/delete.php:46
+#, php-format
+msgid "Delete News Story \"%s\"?"
+msgstr "Poista uutisartikkeli \"%s\"?"
+
+#: channels/index.php:49
+msgid "Delete channel"
+msgstr "Poista kanava"
+
+#: lists/index.php:80
+msgid "Delete recipient"
+msgstr "Poista vastaanottaja"
+
+#: stories/index.php:86
+msgid "Delete story"
+msgstr "Poista artikkeli"
+
+#: lib/Jonah.php:254 lists/edit.php:56 lists/edit.php:61
+msgid "Delivery"
+msgstr "Jakelu"
+
+#: channels/index.php:60
+msgid "Delivery lists"
+msgstr "Jakelulista"
+
+#: lists/edit.php:49
+#, php-format
+msgid "Delivery of '%s' stories"
+msgstr "'%s' artikkeleiden jakelu"
+
+#: lib/Form/EditChannel.php:64 lib/Form/EditChannel.php:80
+#: lib/Form/EditChannel.php:98
+msgid "Description"
+msgstr "Kuvaus"
+
+#: config/templates.php.dist:9
+msgid "Detailed"
+msgstr "Yksityiskohtainen"
+
+#: config/prefs.php.dist:17
+msgid "Display Options"
+msgstr "Näkymän asetukset"
+
+#: stories/delete.php:50 channels/delete.php:49
+msgid "Do not delete"
+msgstr "Ei saa poistaa"
+
+#: templates/subscribe/column_headers.inc:13
+#: templates/subscribe/subscribe.inc:49
+msgid "Edit"
+msgstr "Muokkaa"
+
+#: lib/Form/EditChannel.php:32
+msgid "Edit Channel"
+msgstr "Muokkaa kanavaa"
+
+#: templates/subscribe/header.inc:3
+msgid "Edit Channel Subscriptions"
+msgstr "Muokkaa kanavan tilaustietoja"
+
+#: stories/edit.php:57
+msgid "Edit Story"
+msgstr "Muokkaa artikkelia"
+
+#: templates/subscribe/subscribe.inc:49
+msgid "Edit Subscription Details"
+msgstr "Muokkaa kanavan tilauksen yksityiskohtia"
+
+#: lib/Form/EditChannel.php:87
+msgid "Edit aggregated channels"
+msgstr "Muokkaa yhdisteltyjä kanavia"
+
+#: channels/index.php:44
+msgid "Edit channel"
+msgstr "Muokkaa kanavaa"
+
+#: channels/aggregate.php:23
+#, php-format
+msgid "Edit channel \"%s\""
+msgstr "Muokkaa kanavaa \"%s\""
+
+#: stories/index.php:81
+msgid "Edit story"
+msgstr "Muokkaa artikkelia"
+
+#: templates/subscribe/edit.inc:5
+#, php-format
+msgid "Editing Channel %s"
+msgstr "Muokataan kanavaa %s"
+
+#: lib/Delivery/email.php:195 lib/Delivery/email.php:210
+msgid "Email"
+msgstr "Sähköposti"
+
+#: delivery/email.php:62
+#, php-format
+msgid "Email Delivery for \"%s\""
+msgstr "Jakelu sähköpostitse \"%s\":lle"
+
+#: lib/Block/delivery_email.php:92 delivery/email.php:76
+msgid "Email address"
+msgstr "Sähköpostiosoite"
+
+#: lib/News.php:499
+#, php-format
+msgid "Error fetching channel. %s"
+msgstr "Tapahtui virhe haettaessa kanavaa. %s"
+
+#: stories/share.php:83 stories/view.php:23
+#, php-format
+msgid "Error fetching story: %s"
+msgstr "Tapahtui virhe haettaessa artikkelia: %s"
+
+#: lib/News.php:391
+#, php-format
+msgid "Error parsing external channel from %s: %s"
+msgstr "Tapahtui virhe tulkittaessa ulkoista kanavaa lähteestä %s: %s"
+
+#: lib/Jonah.php:124 lib/News.php:449
+msgid "External"
+msgstr "Ulkopuolinen"
+
+#: lib/Form/EditChannel.php:44
+msgid "Extra information for this channel type"
+msgstr "Tämän kanavatyypin lisätietoja"
+
+#: lib/Block/news.php:62
+msgid "First Story"
+msgstr "Ensimmäinen artikkeli"
+
+#: stories/share.php:101
+msgid "From"
+msgstr "Lähettäjä"
+
+#: stories/edit.php:89 stories/edit.php:91
+msgid "Full Story Text"
+msgstr "Artikkeli kokonaisuudessaan"
+
+#: delivery/html.php:35
+#, php-format
+msgid "HTML Delivery for '%s'"
+msgstr "HTML jakelu '%s':lle"
+
+#: lib/News.php:584
+msgid "HTML Version of Story"
+msgstr "Artikkelin HTML-versio"
+
+#: templates/content/content.html:11
+msgid "Headlines"
+msgstr "Pääotsikot"
+
+#: lib/Delivery/email.php:74 lib/Delivery/email.php:77
+msgid "Hello"
+msgstr "Päivää"
+
+#: config/prefs.php.dist:35
+msgid "How many columns would you like to use to show headlines?"
+msgstr "Kuinka monta saraketta käytetään otsikoiden esittämiseen?"
+
+#: stories/edit.php:96
+msgid ""
+"If you enter a URL without a full story text, clicking on the story will "
+"send the reader straight to the URL, otherwise it will be shown at the end "
+"of the full story."
+msgstr ""
+"Jos laita URL:n ilman artikkelin teksti, niin artikkelia napsauttamalla "
+"lähetät lukijan siihen URL:iin. Muussa tapauksessa URL näkyy vasta "
+"artikkelin lopussa."
+
+#: lib/Form/EditChannel.php:74 channels/aggregate.php:59
+msgid "Image"
+msgstr "Kuva"
+
+#: stories/share.php:104
+msgid "Include"
+msgstr "Lisää"
+
+#: lib/Jonah.php:121 lib/News.php:452
+msgid "Internal"
+msgstr "Sisäinen"
+
+#: lib/api.php:47
+msgid "Internal Channels"
+msgstr "Sisäiset kanavat"
+
+#: stories/index.php:41
+#, php-format
+msgid "Invalid channel requested. %s"
+msgstr "Pyydettiin epäkelpoa kanavaa. %s"
+
+#: channels/delete.php:34
+msgid "Invalid channel specified for deletion."
+msgstr "Poistettavaksi määritelty kanava on epäkelpo."
+
+#: lists/edit.php:33 lists/delete.php:37 delivery/email.php:52
+#: delivery/html.php:29
+msgid "Invalid channel."
+msgstr "Epäkelpo kanava."
+
+#: lists/delete.php:27
+msgid "Invalid request to delete recipient from delivery list."
+msgstr "Epäkelpo pyyntö poistaa vastaanottaja jakelulistalta."
+
+#: delivery/email.php:67
+msgid "Join this channel"
+msgstr "Liity tälle kanavalle"
+
+#: delivery/index.php:53 channels/index.php:92
+msgid "Last Update"
+msgstr "Edellinen päivitys"
+
+#: templates/subscribe/column_headers.inc:9
+msgid "Last Updated"
+msgstr "Päivitetty viimeksi"
+
+#: delivery/email.php:68
+msgid "Leave this channel"
+msgstr "Poistu tältä kanavalta"
+
+#: lib/Form/EditChannel.php:73 channels/aggregate.php:58
+msgid "Link"
+msgstr "Linkki"
+
+#: stories/index.php:109
+msgid "Lists"
+msgstr "Listat"
+
+#: lib/Block/news.php:57
+msgid "Maximum Stories"
+msgstr "Maksimimäärä artikkeleita"
+
+#: lib/Block/tree_menu.php:3
+msgid "Menu List"
+msgstr "Valikkolista"
+
+#: stories/share.php:105
+msgid "Message"
+msgstr "Viesti"
+
+#: lists/index.php:123
+msgid "Method"
+msgstr "Metodi"
+
+#: lib/News.php:78
+msgid "Missing channel id."
+msgstr "Kanava id puuttuu."
+
+#: lib/Jonah.php:249
+msgid "My Content"
+msgstr "Oma sisältö"
+
+#: lib/Block/delivery_email.php:93 lib/Delivery/email.php:212
+#: lists/index.php:123 delivery/email.php:81 delivery/index.php:53
+#: channels/index.php:92
+msgid "Name"
+msgstr "Nimi"
+
+#: lib/Block/news.php:85 stories/index.php:104
+msgid "New Story"
+msgstr "Uusi artikkeli"
+
+#: channels/index.php:88
+msgid "New channel"
+msgstr "Uusi kanava"
+
+#: lists/index.php:104
+msgid "New recipient"
+msgstr "Uusi vastaanottaja"
+
+#: lib/api.php:46 config/prefs.php.dist:10
+msgid "News"
+msgstr "Uutiset"
+
+#: lib/Jonah.php:259 channels/index.php:97
+msgid "News Admin"
+msgstr "Uutisten ylläpitäjä"
+
+#: lib/Block/delivery_email.php:40
+msgid "News Channel"
+msgstr "Uutiskanava"
+
+#: lib/Block/news.php:38
+msgid "News Source"
+msgstr "Uutislähde"
+
+#: lib/Block/delivery_email.php:3
+msgid "News by Email"
+msgstr "Uutiset sähköpostitse"
+
+#: lib/Block/delivery.php:3
+msgid "News delivery"
+msgstr "Uutisjakelu"
+
+#: lists/index.php:37 channels/index.php:23
+msgid "News is not enabled."
+msgstr "Uutiset eivät ole päällä."
+
+#: delivery/index.php:49 channels/index.php:82
+msgid "No Data"
+msgstr "Ei tietoa"
+
+#: channels/index.php:36
+msgid "No available channels."
+msgstr "Ei kanavia saatavilla."
+
+#: stories/index.php:48
+msgid "No available stories."
+msgstr "Ei artikkeleita saatavilla."
+
+#: stories/index.php:21 lists/index.php:23
+msgid "No channel requested."
+msgstr "Ei pyydettyä kanavaa."
+
+#: lib/Block/news.php:94
+msgid "No channel specified."
+msgstr "Ei määriteltyä kanavaa."
+
+#: lib/Block/delivery_email.php:70
+msgid "No channels available."
+msgstr "Ainuttakaan kanavaa ei ole saatavilla."
+
+#: lists/index.php:77
+msgid "No recipients."
+msgstr "Ei vastaanottajia."
+
+#: lib/News.php:516
+msgid "No stories are currently available."
+msgstr "Artikkeleita ei ole saatavilla."
+
+#: lib/Delivery.php:223
+#, php-format
+msgid "No such action '%s' found"
+msgstr "Toimintoa '%s' ei löytynyt"
+
+#: lib/News.php:636
+#, php-format
+msgid "No such backend '%s' found"
+msgstr "Taustajärjestelmää '%s' ei löytynyt"
+
+#: stories/delete.php:35
+msgid "No valid story requested for deletion."
+msgstr "Poistettavaa artikkelia ei ole olemassa."
+
+#: stories/share.php:26
+msgid "Note"
+msgstr "Muistiinpano"
+
+#: stories/edit.php:67
+msgid ""
+"Note: this story won't get delivered to distribution lists automatically if "
+"this time is in the future."
+msgstr ""
+"Huomaa: Tätä artikkelia ei jaeta jakelulistalle automaattisesti, jos tämä "
+"aika on tulevaisuudessa."
+
+#: config/prefs.php.dist:16
+msgid "Other Options"
+msgstr "Muut asetukset"
+
+#: channels/delete.php:20
+msgid "Permission Denied."
+msgstr "Käyttö kielletty."
+
+#: lib/News.php:579
+msgid "Plaintext Version of Story"
+msgstr "Artikkeli tekstimuodossa."
+
+#: templates/stories/index.html:32
+msgid "Read"
+msgstr "Lue"
+
+#: channels/delete.php:52
+msgid "Really delete this News Channel?"
+msgstr "Poistetaanko uutiskanava?"
+
+#: stories/delete.php:53
+msgid "Really delete this News Story?"
+msgstr "Poistetaanko uutisartikkeli?"
+
+#: lists/index.php:123
+msgid "Recipient"
+msgstr "Vastaanottaja"
+
+#: lists/edit.php:85
+msgid "Recipient saved."
+msgstr "Vastaanottaja talletettu."
+
+#: lists/delete.php:52
+msgid "Recipient successfully removed."
+msgstr "Vastaanottajan poistaminen onnistui."
+
+#: stories/share.php:102
+msgid "Recipients"
+msgstr "Vastaanottajat"
+
+#: channels/index.php:76
+msgid "Refresh channel"
+msgstr "Päivitä kanava"
+
+#: stories/edit.php:67
+msgid "Release Date"
+msgstr "Julkaisupäivämäärä"
+
+#: stories/edit.php:66
+msgid "Release?"
+msgstr "Julkaise?"
+
+#: stories/index.php:115
+msgid "Released"
+msgstr "Julkaistu"
+
+#: lists/delete.php:54
+msgid "Removal of recipient failed."
+msgstr "Vastaanottajan poistaminen epäonnistui."
+
+#: channels/aggregate.php:24
+#, php-format
+msgid "Remove channel \"%s\""
+msgstr "Poista kanava \"%s\""
+
+#: delivery/email.php:41
+msgid "Request confirmed."
+msgstr "Pyyntö varmistettu."
+
+#: lib/Delivery/email.php:71
+#, php-format
+msgid "Request to receive news channel \"%s\""
+msgstr "Pyyntö vastaanottaa uutiskanavaa \"%s\""
+
+#: lib/Delivery/email.php:76
+#, php-format
+msgid "Request to stop receiving news channel \"%s\""
+msgstr "Pyyntö lopettaa uutiskanava \"%s\" vastaanotto"
+
+#: lib/Jonah.php:202
+msgid "Rich Text"
+msgstr "Rich Text"
+
+#: lib/Block/delivery_email.php:79 stories/edit.php:61 lists/edit.php:51
+#: delivery/email.php:64
+msgid "Save"
+msgstr "Talleta"
+
+#: templates/subscribe/edit.inc:19
+msgid "Save Changes"
+msgstr "Talleta muutokset"
+
+#: templates/delivery/html.html:14
+msgid "Select a format:"
+msgstr "Valitse muoto:"
+
+#: stories/share.php:98
+msgid "Send"
+msgstr "Lähetä"
+
+#: stories/share.php:102
+msgid "Separate multiple email addresses with commas."
+msgstr "Erota sähköpostiosoitteet toisistaan pilkuilla."
+
+#: stories/share.php:96
+msgid "Share Story"
+msgstr "Jaa artikkeli"
+
+#: stories/view.php:43
+msgid "Share this story"
+msgstr "Jaa tämä artikkeli"
+
+#: stories/edit.php:65
+msgid "Short Description"
+msgstr "Lyhyt kuvaus"
+
+#: lib/Form/EditChannel.php:72 channels/aggregate.php:57
+msgid "Source URL"
+msgstr "Lähde URL"
+
+#: lib/Form/EditChannel.php:87
+msgid "Source URLs"
+msgstr "Lähde URL:t"
+
+#: config/templates.php.dist:26
+msgid "Standard"
+msgstr "Standardi"
+
+#: stories/index.php:115
+msgid "Story"
+msgstr "Artikkeli"
+
+#: stories/share.php:127
+msgid "Story Link"
+msgstr "Artikkelilinkki"
+
+#: stories/edit.php:64
+msgid "Story Title (Headline)"
+msgstr "Artikkelin nimi (Otsikko)"
+
+#: stories/edit.php:96
+msgid "Story URL"
+msgstr "Artikkelin URL"
+
+#: lib/News/sql.php:343
+#, php-format
+msgid "Story URL '%s' not found."
+msgstr "Artikkeli URL '%s' ei löytynyt."
+
+#: lib/Form/EditChannel.php:65 lib/Form/EditChannel.php:99
+#, php-format
+msgid ""
+"Story URL if not the default one. %c gets replaced by the channel ID, %s by "
+"the story ID."
+msgstr ""
+"Artikkelin URL, jos ei ole sama kuin oletus. %c korvautuu kanavan ID:llä, %s "
+"artikkelin ID:llä."
+
+#: stories/edit.php:69
+msgid "Story body type"
+msgstr "Artikkelin body-tyyppi"
+
+#: stories/edit.php:31
+#, php-format
+msgid "Story editing failed: %s"
+msgstr "Artikkelin muokkaus epäonnistui: %s"
+
+#: stories/delete.php:72
+msgid "Story has not been deleted."
+msgstr "Artikkeli on poistettu."
+
+#: lib/News/sql.php:319
+#, php-format
+msgid "Story id '%s' not found."
+msgstr "Artikkelia id '%s' ei löytynyt."
+
+#: stories/share.php:103
+msgid "Subject"
+msgstr "Otsikko"
+
+#: subscribe.php:122 templates/subscribe/subscribe.inc:40
+msgid "Subscribe"
+msgstr "Tilaa"
+
+#: content.php:53
+#, php-format
+msgid ""
+"Subscribed channel '%s' is no longer available, it will be removed from your "
+"subscriptions."
+msgstr ""
+"Tilattua kanavaa '%s' ei ole enää saatavilla, kanavan tilaustiedot "
+"tilaustiedoistasi."
+
+#: templates/subscribe/column_headers.inc:11
+msgid "Subscription"
+msgstr "Tilaus"
+
+#: lib/News.php:283
+#, php-format
+msgid "Successfully delivered to the %s distribution list."
+msgstr "Lähetettiin onnistuneesta %s jakelulistalle."
+
+#: lib/Jonah.php:210
+msgid "Text"
+msgstr "Teksti"
+
+#: channels/aggregate.php:93
+#, php-format
+msgid "The channel \"%s\" has been removed."
+msgstr "Kanava \"%s\" on poistettu."
+
+#: channels/aggregate.php:74
+#, php-format
+msgid "The channel \"%s\" has been saved."
+msgstr "Kanava \"%s\" on talletettu."
+
+#: channels/aggregate.php:82 channels/aggregate.php:100
+#, php-format
+msgid "The channel \"%s\" has been updated."
+msgstr "Kanava \"%s\" on päivitetty."
+
+#: channels/edit.php:72
+#, php-format
+msgid "The channel '%s' has been saved."
+msgstr "Kanava '%s' on talletettu."
+
+#: channels/delete.php:69
+msgid "The channel has been deleted."
+msgstr "Kanava on poistettu."
+
+#: stories/share.php:104
+msgid "The complete text of the story"
+msgstr "Artikkelin teksti kokonaisuudessaan"
+
+#: lib/Form/EditChannel.php:81
+msgid ""
+"The interval before stories aggregated into this channel are rechecked for "
+"updates. If none, then stories will always be refetched from the sources."
+msgstr ""
+"Aikaväli, jonka jälkeen tarkistaan onko tälle kanavalle liitetyissä "
+"artikkeleissa tapahtunut päivityksiä. Jos ei ole, niin artikkelit haetaan "
+"joka kerta uudestaan uutislähteistä."
+
+#: lib/Form/EditChannel.php:70
+msgid ""
+"The interval before stories in this channel are rechecked for updates. If "
+"none, then stories will always be refetched from the source."
+msgstr ""
+"Kuinka usein tarkistetaan onko kanavan artikkeleita päivitetty. Jos valinta "
+"on ei kertaakaan, niin artikkelit haetaan joka kerta uutislähteestä."
+
+#: stories/edit.php:105
+#, php-format
+msgid "The story \"%s\" has been saved."
+msgstr "Artikkeli \"%s\" on talletettu."
+
+#: stories/delete.php:64
+msgid "The story has been deleted."
+msgstr "Artikkeli on poistettu."
+
+#: stories/share.php:138
+msgid "The story was sent successfully."
+msgstr "Artikkelin lähetys onnistui."
+
+#: lib/Form/EditChannel.php:72 channels/aggregate.php:57
+msgid ""
+"The url to use to fetch the stories, for example 'http://www.example.com/"
+"stories.rss'"
+msgstr ""
+"URL, josta haetaan artikkeleita, esimerkiksi: 'http://www.example.com/"
+"stories.rss'"
+
+#: channels/delete.php:67
+#, php-format
+msgid "There was an error deleting the channel: %s"
+msgstr "Tapahtui virhe poistettaessa kanavaa: %s"
+
+#: stories/delete.php:62
+#, php-format
+msgid "There was an error deleting the story: %s"
+msgstr "Tapahtui virhe poistettaessa artikkelia: %s"
+
+#: channels/aggregate.php:91
+#, php-format
+msgid "There was an error removing the channel: %s"
+msgstr "Tapahtui virhe poistettaessa kanavaa: %s"
+
+#: channels/edit.php:70
+#, php-format
+msgid "There was an error saving the channel. %s"
+msgstr "Tapahtui virhe talletettaessa kanavaa. %s"
+
+#: channels/aggregate.php:72
+#, php-format
+msgid "There was an error saving the channel: %s"
+msgstr "Tapahtui virhe talletettaessa kanavaa. %s"
+
+#: stories/edit.php:103
+#, php-format
+msgid "There was an error saving the story: %s"
+msgstr "Tapahtui virhe talletettaessa artikkelia: %s"
+
+#: channels/aggregate.php:80 channels/aggregate.php:98
+#, php-format
+msgid "There was an error updating the channel: %s"
+msgstr "Tapahtui virhe päivitettäessä kanavaa: %s"
+
+#: lib/Delivery/email.php:196
+msgid ""
+"This driver allows the delivery of news stories via email to one or more "
+"recipients."
+msgstr ""
+"Tämä ajuri mahdillistaa uutisartikkeleiden jakamisen sähköpostitse yhdelle "
+"tai useammalle vastaanottajalle."
+
+#: channels/aggregate.php:41
+msgid "This is no aggregated channel."
+msgstr "Tämä ei ole yhdistelty kanava."
+
+#: lib/Form/EditChannel.php:37 channels/index.php:92
+msgid "Type"
+msgstr "Tyyppi"
+
+#: config/templates.php.dist:54
+msgid "Ultracompact"
+msgstr "Ultrapieni"
+
+#: delivery/email.php:39
+#, php-format
+msgid "Unable to confirm request."
+msgstr "Pyyntöä ei pystytty varmistamaan."
+
+#: stories/share.php:136
+#, php-format
+msgid "Unable to send story: %s"
+msgstr "Artikkelia ei voitu lähettää: %s"
+
+#: templates/subscribe/edit.inc:20
+msgid "Undo Changes"
+msgstr "Peru muutokset"
+
+#: subscribe.php:118 templates/subscribe/subscribe.inc:36
+msgid "Unsubscribe"
+msgstr "Lopeta tilaus"
+
+#: channels/aggregate.php:105
+msgid "Update"
+msgstr "Päivitä"
+
+#: stories/index.php:115
+msgid "Updated"
+msgstr "Päivitetty"
+
+#: templates/subscribe/edit.inc:9 lib/Block/news.php:49
+msgid "View"
+msgstr "Näytä"
+
+#: delivery/email.php:65
+msgid "What do you want to do?"
+msgstr "Mitä haluat tehdä?"
+
+#: stories/delete.php:20 stories/edit.php:39 stories/index.php:29
+#: lists/edit.php:41 lists/index.php:31 lists/delete.php:45
+#: channels/edit.php:43 channels/index.php:17 channels/aggregate.php:48
+msgid "You are not authorised for this action."
+msgstr "Sinulla ei ole oikeuksia suorittaa tätä toimenpidettä."
+
+#: channels/edit.php:74
+msgid "You can now edit the sub-channels."
+msgstr "Voit nyt muokata alikanavia."
+
+#: subscribe.php:84
+#, php-format
+msgid "You have edited your subscription to the channel '%s'."
+msgstr "Olet muokannut tilaustietojasi kanavalle '%s'."
+
+#: templates/content/content.html:28
+msgid "You have no news subscriptions."
+msgstr "Sinulla ei ole uutistilauksia."
+
+#: lib/Delivery/email.php:74
+#, php-format
+msgid ""
+"You have requested to receive \"%s\" stories by email. Click on this link to "
+"confirm the request: %s"
+msgstr ""
+"Olet pyytänyt saada \"%s\" artikkeli sähköpostitse. Napsauta tätä linkkiä "
+"vahvistaaksesi: %s"
+
+#: lib/Delivery/email.php:77
+#, php-format
+msgid ""
+"You have requested to stop receiving \"%s\" stories by email. Click on this "
+"link to confirm the request: %s"
+msgstr ""
+"Olet pyytänyt lopettamaan \"%s\" artikkeleiden saapumista sähköpostitse "
+"Napsauta tätä linkkiä vahvistaaksesi: %s"
+
+#: subscribe.php:39
+#, php-format
+msgid "You have subscribed to the channel '%s'."
+msgstr "Olet tilannut kanavan '%s'."
+
+#: subscribe.php:62
+#, php-format
+msgid "You have unsubscribed from the channel '%s'."
+msgstr "Olet lopettanut tilauksen kanavaan '%s'."
+
+#: lib/Delivery.php:282
+msgid "You must configure a Horde Datatree backend to use Jonah."
+msgstr ""
+"Sinun pitää asentaa Horde Datatree taustajärjestelmä ennen kuin voit käyttää "
+"Jonah:ia."
+
+#: config/prefs.php.dist:9
+msgid "Your Content"
+msgstr "Oma sisältö"
+
+#: lib/News.php:403
+msgid "[No title]"
+msgstr "[Ei otsikkoa]"
+
+#: lib/News.php:415
+msgid "none"
+msgstr "ei mitään"
--- /dev/null
+# Jonah 3.1 French Translation.
+# Copyright 2002-2009 The Horde Project.
+# Eric Rostetter <eric.rostetter@physics.utexas.edu>, 2002.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Jonah 0.0.3-cvs\n"
+"POT-Creation-Date: 2002-09-09 23:48-0500\n"
+"PO-Revision-Date: 2002-09-09 23:48-0500\n"
+"Last-Translator: Eric Rostetter <eric.rostetter@physics.utexas.edu>\n"
+"Language-Team: French <i18n@lists.horde.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#, c-format
+msgid "%d Headlines Available"
+msgstr ""
+
+msgid "Admin Home"
+msgstr ""
+
+msgid "Ascending"
+msgstr "Croissant"
+
+msgid "Change the location for which weather is obtained."
+msgstr "Changez l'endroit pour lequel le temps est obtenu."
+
+msgid "Change your content display options."
+msgstr "Changer les préférences de son contenu."
+
+msgid "Change"
+msgstr "Modifier"
+
+msgid "Channel Listing"
+msgstr "Liste de canal"
+
+msgid "Channel Name"
+msgstr "Nom de canal"
+
+msgid "Channel Subscriptions"
+msgstr "Abonnements de canal"
+
+msgid "Channel display failed."
+msgstr "L'affichage de canal a échoué"
+
+msgid "Channel subscription editing failed."
+msgstr "L'édition de l'abonnement de canal a échoué."
+
+msgid "Channel subscription failed."
+msgstr "L'abonnement de canal a échoué"
+
+msgid "Channel unsubscription failed."
+msgstr "L'unsubscription de canal a échoué"
+
+msgid "Choose which news channels you are subscribed to."
+msgstr "Choisissez au lequel on souscrit des canaux vous."
+
+msgid "Default sorting direction:"
+msgstr "Ordre de tru par défaut"
+
+msgid "Descending"
+msgstr "Décroissant"
+
+msgid "Detail"
+msgstr "Détail"
+
+msgid "Display Options"
+msgstr "Options d'affichage"
+
+msgid "Edit Channel Subscriptions"
+msgstr "Éditez vos abonnements de canal"
+
+msgid "Edit Subscription Details"
+msgstr "Éditez vos détails d'abonnement de canal"
+
+msgid "Edit your news subscriptions"
+msgstr "Éditez vos abonnements de nouvelles"
+
+msgid "Edit"
+msgstr "Éditer"
+
+#, c-format
+msgid "Editing Channel %s"
+msgstr "Éditez le canal %s"
+
+msgid "Enter NASDAQ/AMEX/NYSE ticker symbols to watch, separated with commas:"
+msgstr ""
+"Écrivez les symboles de ticker de Nasdaq/AMEX/NYSE pour observer, séparé "
+"avec des virgules:"
+
+msgid "Headlines Update"
+msgstr "Mettez à jour les canaux"
+
+msgid "Help"
+msgstr "Aide"
+
+msgid "How many columns would you like to use to show headlines?"
+msgstr "Combien de colonnes aimez-vous employer pour montrer des canaux?"
+
+msgid "Jonah Backend"
+msgstr "Jonah Interface"
+
+msgid "Jonah is not properly configured"
+msgstr "Jonah n'est pas configuré correctement"
+
+msgid "Just Fetch latest RSS files"
+msgstr "Recherchez seulement les derniers fichiers de RSS."
+
+msgid "Just Generate HTML from RSS files"
+msgstr "Produisez seulement du HTML aux fichiers de RSS."
+
+msgid "Language"
+msgstr "Langue"
+
+msgid "Last Updated"
+msgstr "Mis à jour"
+
+msgid "Location for which to retrieve weather information."
+msgstr "L'endroit pour que lequel recherche l'information de temps."
+
+msgid "My Content"
+msgstr "Mon contenu"
+
+msgid "News"
+msgstr "Nouvelles"
+
+msgid "Options"
+msgstr "Options"
+
+msgid "Other Options"
+msgstr "Autres Options"
+
+msgid "Problem?"
+msgstr "Problème?"
+
+msgid "Radar"
+msgstr "Radar"
+
+msgid "Save Changes"
+msgstr "Enregistrer modifications"
+
+msgid "Select your preferred language:"
+msgstr "Définir votre langue préférée:"
+
+msgid "Set the your preferred display language."
+msgstr "Affecter la langue utilisée pour l'affichage."
+
+msgid "Set which stock ticker symbols to watch."
+msgstr "Indiquez quels symboles de ticker courant à observer."
+
+msgid "Show Dow Jones Industrial Average (INDU)?"
+msgstr "Montrez Dow Jones Industrial Average (INDU)?"
+
+msgid "Show S&P 500 (SPX)?"
+msgstr "Montrez S&P 500 (SPX)?"
+
+msgid "Show headlines in Horde summary?"
+msgstr "Montrez les canaux dans le sommaire de Horde?"
+
+msgid "Show headlines in channels display?"
+msgstr "Montrez les citations courantes dans l'affichage de canal?"
+
+msgid "Show stock quotes in Horde summary?"
+msgstr "Montrez les citations courantes dans le sommaire de Horde?"
+
+msgid "Show stock quotes in channels display?"
+msgstr "Montrez les citations courantes dans l'affichage de canal?"
+
+msgid "Show weather and stock quotes on same row in two columns?"
+msgstr ""
+"Montrez les citations de temps et des marché des actions sur la même ligne "
+"dans deux colonnes?"
+
+msgid "Show weather in Horde summary?"
+msgstr "Montrez le temps dans le sommaire de Horde?"
+
+msgid "Show weather in channels display?"
+msgstr "Montrez le temps dans l'affichage de canal?"
+
+msgid "Some of Jonah's configuration files are missing:"
+msgstr "Certains fichiers de configuration sont absents:"
+
+msgid "Sort Direction"
+msgstr "Sens du tri"
+
+msgid "Sort by Channel Name"
+msgstr "Sorte par le nom de canal"
+
+msgid "Sort by Subscription Status"
+msgstr "Sorte par statut d'abonnement"
+
+msgid "Sort by Update Date"
+msgstr "Sorte par la date de mise à jour"
+
+msgid "Stock Quote"
+msgstr ""
+
+msgid "Stocks"
+msgstr ""
+
+msgid "Subscribe"
+msgstr "Souscrivez"
+
+msgid "This file contains preferences for Jonah."
+msgstr "Ce fichier contrôle les préférences pour Jonah"
+
+msgid ""
+"This file defines all of the headline channels that you wish Jonah to "
+"display."
+msgstr ""
+"Ce fichier définit tous les canaux que vous souhaitez Jonah pour montrer."
+
+msgid ""
+"This is the main Jonah configuration file. It contains options for all Jonah "
+"scripts."
+msgstr ""
+"Ceci est le principal fichier de configuration. Il contient toutes lesles "
+"options des scripts."
+
+msgid "Undo Changes"
+msgstr "Défaites les changements."
+
+msgid "Unsubscribe"
+msgstr ""
+
+msgid "Update All Channels"
+msgstr "Mettez à jour tous les canaux."
+
+msgid "Update"
+msgstr "Mettez à jour"
+
+msgid "User Options"
+msgstr "Options personnelles"
+
+msgid "Weather Station"
+msgstr ""
+
+msgid "Weather station:"
+msgstr "Station météorologique."
+
+msgid "You have edited your subscription to the channel "
+msgstr "Vous avez édité votre abonnement au canal."
+
+msgid "You have subscribed to the channel "
+msgstr "Vous avez souscrit au canal."
+
+msgid "You have unsubscribed to the channel "
+msgstr ""
+
+msgid "Your Content"
+msgstr "Votre contenu"
+
+msgid "Your Information"
+msgstr "Vos données personnelles"
+
+msgid "no headlines available at this time"
+msgstr "aucuns canaux disponibles actuellement"
--- /dev/null
+# SOME DESCRIPTIVE TITLE.
+# Copyright YEAR Horde Project
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: dev@lists.horde.org\n"
+"POT-Creation-Date: 2008-08-01 10:44+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: lib/Block/news_popular.php:75 lib/Block/news_popular.php:78
+msgid " - Most read stories"
+msgstr ""
+
+#: lib/Block/delivery.php:48
+#, php-format
+msgid "\"%s\" stories in HTML"
+msgstr ""
+
+#: lib/News.php:461
+msgid "1 hour"
+msgstr ""
+
+#: lib/News.php:465
+msgid "12 hours"
+msgstr ""
+
+#: lib/News.php:462
+msgid "2 hours"
+msgstr ""
+
+#: lib/News.php:466
+msgid "24 hours"
+msgstr ""
+
+#: lib/News.php:460
+msgid "30 mins"
+msgstr ""
+
+#: lib/News.php:463
+msgid "4 hours"
+msgstr ""
+
+#: lib/News.php:464
+msgid "8 hours"
+msgstr ""
+
+#: stories/share.php:86
+msgid "A link to the story"
+msgstr ""
+
+#: channels/aggregate.php:59
+msgid "Add"
+msgstr ""
+
+#: content.php:37
+msgid "Add Content"
+msgstr ""
+
+#: lib/Forms/Story.php:37
+msgid "Add New Story"
+msgstr ""
+
+#: channels/index.php:65
+msgid "Add story"
+msgstr ""
+
+#: lib/api.php:118
+msgid "Administrator"
+msgstr ""
+
+#: lib/News.php:499 lib/Jonah.php:113
+msgid "Aggregated Feed"
+msgstr ""
+
+#: channels/aggregate.php:58
+#, php-format
+msgid "Aggregated channels for channel \"%s\""
+msgstr ""
+
+#: channels/delete.php:56
+msgid "All stories created in this channel will be lost!"
+msgstr ""
+
+#: stories/results.php:114
+#, php-format
+msgid "All stories tagged with %s"
+msgstr ""
+
+#: channels/index.php:35
+#, php-format
+msgid "An error occurred fetching channels: %s"
+msgstr ""
+
+#: channels/delete.php:58
+msgid "Any cached stories for this channel will be lost!"
+msgstr ""
+
+#: lib/Forms/Feed.php:83 lib/Forms/Feed.php:94
+msgid "Caching"
+msgstr ""
+
+#: stories/pdf.php:44
+msgid "Cannot generate PDFs of remote stories."
+msgstr ""
+
+#: lib/News/sql.php:510
+#, php-format
+msgid "Channel \"%s\" not found."
+msgstr ""
+
+#: channels/aggregate.php:61
+msgid "Channel Name"
+msgstr ""
+
+#: lib/Forms/Feed.php:77
+msgid ""
+"Channel URL for further pages, if not the default one. %c gets replaced by "
+"the feed ID, %n by the story offset."
+msgstr ""
+
+#: lib/Forms/Feed.php:112
+msgid ""
+"Channel URL if not the default one. %c gets replaced by the feed ID, %n by "
+"the story offset."
+msgstr ""
+
+#: lib/Forms/Feed.php:76
+#, php-format
+msgid "Channel URL if not the default one. %c gets replaced by the feed ID."
+msgstr ""
+
+#: channels/delete.php:76
+msgid "Channel has not been deleted."
+msgstr ""
+
+#: lib/News/sql.php:174
+#, php-format
+msgid "Channel id \"%s\" not found."
+msgstr ""
+
+#: stories/index.php:54
+msgid "Channel refreshed."
+msgstr ""
+
+#: templates/channels/index.html:13
+msgid "Close Search"
+msgstr ""
+
+#: templates/stories/index.html:27
+msgid "Comments"
+msgstr ""
+
+#: config/templates.php.dist:42
+msgid "Compact"
+msgstr ""
+
+#: lib/News.php:502 lib/Jonah.php:116
+msgid "Composite Feed"
+msgstr ""
+
+#: lib/Forms/Feed.php:114
+msgid "Composite feeds"
+msgstr ""
+
+#: lib/Jonah.php:62
+#, php-format
+msgid "Could not open %s."
+msgstr ""
+
+#: lib/Block/latest.php:49
+msgid "Count reads of the latest story when this block is displayed"
+msgstr ""
+
+#: lib/Block/story.php:49
+msgid "Count reads of this story when this block is displayed"
+msgstr ""
+
+#: stories/index.php:124 stories/results.php:116
+msgid "Date"
+msgstr ""
+
+#: stories/delete.php:61 stories/delete.php:66 channels/delete.php:51
+#: channels/delete.php:62
+msgid "Delete"
+msgstr ""
+
+#: channels/delete.php:48
+#, php-format
+msgid "Delete News Channel \"%s\"?"
+msgstr ""
+
+#: stories/delete.php:57
+#, php-format
+msgid "Delete News Story \"%s\"?"
+msgstr ""
+
+#: channels/index.php:49
+msgid "Delete channel"
+msgstr ""
+
+#: stories/index.php:100 stories/results.php:94
+msgid "Delete story"
+msgstr ""
+
+#: lib/Forms/Feed.php:75 lib/Forms/Feed.php:93 lib/Forms/Feed.php:111
+msgid "Description"
+msgstr ""
+
+#: stories/delete.php:61 channels/delete.php:51
+msgid "Do not delete"
+msgstr ""
+
+#: lib/Forms/Feed.php:40
+msgid "Edit Feed"
+msgstr ""
+
+#: lib/Forms/Story.php:37
+msgid "Edit Story"
+msgstr ""
+
+#: lib/Forms/Feed.php:100
+msgid "Edit aggregated feeds"
+msgstr ""
+
+#: channels/index.php:44
+msgid "Edit channel"
+msgstr ""
+
+#: channels/aggregate.php:25
+#, php-format
+msgid "Edit channel \"%s\""
+msgstr ""
+
+#: stories/index.php:94 stories/results.php:87
+msgid "Edit story"
+msgstr ""
+
+#: lib/News.php:541
+#, php-format
+msgid "Error fetching feed: %s"
+msgstr ""
+
+#: stories/share.php:66 stories/pdf.php:15 stories/view.php:25
+#: stories/view.php:35 lib/Block/latest.php:91 lib/Block/story.php:91
+#, php-format
+msgid "Error fetching story: %s"
+msgstr ""
+
+#: lib/News.php:780
+#, php-format
+msgid "Error parsing external feed from %s: %s"
+msgstr ""
+
+#: lib/api.php:109
+msgid "External Channels"
+msgstr ""
+
+#: lib/News.php:493 lib/Jonah.php:110
+msgid "External Feed"
+msgstr ""
+
+#: lib/Forms/Feed.php:51
+msgid "Extra information for this feed type"
+msgstr ""
+
+#: lib/Block/news_popular.php:32 lib/Block/news.php:3 lib/Block/news.php:30
+#: lib/Block/story.php:42
+msgid "Feed"
+msgstr ""
+
+#: lib/News.php:105
+#, php-format
+msgid "Feed \"%s\" is not authored on this system."
+msgstr ""
+
+#: channels/edit.php:56
+msgid "Feed type changed."
+msgstr ""
+
+#: channels/index.php:93 lib/Block/delivery.php:3 lib/Block/delivery.php:25
+msgid "Feeds"
+msgstr ""
+
+#: lib/Block/news.php:54
+msgid "First Story"
+msgstr ""
+
+#: stories/share.php:78
+msgid "From"
+msgstr ""
+
+#: lib/Forms/Story.php:74 lib/Forms/Story.php:76
+msgid "Full Story Text"
+msgstr ""
+
+#: delivery/html.php:39
+#, php-format
+msgid "HTML Delivery for \"%s\""
+msgstr ""
+
+#: lib/News.php:680
+msgid "HTML Version of Story"
+msgstr ""
+
+#: lib/Forms/Story.php:82
+msgid ""
+"If you enter a URL without a full story text, clicking on the story will "
+"send the reader straight to the URL, otherwise it will be shown at the end "
+"of the full story."
+msgstr ""
+
+#: channels/aggregate.php:64 lib/Forms/Feed.php:87
+msgid "Image"
+msgstr ""
+
+#: stories/share.php:86
+msgid "Include"
+msgstr ""
+
+#: config/templates.php.dist:31
+msgid "Internal"
+msgstr ""
+
+#: lib/api.php:91
+msgid "Internal Channels"
+msgstr ""
+
+#: stories/index.php:43 stories/results.php:49
+#, php-format
+msgid "Invalid channel requested. %s"
+msgstr ""
+
+#: channels/delete.php:30
+msgid "Invalid channel specified for deletion."
+msgstr ""
+
+#: delivery/html.php:33
+msgid "Invalid channel."
+msgstr ""
+
+#: channels/index.php:87
+msgid "Last Update"
+msgstr ""
+
+#: lib/Block/latest.php:3 lib/Block/latest.php:65
+msgid "Latest News"
+msgstr ""
+
+#: channels/aggregate.php:63 lib/Forms/Feed.php:86
+msgid "Link"
+msgstr ""
+
+#: lib/News.php:496 lib/Jonah.php:107
+msgid "Local Feed"
+msgstr ""
+
+#: channels/index.php:84
+msgid "Manage Feeds"
+msgstr ""
+
+#: lib/Block/news_popular.php:52 lib/Block/news.php:49
+msgid "Maximum Stories"
+msgstr ""
+
+#: config/templates.php.dist:20
+msgid "Media"
+msgstr ""
+
+#: lib/Block/tree_menu.php:3
+msgid "Menu List"
+msgstr ""
+
+#: stories/share.php:87
+msgid "Message"
+msgstr ""
+
+#: lib/News.php:66
+msgid "Missing channel id."
+msgstr ""
+
+#: lib/Block/news_popular.php:3
+msgid "Most Popular Stories"
+msgstr ""
+
+#: content.php:34 templates/content/header.inc:2
+msgid "My News"
+msgstr ""
+
+#: content_edit.php:33
+msgid "My News :: Add Content"
+msgstr ""
+
+#: channels/index.php:85 lib/Forms/Feed.php:50
+msgid "Name"
+msgstr ""
+
+#: lib/Jonah.php:245 lib/Forms/Feed.php:40
+msgid "New Feed"
+msgstr ""
+
+#: lib/api.php:90 lib/api.php:108
+msgid "News"
+msgstr ""
+
+#: lib/Block/latest.php:33
+msgid "News Source"
+msgstr ""
+
+#: channels/index.php:25
+msgid "News is not enabled."
+msgstr ""
+
+#: stories/index.php:51 stories/results.php:57
+msgid "No available stories."
+msgstr ""
+
+#: stories/index.php:23
+msgid "No channel requested."
+msgstr ""
+
+#: lib/Block/latest.php:86
+msgid "No channel specified."
+msgstr ""
+
+#: templates/channels/index.html:55
+msgid "No channels are available."
+msgstr ""
+
+#: lib/Block/news_popular.php:91 lib/Block/news.php:92
+msgid "No feed specified."
+msgstr ""
+
+#: lib/Block/delivery.php:68
+msgid "No feeds are available."
+msgstr ""
+
+#: templates/channels/index.html:51
+msgid "No feeds match"
+msgstr ""
+
+#: lib/News.php:571
+msgid "No stories are currently available."
+msgstr ""
+
+#: lib/Block/story.php:86
+msgid "No story is selected."
+msgstr ""
+
+#: lib/News.php:730
+#, php-format
+msgid "No such backend \"%s\" found"
+msgstr ""
+
+#: stories/results.php:40
+msgid "No tag requested."
+msgstr ""
+
+#: stories/delete.php:46
+msgid "No valid story requested for deletion."
+msgstr ""
+
+#: stories/share.php:25
+msgid "Note"
+msgstr ""
+
+#: lib/Forms/Story.php:58
+msgid ""
+"Note: this story won't be delivered to distribution lists automatically if "
+"this time is in the future."
+msgstr ""
+
+#: lib/Forms/Story.php:55
+msgid "Or publish on this date:"
+msgstr ""
+
+#: stories/index.php:89 stories/results.php:81
+msgid "PDF version"
+msgstr ""
+
+#: scripts/feed_tester.php:57
+msgid "Parse failed:"
+msgstr ""
+
+#: scripts/feed_tester.php:60
+msgid "Parse succeeded, structure is:"
+msgstr ""
+
+#: lib/News.php:675
+msgid "Plaintext Version of Story"
+msgstr ""
+
+#: lib/Forms/Story.php:45
+msgid "Publish Now?"
+msgstr ""
+
+#: lib/Block/delivery.php:59
+#, php-format
+msgid "RSS Feed of \"%s\""
+msgstr ""
+
+#: templates/stories/index.html:22
+msgid "Read"
+msgstr ""
+
+#: channels/delete.php:54
+msgid "Really delete this News Channel?"
+msgstr ""
+
+#: stories/delete.php:64
+msgid "Really delete this News Story?"
+msgstr ""
+
+#: stories/index.php:123
+msgid "Refresh Channel"
+msgstr ""
+
+#: channels/index.php:73
+msgid "Refresh channel"
+msgstr ""
+
+#: channels/aggregate.php:26
+#, php-format
+msgid "Remove channel \"%s\""
+msgstr ""
+
+#: lib/Block/cloud.php:28
+msgid "Results URL"
+msgstr ""
+
+#: lib/Jonah.php:193
+msgid "Rich Text"
+msgstr ""
+
+#: lib/Forms/Story.php:39
+msgid "Save"
+msgstr ""
+
+#: channels/index.php:90 templates/channels/index.html:9
+msgid "Search"
+msgstr ""
+
+#: templates/delivery/html.html:13
+msgid "Select a format:"
+msgstr ""
+
+#: stories/share.php:75
+msgid "Send"
+msgstr ""
+
+#: stories/share.php:84
+msgid "Separate multiple email addresses with commas."
+msgstr ""
+
+#: stories/share.php:73
+msgid "Share Story"
+msgstr ""
+
+#: stories/view.php:87
+msgid "Share this story"
+msgstr ""
+
+#: lib/Forms/Story.php:44
+msgid "Short Description"
+msgstr ""
+
+#: channels/aggregate.php:62 lib/Forms/Feed.php:85
+msgid "Source URL"
+msgstr ""
+
+#: lib/Forms/Feed.php:100
+msgid "Source URLs"
+msgstr ""
+
+#: config/templates.php.dist:9
+msgid "Standard"
+msgstr ""
+
+#: delivery/rss.php:58 stories/results.php:112
+#, php-format
+msgid "Stories tagged with %s in %s"
+msgstr ""
+
+#: stories/index.php:124 stories/results.php:116 lib/Block/story.php:3
+#: lib/Block/story.php:46 lib/Block/story.php:65
+msgid "Story"
+msgstr ""
+
+#: lib/News.php:751
+#, php-format
+msgid "Story \"%s\" not found in \"%s\"."
+msgstr ""
+
+#: stories/share.php:109
+msgid "Story Link"
+msgstr ""
+
+#: lib/Forms/Story.php:43
+msgid "Story Title (Headline)"
+msgstr ""
+
+#: lib/Forms/Story.php:82
+msgid "Story URL"
+msgstr ""
+
+#: lib/News/sql.php:456
+#, php-format
+msgid "Story URL \"%s\" not found."
+msgstr ""
+
+#: lib/Forms/Feed.php:78 lib/Forms/Feed.php:113
+#, php-format
+msgid ""
+"Story URL if not the default one. %c gets replaced by the feed ID, %s by the "
+"story ID."
+msgstr ""
+
+#: lib/Forms/Story.php:61
+msgid "Story body type"
+msgstr ""
+
+#: stories/edit.php:33 stories/delete.php:31
+#, php-format
+msgid "Story editing failed: %s"
+msgstr ""
+
+#: stories/delete.php:81
+msgid "Story has not been deleted."
+msgstr ""
+
+#: lib/News/sql.php:424
+#, php-format
+msgid "Story id \"%s\" not found."
+msgstr ""
+
+#: stories/share.php:85
+msgid "Subject"
+msgstr ""
+
+#: lib/Block/cloud.php:3 lib/Block/cloud.php:35
+msgid "Tag Cloud"
+msgstr ""
+
+#: lib/News.php:838 lib/News.php:843 lib/News.php:848 lib/News.php:854
+#: lib/News.php:859
+msgid "Tag support not enabled in backend."
+msgstr ""
+
+#: lib/Forms/Story.php:79
+msgid "Tags"
+msgstr ""
+
+#: lib/Block/story.php:105 templates/stories/story.html:6
+msgid "Tags: "
+msgstr ""
+
+#: lib/Jonah.php:200
+msgid "Text"
+msgstr ""
+
+#: channels/aggregate.php:100
+#, php-format
+msgid "The channel \"%s\" has been removed."
+msgstr ""
+
+#: channels/aggregate.php:79
+#, php-format
+msgid "The channel \"%s\" has been saved."
+msgstr ""
+
+#: channels/aggregate.php:87 channels/aggregate.php:107
+#, php-format
+msgid "The channel \"%s\" has been updated."
+msgstr ""
+
+#: channels/delete.php:69
+msgid "The channel has been deleted."
+msgstr ""
+
+#: stories/share.php:86
+msgid "The complete text of the story"
+msgstr ""
+
+#: channels/edit.php:70
+#, php-format
+msgid "The feed \"%s\" has been saved."
+msgstr ""
+
+#: lib/Forms/Feed.php:94
+msgid ""
+"The interval before stories aggregated into this feeds are rechecked for "
+"updates. If none, then stories will always be refetched from the sources."
+msgstr ""
+
+#: lib/Forms/Feed.php:83
+msgid ""
+"The interval before stories in this feed are rechecked for updates. If none, "
+"then stories will always be refetched from the source."
+msgstr ""
+
+#: stories/edit.php:61
+#, php-format
+msgid "The story \"%s\" has been saved."
+msgstr ""
+
+#: stories/delete.php:73
+msgid "The story has been deleted."
+msgstr ""
+
+#: stories/share.php:120
+msgid "The story was sent successfully."
+msgstr ""
+
+#: channels/aggregate.php:62 lib/Forms/Feed.php:85
+msgid ""
+"The url to use to fetch the stories, for example 'http://www.example.com/"
+"stories.rss'"
+msgstr ""
+
+#: channels/delete.php:67
+#, php-format
+msgid "There was an error deleting the channel: %s"
+msgstr ""
+
+#: stories/delete.php:71
+#, php-format
+msgid "There was an error deleting the story: %s"
+msgstr ""
+
+#: channels/aggregate.php:98
+#, php-format
+msgid "There was an error removing the channel: %s"
+msgstr ""
+
+#: channels/aggregate.php:77
+#, php-format
+msgid "There was an error saving the channel: %s"
+msgstr ""
+
+#: channels/edit.php:68
+#, php-format
+msgid "There was an error saving the feed: %s"
+msgstr ""
+
+#: stories/edit.php:59
+#, php-format
+msgid "There was an error saving the story: %s"
+msgstr ""
+
+#: channels/aggregate.php:85 channels/aggregate.php:105
+#, php-format
+msgid "There was an error updating the channel: %s"
+msgstr ""
+
+#: channels/aggregate.php:46
+msgid "This is no aggregated channel."
+msgstr ""
+
+#: stories/share.php:84
+msgid "To"
+msgstr ""
+
+#: channels/index.php:86 lib/Forms/Feed.php:45
+msgid "Type"
+msgstr ""
+
+#: config/templates.php.dist:53
+msgid "Ultracompact"
+msgstr ""
+
+#: stories/share.php:118
+#, php-format
+msgid "Unable to send story: %s"
+msgstr ""
+
+#: channels/aggregate.php:115
+msgid "Update"
+msgstr ""
+
+#: lib/Block/news_popular.php:45 lib/Block/news.php:41
+msgid "View"
+msgstr ""
+
+#: stories/edit.php:41 stories/index.php:31 stories/results.php:24
+#: stories/delete.php:39 channels/edit.php:43 channels/index.php:19
+#: channels/delete.php:38 channels/aggregate.php:53
+msgid "You are not authorised for this action."
+msgstr ""
+
+#: channels/edit.php:72
+msgid "You can now edit the sub-feeds."
+msgstr ""
+
+#: lib/News.php:793
+msgid "[No title]"
+msgstr ""
+
+#: lib/Jonah.php:241
+msgid "_Feeds"
+msgstr ""
+
+#: lib/Jonah.php:237
+msgid "_My News"
+msgstr ""
+
+#: lib/Jonah.php:254
+msgid "_New Story"
+msgstr ""
+
+#: lib/News.php:459
+msgid "none"
+msgstr ""
--- /dev/null
+# Dutch translation for Jonah
+# Copyright 2005-2009 The Horde Project
+# This file is distributed under the same license as the Jonah package.
+# Resan Sa-Ardnuam <horde@sa-ardnuam.nl>, 2005.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Jonah 0.1-cvs\n"
+"Report-Msgid-Bugs-To: dev@lists.horde.org\n"
+"POT-Creation-Date: 2005-05-11 11:00+0200\n"
+"PO-Revision-Date: 2005-05-20 09:54+0200\n"
+"Last-Translator: Resan Sa-Ardnuam <horde@sa-ardnuam.nl>\n"
+"Language-Team: i18n@lists.horde.org\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: lib/Block/delivery_email.php:59
+#, php-format
+msgid "%s by Email"
+msgstr "%s via Email"
+
+#: lists/index.php:118
+#, php-format
+msgid "%s to %s of %s"
+msgstr "%s tot %s van %s"
+
+#: delivery/index.php:47 lib/Block/delivery.php:76
+#, php-format
+msgid "'%s' stories by email"
+msgstr "'%s' artikelen via email"
+
+#: delivery/index.php:35 lib/Block/delivery.php:59
+#, php-format
+msgid "'%s' stories in HTML"
+msgstr "'%s' artikelen in HTML"
+
+#: delivery/index.php:41 lib/Block/delivery.php:68
+#, php-format
+msgid "'%s' stories in XML"
+msgstr "'%s' artikelen in XML"
+
+#: lib/News.php:375
+msgid "1 hour"
+msgstr "1 uur"
+
+#: lib/News.php:379
+msgid "12 hours"
+msgstr "12 uren"
+
+#: lib/News.php:376
+msgid "2 hours"
+msgstr "2 uren"
+
+#: lib/News.php:380
+msgid "24 hours"
+msgstr "24 uren"
+
+#: lib/News.php:374
+msgid "30 mins"
+msgstr "30 min."
+
+#: lib/News.php:377
+msgid "4 hours"
+msgstr "4 uren"
+
+#: lib/News.php:378
+msgid "8 hours"
+msgstr "8 uren"
+
+#: delivery/email.php:95
+#, php-format
+msgid ""
+"A confirmation message has been sent to %s. Click on the link in that "
+"message to finally subscribe to channel \"%s\"."
+msgstr ""
+"Een bevestigings bericht is verzonden naar %s. Klik op de link in dat "
+"bericht om uiteindelijk te abonneren op kanaal \"%s\"."
+
+#: stories/share.php:104
+msgid "A link to the story"
+msgstr "Een link naar het artikel"
+
+#: channels/aggregate.php:54
+msgid "Add"
+msgstr "Toevoegen"
+
+#: lib/Form/EditChannel.php:32
+msgid "Add New Channel"
+msgstr "Nieuw kanaal toevoegen"
+
+#: stories/edit.php:57
+msgid "Add New Story"
+msgstr "Nieuw Artikel toevoegen"
+
+#: stories/view.php:56
+msgid "Add a comment"
+msgstr "Commentaar toevoegen"
+
+#: channels/index.php:68
+msgid "Add story"
+msgstr "Artikel toevoegen"
+
+#: lib/News.php:413 lib/Jonah.php:125
+msgid "Aggregated"
+msgstr "Aggregeren"
+
+#: channels/aggregate.php:53
+#, php-format
+msgid "Aggregated channels for channel \"%s\""
+msgstr "Kanalen geaggregeerd voor kanaal \"%s\""
+
+#: channels/delete.php:54
+msgid "All stories created in this channel will be lost!"
+msgstr "Alle gemaakte artikelen in dit kanaal gaan verloren!"
+
+#: channels/index.php:33
+#, php-format
+msgid "An error occurred fetching channels: %s"
+msgstr "Een fout vond plaats bij het binnenhalen van kanalen: %s"
+
+#: channels/delete.php:56
+msgid "Any cached stories for this channel will be lost!"
+msgstr "Ieder artikel in de cache in dit kanaal gaan verloren!"
+
+#: channels/index.php:91
+msgid "Available Channels"
+msgstr "Beschikbare Kanalen"
+
+#: lib/Block/delivery.php:34
+#, php-format
+msgid "Available Channels from %s"
+msgstr "Beschikbare Kanalen van %s"
+
+#: delivery/index.php:29
+#, php-format
+msgid "Available Channels from %s (%s)"
+msgstr "Beschikbare Kanalen van %s (%s)"
+
+#: lib/Block/delivery.php:50
+msgid "Available News Channels"
+msgstr "Beschikbare Nieuws Kanalen"
+
+#: lib/Form/EditChannel.php:70 lib/Form/EditChannel.php:81
+msgid "Caching"
+msgstr "Caching"
+
+#: config/prefs.php.dist:18
+msgid "Change your content display options."
+msgstr "Verander de opties van de weergave van de inhoud"
+
+#: lib/Block/delivery_email.php:82
+msgid "Channel"
+msgstr "Kanaal"
+
+#: stories/index.php:125
+#, php-format
+msgid "Channel \"%s\""
+msgstr "Kanaal \"%s\""
+
+#: lib/News.php:110
+#, php-format
+msgid "Channel '%s' is not an internally authored channel."
+msgstr "Kanaal '%s' is een niet intern geauthoriseerd kanaal."
+
+#: content.php:77
+msgid "Channel Listing"
+msgstr "Kanaal Inhoud"
+
+#: lists/index.php:128
+msgid "Channel Lists Admin"
+msgstr "Kanaal Lijst Administratie"
+
+#: templates/subscribe/column_headers.inc:7 channels/aggregate.php:56
+#: lib/Form/EditChannel.php:43
+msgid "Channel Name"
+msgstr "Kanaal Naam"
+
+#: subscribe.php:134
+msgid "Channel Subscriptions"
+msgstr "Kanaal Abonnenmenten"
+
+#: subscribe.php:127
+msgid "Channel display failed."
+msgstr "Kanaal weergeven faalde."
+
+#: channels/delete.php:76
+msgid "Channel has not been deleted."
+msgstr "Kanaal is niet verwijderen."
+
+#: lib/News/sql.php:164
+#, php-format
+msgid "Channel id '%s' not found."
+msgstr "Kanaal id '%s' niet gevonden."
+
+#: stories/index.php:51
+msgid "Channel refreshed."
+msgstr "Kanaal ververst"
+
+#: subscribe.php:86
+msgid "Channel subscription editing failed."
+msgstr "Wijzigen van Kanaalabonnement is mislukt."
+
+#: subscribe.php:41
+msgid "Channel subscription failed."
+msgstr "Abonneren op kanaal is mislukt."
+
+#: channels/edit.php:56
+msgid "Channel type changed."
+msgstr "Kanaal type is gewijzigd."
+
+#: subscribe.php:73
+msgid "Channel unsubscription failed."
+msgstr "Kanaal opzeggen is mislukt."
+
+#: config/prefs.php.dist:11
+msgid "Choose which news channels you are subscribed to."
+msgstr "Kies op welke nieuws kanalen u geabonneerd bent."
+
+#: templates/stories/index.html:34
+msgid "Comments"
+msgstr "Commentaar"
+
+#: config/templates.php.dist:40
+msgid "Compact"
+msgstr "Compact"
+
+#: lib/News.php:416 lib/Jonah.php:128
+msgid "Composite"
+msgstr "Samengesteld"
+
+#: lib/Form/EditChannel.php:100
+msgid "Composite channels"
+msgstr "Samengestelde Kanalen"
+
+#: lib/Block/news.php:3
+msgid "Content Channel"
+msgstr "Inhoud Kanaal"
+
+#: lib/News.php:281
+#, php-format
+msgid "Could not deliver to the %s distribution list. %s"
+msgstr "Kan niet leveren aan de %s distributie lijst. %s"
+
+#: delivery/index.php:23
+#, php-format
+msgid "Could not get channel list. %s"
+msgstr "Kan kanaal lijst niet verkrijgen. %s"
+
+#: lib/Jonah.php:69
+#, php-format
+msgid "Could not open %s."
+msgstr "Kan %s niet openen."
+
+#: lists/index.php:107
+msgid "Current Recipients"
+msgstr "Huidige Ontvangers"
+
+#: stories/delete.php:50 stories/delete.php:55 channels/delete.php:49
+#: channels/delete.php:60
+msgid "Delete"
+msgstr "Verwijderen"
+
+#: channels/delete.php:46
+#, php-format
+msgid "Delete News Channel \"%s\"?"
+msgstr "Verwijder Nieuws Kanaal \"%s\"?"
+
+#: stories/delete.php:46
+#, php-format
+msgid "Delete News Story \"%s\"?"
+msgstr "Verwijder Artikel \"%s\"?"
+
+#: channels/index.php:49
+msgid "Delete channel"
+msgstr "Verwijder kanaal"
+
+#: lists/index.php:80
+msgid "Delete recipient"
+msgstr "Verwijder ontvanger"
+
+#: stories/index.php:86
+msgid "Delete story"
+msgstr "Verwijder artikel"
+
+#: lists/edit.php:56 lists/edit.php:61 lib/Jonah.php:252
+msgid "Delivery"
+msgstr "Distributie"
+
+#: channels/index.php:60
+msgid "Delivery lists"
+msgstr "Distributielijst"
+
+#: lists/edit.php:49
+#, php-format
+msgid "Delivery of '%s' stories"
+msgstr "Distributie van '%s' artikelen"
+
+#: lib/Form/EditChannel.php:64 lib/Form/EditChannel.php:80
+#: lib/Form/EditChannel.php:98
+msgid "Description"
+msgstr "Omschrijving"
+
+#: config/templates.php.dist:9
+msgid "Detailed"
+msgstr "Gedetaileerd"
+
+#: config/prefs.php.dist:17
+msgid "Display Options"
+msgstr "Weergave Opties"
+
+#: stories/delete.php:50 channels/delete.php:49
+msgid "Do not delete"
+msgstr "Niet verwijderen"
+
+#: templates/subscribe/subscribe.inc:49
+#: templates/subscribe/column_headers.inc:13
+msgid "Edit"
+msgstr "Wijzigen"
+
+#: lib/Form/EditChannel.php:32
+msgid "Edit Channel"
+msgstr "Wijzig Kanaal"
+
+#: templates/subscribe/header.inc:3
+msgid "Edit Channel Subscriptions"
+msgstr "Wijzig Kanaal Abonnementen"
+
+#: stories/edit.php:57
+msgid "Edit Story"
+msgstr "Wijzig Artikel"
+
+#: templates/subscribe/subscribe.inc:49
+msgid "Edit Subscription Details"
+msgstr "Wijzig Abonnement Details"
+
+#: lib/Form/EditChannel.php:87
+msgid "Edit aggregated channels"
+msgstr "Wijzig geaggregeerde kanalen"
+
+#: channels/index.php:44
+msgid "Edit channel"
+msgstr "Wijzig kanaal"
+
+#: channels/aggregate.php:23
+#, php-format
+msgid "Edit channel \"%s\""
+msgstr "Wijzig kanaal \"%s\""
+
+#: stories/index.php:81
+msgid "Edit story"
+msgstr "Wijzig artikel"
+
+#: templates/subscribe/edit.inc:5
+#, php-format
+msgid "Editing Channel %s"
+msgstr "Kanaal %s wijziging"
+
+#: lib/Delivery/email.php:195 lib/Delivery/email.php:210
+msgid "Email"
+msgstr "Email"
+
+#: delivery/email.php:62
+#, php-format
+msgid "Email Delivery for \"%s\""
+msgstr "Email distributie voor \"%s\""
+
+#: delivery/email.php:76 lib/Block/delivery_email.php:91
+msgid "Email address"
+msgstr "Email adres"
+
+#: lib/News.php:457
+#, php-format
+msgid "Error fetching channel. %s"
+msgstr "Fout bij binnenhalen van kanaal. %s"
+
+#: stories/share.php:83 stories/view.php:23
+#, php-format
+msgid "Error fetching story: %s"
+msgstr "Fout bij binnenhalen van artikel: %s"
+
+#: lib/News.php:694
+#, php-format
+msgid "Error parsing external channel from %s: %s"
+msgstr "Fout in onderzoeken van extern kanaal van %s: %s"
+
+#: lib/News.php:407 lib/Jonah.php:122
+msgid "External"
+msgstr "Extern"
+
+#: lib/Form/EditChannel.php:44
+msgid "Extra information for this channel type"
+msgstr "Extra informatie voor dit kanaal type"
+
+#: lib/Block/news.php:62
+msgid "First Story"
+msgstr "Eerste Artikel"
+
+#: stories/share.php:101
+msgid "From"
+msgstr "Van"
+
+#: stories/edit.php:89 stories/edit.php:91
+msgid "Full Story Text"
+msgstr "Volledig Artikel"
+
+#: delivery/html.php:35
+#, php-format
+msgid "HTML Delivery for '%s'"
+msgstr "HTML Distributie voor '%s'"
+
+#: lib/News.php:549
+msgid "HTML Version of Story"
+msgstr "HTML Versie van Artikel"
+
+#: templates/content/content.html:9
+msgid "Headlines"
+msgstr "Onderwerpen"
+
+#: lib/Delivery/email.php:74 lib/Delivery/email.php:77
+msgid "Hello"
+msgstr "Hallo"
+
+#: config/prefs.php.dist:35
+msgid "How many columns would you like to use to show headlines?"
+msgstr "Hoeveel kolommen wilt u zien voor de onderwerpen?"
+
+#: stories/edit.php:96
+msgid ""
+"If you enter a URL without a full story text, clicking on the story will "
+"send the reader straight to the URL, otherwise it will be shown at the end "
+"of the full story."
+msgstr ""
+"Als u een URL opgeeft zonder volledige tekst, zal de lezer rechtstreeksnaar "
+"de URL worden geleid, anders zal het aan het eind van het volledigeartikel "
+"worden getoond."
+
+#: channels/aggregate.php:59 lib/Form/EditChannel.php:74
+msgid "Image"
+msgstr "Afbeelding"
+
+#: stories/share.php:104
+msgid "Include"
+msgstr "Invoegen"
+
+#: lib/News.php:410 lib/Jonah.php:119
+msgid "Internal"
+msgstr "Intern"
+
+#: lib/api.php:47
+msgid "Internal Channels"
+msgstr "Interne Kanalen"
+
+#: stories/index.php:41
+#, php-format
+msgid "Invalid channel requested. %s"
+msgstr "Ongeldig verzoek voor kanaal. %s"
+
+#: channels/delete.php:34
+msgid "Invalid channel specified for deletion."
+msgstr "Ongeldig kanaal gespecifiseerd voor verwijdering."
+
+#: delivery/html.php:29 delivery/email.php:52 lists/edit.php:33
+#: lists/delete.php:37
+msgid "Invalid channel."
+msgstr "Ongeldig kanaal."
+
+#: lists/delete.php:27
+msgid "Invalid request to delete recipient from delivery list."
+msgstr "Ongeldig verzoek om ontvanger te verwijderen van distributie lijst."
+
+#: delivery/email.php:67
+msgid "Join this channel"
+msgstr "Abonneren op dit kanaal"
+
+#: delivery/index.php:53 channels/index.php:92
+msgid "Last Update"
+msgstr "Laatst bijgewerkt"
+
+#: templates/subscribe/column_headers.inc:9
+msgid "Last Updated"
+msgstr "Laatst bijgewerkt"
+
+#: delivery/email.php:68
+msgid "Leave this channel"
+msgstr "Kanaal opzeggen"
+
+#: channels/aggregate.php:58 lib/Form/EditChannel.php:73
+msgid "Link"
+msgstr "Link"
+
+#: stories/index.php:115
+msgid "Lists"
+msgstr "Lijst"
+
+#: lib/Block/news.php:57
+msgid "Maximum Stories"
+msgstr "Maximum artikelen"
+
+#: lib/Block/tree_menu.php:3
+msgid "Menu List"
+msgstr "Menu Lijst"
+
+#: stories/share.php:105
+msgid "Message"
+msgstr "Bericht"
+
+#: lists/index.php:123
+msgid "Method"
+msgstr "Methode"
+
+#: lib/News.php:78
+msgid "Missing channel id."
+msgstr "Onbrekende kanaal id."
+
+#: lib/Jonah.php:247
+msgid "My Content"
+msgstr "Mijn Inhoud"
+
+#: delivery/email.php:81 delivery/index.php:53 channels/index.php:92
+#: lists/index.php:123 lib/Delivery/email.php:212
+#: lib/Block/delivery_email.php:92
+msgid "Name"
+msgstr "Naam"
+
+#: stories/index.php:109 lib/Block/news.php:85
+msgid "New Story"
+msgstr "Nieuw artikel"
+
+#: channels/index.php:88
+msgid "New channel"
+msgstr "Nieuw kanaal"
+
+#: lists/index.php:104
+msgid "New recipient"
+msgstr "Nieuwe ontvanger"
+
+#: lib/api.php:46 config/prefs.php.dist:10
+msgid "News"
+msgstr "Nieuws"
+
+#: channels/index.php:97 lib/Jonah.php:257
+msgid "News Admin"
+msgstr "Nieuws Admin"
+
+#: lib/Block/delivery_email.php:39
+msgid "News Channel"
+msgstr "Nieuws Kanaal"
+
+#: lib/Block/news.php:38
+msgid "News Source"
+msgstr "Nieuws Bron"
+
+#: lib/Block/delivery_email.php:3
+msgid "News by Email"
+msgstr "Nieuws via Email"
+
+#: lib/Block/delivery.php:3
+msgid "News delivery"
+msgstr "Nieuws distributie"
+
+#: channels/index.php:23 lists/index.php:37
+msgid "News is not enabled."
+msgstr "Nieuws is niet actief."
+
+#: delivery/index.php:49 channels/index.php:82
+msgid "No Data"
+msgstr "Geen Data"
+
+#: channels/index.php:36
+msgid "No available channels."
+msgstr "Geen beschikbare kanalen."
+
+#: stories/index.php:48
+msgid "No available stories."
+msgstr "Geen beschikbare artikelen."
+
+#: stories/index.php:21 lists/index.php:23
+msgid "No channel requested."
+msgstr "Geen verzoek van kanaal."
+
+#: lib/Block/news.php:94
+msgid "No channel specified."
+msgstr "Geen kanaal gespecifiseerd."
+
+#: lib/Block/delivery_email.php:69
+msgid "No channels available."
+msgstr "Geen kanalen beschikbaar."
+
+#: lists/index.php:77
+msgid "No recipients."
+msgstr "Geen ontvangers."
+
+#: lib/News.php:474
+msgid "No stories are currently available."
+msgstr "Er zijn geen artikelen beschikbaar op dit moment."
+
+#: lib/Delivery.php:218
+#, php-format
+msgid "No such action '%s' found"
+msgstr "Actie '%s' niet gevonden"
+
+#: lib/News.php:601
+#, php-format
+msgid "No such backend '%s' found"
+msgstr "Backend '%s' niet gevonden"
+
+#: stories/delete.php:35
+msgid "No valid story requested for deletion."
+msgstr "Geen geldig artikel voor verwijdering."
+
+#: stories/share.php:26
+msgid "Note"
+msgstr "Notitie"
+
+#: stories/edit.php:67
+msgid ""
+"Note: this story won't get delivered to distribution lists automatically if "
+"this time is in the future."
+msgstr ""
+"N.B.: dit artikel zal niet automatisch worden afgeleverd aan distributie "
+"lijsten als de tijd in de toekomst ligt."
+
+#: config/prefs.php.dist:16
+msgid "Other Options"
+msgstr "Andere Opties"
+
+#: channels/delete.php:20
+msgid "Permission Denied."
+msgstr "Toegang geweigerd."
+
+#: lib/News.php:544
+msgid "Plaintext Version of Story"
+msgstr "Plattetekst versie van artikel"
+
+#: templates/stories/index.html:29
+msgid "Read"
+msgstr "Lezen"
+
+#: channels/delete.php:52
+msgid "Really delete this News Channel?"
+msgstr "Dit nieuws kanaal verwijderen?"
+
+#: stories/delete.php:53
+msgid "Really delete this News Story?"
+msgstr "Dit artikel verwijderen?"
+
+#: lists/index.php:123
+msgid "Recipient"
+msgstr "Ontvanger"
+
+#: lists/edit.php:85
+msgid "Recipient saved."
+msgstr "Ontvanger opgeslagen."
+
+#: lists/delete.php:52
+msgid "Recipient successfully removed."
+msgstr "Ontvanger met succes verwijderd."
+
+#: stories/share.php:102
+msgid "Recipients"
+msgstr "Ontvangers"
+
+#: channels/index.php:76
+msgid "Refresh channel"
+msgstr "Ververs kanaal"
+
+#: stories/edit.php:67
+msgid "Release Date"
+msgstr "Publicatie datum"
+
+#: stories/edit.php:66
+msgid "Release?"
+msgstr "Publiceren"
+
+#: stories/index.php:120
+msgid "Released"
+msgstr "Gepubliceerd"
+
+#: lists/delete.php:54
+msgid "Removal of recipient failed."
+msgstr "Verwijdering van ontvanger mislukt."
+
+#: channels/aggregate.php:24
+#, php-format
+msgid "Remove channel \"%s\""
+msgstr "Verwijder kanaal \"%s\""
+
+#: delivery/email.php:41
+msgid "Request confirmed."
+msgstr "Verzoek bevestigd."
+
+#: lib/Delivery/email.php:71
+#, php-format
+msgid "Request to receive news channel \"%s\""
+msgstr "Verzoek om kanaal \"%s\" te ontvangen"
+
+#: lib/Delivery/email.php:76
+#, php-format
+msgid "Request to stop receiving news channel \"%s\""
+msgstr "Verzoek om ontvangst van kanaal \"%s\" te stoppen"
+
+#: lib/Jonah.php:200
+msgid "Rich Text"
+msgstr "Rich Tekst"
+
+#: delivery/email.php:64 stories/edit.php:61 lists/edit.php:51
+#: lib/Block/delivery_email.php:78
+msgid "Save"
+msgstr "Opslaan"
+
+#: templates/subscribe/edit.inc:19
+msgid "Save Changes"
+msgstr "Wijzigingen opslaan"
+
+#: templates/delivery/html.html:14
+msgid "Select a format:"
+msgstr "Selecteer een formaat:"
+
+#: stories/share.php:98
+msgid "Send"
+msgstr "Zend"
+
+#: stories/share.php:102
+msgid "Separate multiple email addresses with commas."
+msgstr "Scheid meerdere email adressen met komma's."
+
+#: stories/share.php:96
+msgid "Share Story"
+msgstr "Deel Artikel"
+
+#: stories/view.php:43
+msgid "Share this story"
+msgstr "Deel dit artikel"
+
+#: stories/edit.php:65
+msgid "Short Description"
+msgstr "Korte Omschrijving"
+
+#: channels/aggregate.php:57 lib/Form/EditChannel.php:72
+msgid "Source URL"
+msgstr "Bron URL"
+
+#: lib/Form/EditChannel.php:87
+msgid "Source URLs"
+msgstr "Bron URL's"
+
+#: config/templates.php.dist:26
+msgid "Standard"
+msgstr "Standaard"
+
+#: stories/index.php:118
+msgid "Story"
+msgstr "Artikel"
+
+#: lib/News.php:667
+#, php-format
+msgid "Story \"%s\" not found in channel \"%s\"."
+msgstr "Artikel \"%s\" niet gevonden in kanaal \"%s\"."
+
+#: stories/share.php:127
+msgid "Story Link"
+msgstr "Link naar artikel"
+
+#: stories/edit.php:64
+msgid "Story Title (Headline)"
+msgstr "Titel"
+
+#: stories/edit.php:96
+msgid "Story URL"
+msgstr "Artikel URL"
+
+#: lib/News/sql.php:356
+#, php-format
+msgid "Story URL '%s' not found."
+msgstr "Artikel URL '%s' niet gevonden."
+
+#: lib/Form/EditChannel.php:65 lib/Form/EditChannel.php:99
+#, php-format
+msgid ""
+"Story URL if not the default one. %c gets replaced by the channel ID, %s by "
+"the story ID."
+msgstr ""
+"Artikel URL als niet de standaard. %c wordt vervangen door kanaal ID, %s "
+"door het artikel ID."
+
+#: stories/edit.php:69
+msgid "Story body type"
+msgstr "Artikel body type"
+
+#: stories/edit.php:31
+#, php-format
+msgid "Story editing failed: %s"
+msgstr "Artikel wijzigen mislukte: %s"
+
+#: stories/delete.php:72
+msgid "Story has not been deleted."
+msgstr "Artikel is niet verwijderd."
+
+#: lib/News/sql.php:331
+#, php-format
+msgid "Story id '%s' not found."
+msgstr "Artikel id '%s' niet gevonden."
+
+#: stories/share.php:103
+msgid "Subject"
+msgstr "Onderwerp"
+
+#: subscribe.php:122 templates/subscribe/subscribe.inc:40
+msgid "Subscribe"
+msgstr "Abonneren"
+
+#: content.php:53
+#, php-format
+msgid ""
+"Subscribed channel '%s' is no longer available, it will be removed from your "
+"subscriptions."
+msgstr ""
+"Geabonneerd kanaal '%s' is niet langer beschikbaar, het zal verwijderd "
+"worden van uw lijst."
+
+#: templates/subscribe/column_headers.inc:11
+msgid "Subscription"
+msgstr "Abonnement"
+
+#: lib/News.php:283
+#, php-format
+msgid "Successfully delivered to the %s distribution list."
+msgstr "De %s distributie lijst is met succes afgeleverd."
+
+#: lib/Jonah.php:208
+msgid "Text"
+msgstr "Tekst"
+
+#: channels/aggregate.php:93
+#, php-format
+msgid "The channel \"%s\" has been removed."
+msgstr "Het kanaal \"%s\" is verwijderd."
+
+#: channels/aggregate.php:74
+#, php-format
+msgid "The channel \"%s\" has been saved."
+msgstr "Het kanaal \"%s\" is opgeslagen."
+
+#: channels/aggregate.php:82 channels/aggregate.php:100
+#, php-format
+msgid "The channel \"%s\" has been updated."
+msgstr "Het kanaal \"%s\" is bijgewerkt."
+
+#: channels/edit.php:72
+#, php-format
+msgid "The channel '%s' has been saved."
+msgstr "Het kanaal '%s' is opgeslagen."
+
+#: channels/delete.php:69
+msgid "The channel has been deleted."
+msgstr "Het kanaal is verwijderd."
+
+#: stories/share.php:104
+msgid "The complete text of the story"
+msgstr "De complete tekst van het artikel"
+
+#: lib/Form/EditChannel.php:81
+msgid ""
+"The interval before stories aggregated into this channel are rechecked for "
+"updates. If none, then stories will always be refetched from the sources."
+msgstr ""
+"Het interval voordat artikelen geaggregeerd worden in dit kanaal zijn "
+"opnieuw gecontroleerd voor updates. Zoniet worden de artikelen altijd "
+"herladen vanuit de bronnen."
+
+#: lib/Form/EditChannel.php:70
+msgid ""
+"The interval before stories in this channel are rechecked for updates. If "
+"none, then stories will always be refetched from the source."
+msgstr ""
+"Het interval voordat artikelen in dit kanaal zijn opnieuw gecontroleerd voor "
+"updates. Zoniet worden de artikelen altijd herladen vanuit de bronnen."
+
+#: stories/edit.php:105
+#, php-format
+msgid "The story \"%s\" has been saved."
+msgstr "Het artikel \"%s\" is opgeslagen."
+
+#: stories/delete.php:64
+msgid "The story has been deleted."
+msgstr "Het artikel is verwijderd."
+
+#: stories/share.php:138
+msgid "The story was sent successfully."
+msgstr "Het artikel is met succes verzonden."
+
+#: channels/aggregate.php:57 lib/Form/EditChannel.php:72
+msgid ""
+"The url to use to fetch the stories, for example 'http://www.example.com/"
+"stories.rss'"
+msgstr ""
+"Het URL om artikelen op te halen, bijvoorbeed 'http://www.example.com/"
+"stories.rss'"
+
+#: channels/delete.php:67
+#, php-format
+msgid "There was an error deleting the channel: %s"
+msgstr "Er was een fout bij verwijderen van kanaal: %s"
+
+#: stories/delete.php:62
+#, php-format
+msgid "There was an error deleting the story: %s"
+msgstr "Er was een fout bij verwijderen van artikel: %s"
+
+#: channels/aggregate.php:91
+#, php-format
+msgid "There was an error removing the channel: %s"
+msgstr "Er was een fout bij verwijderen van kanaal: %s"
+
+#: channels/edit.php:70
+#, php-format
+msgid "There was an error saving the channel. %s"
+msgstr "Er was een fout bij opslaan van kanaal. %s"
+
+#: channels/aggregate.php:72
+#, php-format
+msgid "There was an error saving the channel: %s"
+msgstr "Er was een fout bij opslaan van kanaal: %s"
+
+#: stories/edit.php:103
+#, php-format
+msgid "There was an error saving the story: %s"
+msgstr "Er was een fout bij opslaan van artikel: %s"
+
+#: channels/aggregate.php:80 channels/aggregate.php:98
+#, php-format
+msgid "There was an error updating the channel: %s"
+msgstr "Er was een fout met bijwerken van kanaal: %s"
+
+#: lib/Delivery/email.php:196
+msgid ""
+"This driver allows the delivery of news stories via email to one or more "
+"recipients."
+msgstr ""
+"Met deze driver is het mogelijk om nieuwsberichten af te leveren via email "
+"naar een of meer ontvangers"
+
+#: channels/aggregate.php:41
+msgid "This is no aggregated channel."
+msgstr "Er is geen geaggregeerd kanaal."
+
+#: channels/index.php:92 lib/Form/EditChannel.php:37
+msgid "Type"
+msgstr "Type"
+
+#: config/templates.php.dist:54
+msgid "Ultracompact"
+msgstr "Ultracompact"
+
+#: delivery/email.php:39
+#, php-format
+msgid "Unable to confirm request."
+msgstr "Niet in staat op verzoek te bevestigen."
+
+#: stories/share.php:136
+#, php-format
+msgid "Unable to send story: %s"
+msgstr "Niet in staat om artikel %s te verzenden."
+
+#: templates/subscribe/edit.inc:20
+msgid "Undo Changes"
+msgstr "Wijzigingen ongedaan maken"
+
+#: subscribe.php:118 templates/subscribe/subscribe.inc:36
+msgid "Unsubscribe"
+msgstr "Opzeggen"
+
+#: channels/aggregate.php:105
+msgid "Update"
+msgstr "Bijwerken"
+
+#: stories/index.php:118
+msgid "Updated"
+msgstr "Bijgewerkt"
+
+#: templates/subscribe/edit.inc:9 lib/Block/news.php:49
+msgid "View"
+msgstr "Weergave"
+
+#: delivery/email.php:65
+msgid "What do you want to do?"
+msgstr "Wat wilt u doen?"
+
+#: stories/edit.php:39 stories/index.php:29 stories/delete.php:20
+#: channels/edit.php:43 channels/index.php:17 channels/aggregate.php:48
+#: lists/edit.php:41 lists/index.php:31 lists/delete.php:45
+msgid "You are not authorised for this action."
+msgstr "U bent niet geautoriseerd voor deze actie."
+
+#: channels/edit.php:74
+msgid "You can now edit the sub-channels."
+msgstr "U kunt nu de sub-kanalen bewerken."
+
+#: subscribe.php:84
+#, php-format
+msgid "You have edited your subscription to the channel '%s'."
+msgstr "U heeft uw abonnement op kanaal '%s' bewerkt."
+
+#: templates/content/content.html:24
+msgid "You have no news subscriptions."
+msgstr "U hebt geen abonnementen."
+
+#: lib/Delivery/email.php:74
+#, php-format
+msgid ""
+"You have requested to receive \"%s\" stories by email. Click on this link to "
+"confirm the request: %s"
+msgstr ""
+"U hebt verzocht om \"%s\" artikelen te ontvangen per email. Klik op deze "
+"link om het verzoek %s te bevestigen."
+
+#: lib/Delivery/email.php:77
+#, php-format
+msgid ""
+"You have requested to stop receiving \"%s\" stories by email. Click on this "
+"link to confirm the request: %s"
+msgstr ""
+"U heeft verzocht om de ontvangst van \"%s\" artikelen via email te stoppen. "
+"Klik op deze link om verzoek %s te bevestigen."
+
+#: subscribe.php:39
+#, php-format
+msgid "You have subscribed to the channel '%s'."
+msgstr "U heeft zich geabonneerd op kanaal '%s'."
+
+#: subscribe.php:62
+#, php-format
+msgid "You have unsubscribed from the channel '%s'."
+msgstr "U heeft het abonnement op kanaal '%s' opgezegd."
+
+#: lib/Delivery.php:275
+msgid "You must configure a Horde Datatree backend to use Jonah."
+msgstr "U moet de Horde Datatree backend configureren om Jonah te gebruiken."
+
+#: config/prefs.php.dist:9
+msgid "Your Content"
+msgstr "Uw Inhoud"
+
+#: lib/News.php:706
+msgid "[No title]"
+msgstr "[Geen titel]"
+
+#: lib/News.php:373
+msgid "none"
+msgstr "geen"
--- /dev/null
+# Romanian translations for Jonah package.
+# Copyright 2003-2009 The Horde Project
+# This file is distributed under the same license as the Jonah package.
+# Eugen Hoanca <eugenh@urban-grafx.ro>, 2003.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Jonah 0.0.3\n"
+"POT-Creation-Date: 2003-03-04 13:46+0100\n"
+"PO-Revision-Date: 2003-03-28 11:01+0200\n"
+"Last-Translator: Eugen Hoanca <eugenh@urban-grafx.ro>\n"
+"Language-Team: Romanian <i18n@lists.horde.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: lib/NewsAdmin.php:41
+msgid " (id=%d, %d stories)"
+msgstr "(id=%d, %d articole)"
+
+#: lib/Jonah.php:405
+msgid " and the UV index is a %s. "
+msgstr " si indexul UV este %s. "
+
+#: lib/Block/metar.php:103
+msgid " at %s %s"
+msgstr " la %s %s"
+
+#: lib/Jonah.php:389
+msgid " km"
+msgstr " km"
+
+#: lib/Jonah.php:365
+msgid " km/h"
+msgstr " km/h"
+
+#: lib/Jonah.php:390
+msgid " miles"
+msgstr " mile"
+
+#: templates/content/header.inc:4
+msgid "%d Headlines Available"
+msgstr " %d titluri disponibile"
+
+#: lib/Jonah.php:346
+msgid "%s at %s"
+msgstr "%s la %s"
+
+#: lib/NewsAdmin.php:17
+msgid "Add News Source"
+msgstr "Adaugare sursa stiri"
+
+#: lib/NewsAdmin.php:31 newsadmin.php:84
+msgid "Add Story"
+msgstr "Adaugare articol"
+
+#: lib/NewsAdmin.php:115
+msgid "Add a News Story for '%s'"
+msgstr "Adaugare stire pentru '%s'"
+
+#: lib/Jonah.php:378
+msgid "Barometer shows %s. "
+msgstr "Barometrul arata %s. "
+
+#: templates/channels/column_headers.inc:13
+msgid "Change"
+msgstr "Schimba"
+
+#: config/prefs.php.dist:22
+msgid "Change the location for which weather is obtained."
+msgstr "Schimbare locatie pentru care se obtine timp probabil."
+
+#: config/prefs.php.dist:34
+msgid "Change your content display options."
+msgstr "Schimbare optiuni afisare continut."
+
+#: content.php:15
+msgid "Channel Listing"
+msgstr "Listare canale"
+
+#: templates/channels/column_headers.inc:7
+msgid "Channel Name"
+msgstr "Nume canal"
+
+#: channels.php:180
+msgid "Channel Subscriptions"
+msgstr "Abonamente canal"
+
+#: channels.php:173
+msgid "Channel display failed."
+msgstr "Afisare canal esuata"
+
+#: channels.php:130
+msgid "Channel subscription editing failed."
+msgstr "Editarea abonarii la canal esuata."
+
+#: channels.php:73
+msgid "Channel subscription failed."
+msgstr "Abonare la canal esuata."
+
+#: channels.php:111
+msgid "Channel unsubscription failed."
+msgstr "Dezabonarea de la canal esuata."
+
+#: config/prefs.php.dist:16
+msgid "Choose which news channels you are subscribed to."
+msgstr "Alegere canale de stiri la care sunteti abonat."
+
+#: lib/Block/metar.php:101
+msgid "Clouds: %s"
+msgstr "Nori: %s"
+
+#: config/templates.php.dist:41
+msgid "Compact"
+msgstr "Compact"
+
+#: lib/api.php:63
+msgid "Current Phase"
+msgstr "Faza curenta a lunii"
+
+#: lib/Block/metar.php:25 lib/api.php:74
+msgid "Current Weather"
+msgstr "Timp probabil curent"
+
+#: lib/Block/metar.php:45 lib/Block/metar.php:47
+msgid "Current Weather for: %s"
+msgstr "Timp probabil curent pentru: %s"
+
+#: lib/NewsAdmin.php:90
+msgid "Delete News Source"
+msgstr "Stergere surse stiri"
+
+#: lib/NewsAdmin.php:216
+msgid "Delete News Story"
+msgstr "Stergere stire"
+
+#: lib/NewsAdmin.php:34 newsadmin.php:99
+msgid "Delete Source"
+msgstr "Stergere sursa"
+
+#: lib/NewsAdmin.php:144 newsadmin.php:165
+msgid "Delete Story"
+msgstr "Stergere articol"
+
+#: config/templates.php.dist:9
+msgid "Detailed"
+msgstr "Detaliat"
+
+#: config/prefs.php.dist:33
+msgid "Display Options"
+msgstr "Optiuni afisaj"
+
+#: templates/channels/subscribe.inc:56
+#: templates/channels/column_headers.inc:15
+msgid "Edit"
+msgstr "Editare"
+
+#: templates/channels/header.inc:3
+msgid "Edit Channel Subscriptions"
+msgstr "Editare abonamente canale"
+
+#: lib/NewsAdmin.php:59
+msgid "Edit News Source"
+msgstr "Editare sursa stiri"
+
+#: lib/NewsAdmin.php:171
+msgid "Edit News Story"
+msgstr "Editare stire"
+
+#: lib/NewsAdmin.php:33 newsadmin.php:94
+msgid "Edit Source"
+msgstr "Editare sursa"
+
+#: lib/NewsAdmin.php:32 newsadmin.php:89
+msgid "Edit Stories"
+msgstr "Editare articole"
+
+#: lib/NewsAdmin.php:143 newsadmin.php:160
+msgid "Edit Story"
+msgstr "Editare articol"
+
+#: templates/channels/subscribe.inc:55
+msgid "Edit Subscription Details"
+msgstr "Editare detalii abonare"
+
+#: templates/channels/edit.inc:5
+msgid "Editing Channel %s"
+msgstr "Editare canal %s"
+
+#: lib/Jonah.php:59
+msgid ""
+"Either no weather data is available for the location you have selected, or "
+"you have not selected a valid location."
+msgstr ""
+"Ori datele despre vreme nu sunt disponibile pentru locatia selectata, ori "
+"nu ati selectat o locatie valida."
+
+#: config/prefs.php.dist:128
+msgid "Enter NASDAQ/AMEX/NYSE ticker symbols to watch, separated with commas:"
+msgstr "Introduceti simbolurile NASDAQ/AMEX/NYSE, separate cu virgule:"
+
+#: lib/Jonah.php:490
+msgid "Error: could not open weather cache file for writing."
+msgstr "Eroare: deschidere fisier de cache al vremii pentru scriere esuata."
+
+#: lib/Jonah.php:483
+msgid "Error: could not update weather."
+msgstr "Eroare: nu s-a putut reinnoi informatia despre vreme."
+
+#: lib/Jonah.php:495
+msgid "Error: write to weather cache file failed."
+msgstr "Eroare: scrierea in fisierul cache al vremii esuata."
+
+#: lib/Block/moon.php:143
+msgid "First Half"
+msgstr "Prima jumatate"
+
+#: lib/Block/moon.php:53 lib/Block/moon.php:55
+msgid "First Quater"
+msgstr "Primul patrar"
+
+#: lib/Jonah.php:410
+msgid "Forecast: "
+msgstr "Prognoza: "
+
+#: lib/Jonah.php:325
+msgid "Friday"
+msgstr "Vineri"
+
+#: lib/Block/moon.php:61
+msgid "Full Moon"
+msgstr "Luna plina"
+
+#: lib/NewsAdmin.php:121 lib/NewsAdmin.php:191
+msgid "Full Story Text"
+msgstr "Text integral articol"
+
+#: lib/Block/moon.php:153
+msgid "Full moon"
+msgstr "Luna plina"
+
+#: lib/api.php:86
+msgid "Headlines"
+msgstr "Titluri"
+
+#: templates/menu/menu.inc:18
+msgid "Help"
+msgstr "Ajutor"
+
+#: lib/api.php:65
+msgid "Hemisphere:"
+msgstr "Emisfera:"
+
+#: config/prefs.php.dist:57
+msgid "How many columns would you like to use to show headlines?"
+msgstr "Cate coloane doriti pentru afisare titluri?"
+
+#: lib/Jonah.php:372
+msgid "It feels like %s. "
+msgstr "Se pare ca %s. "
+
+#: templates/backend/backend.inc:5
+msgid "Jonah Backend"
+msgstr "Suport Jonah"
+
+#: templates/index/notconfigured.inc:4
+msgid "Jonah is not properly configured"
+msgstr "Jonah nu este corect configurat"
+
+#: templates/backend/backend.inc:14
+msgid "Just Fetch latest RSS files"
+msgstr "Aducere ultimele fisiere RSS"
+
+#: templates/backend/backend.inc:15
+msgid "Just Generate HTML from RSS files"
+msgstr "Generare HTML din fisiere RSS"
+
+#: config/prefs.php.dist:9
+msgid "Language"
+msgstr "Limba"
+
+#: lib/Block/moon.php:146
+msgid "Last Half"
+msgstr "Ultima jumatate"
+
+#: lib/Block/moon.php:67 lib/Block/moon.php:69
+msgid "Last Quater"
+msgstr "Ultimul patrar"
+
+#: templates/channels/column_headers.inc:10
+msgid "Last Updated"
+msgstr "Ultima data innoit"
+
+#: lib/Block/metar.php:52
+msgid "Last Updated: %s"
+msgstr "Ultima data innoit: %s"
+
+#: lib/api.php:47
+msgid "Latitude"
+msgstr "Latitudine"
+
+#: templates/index/notconfigured.inc:58
+msgid "List of METAR weather stations."
+msgstr "Lista statii meteo METAR"
+
+#: lib/api.php:75
+msgid "Location"
+msgstr "Locatie"
+
+#: lib/NewsAdmin.php:36
+msgid "Manage News Sources"
+msgstr "Administrare surse stiri"
+
+#: lib/NewsAdmin.php:146
+msgid "Manage News Stories"
+msgstr "Administrare stiri"
+
+#: lib/Jonah.php:325
+msgid "Monday"
+msgstr "Luni"
+
+#: lib/Block/moon.php:19 lib/api.php:58
+msgid "Moon Phases"
+msgstr "Fazele lunii"
+
+#: templates/menu/menu.inc:6
+msgid "My Content"
+msgstr "Cuprinsul meu"
+
+#: lib/Block/moon.php:47
+msgid "New Moon"
+msgstr "Luna noua"
+
+#: lib/Block/moon.php:31 lib/Block/moon.php:151
+msgid "New moon"
+msgstr "Luna noua"
+
+#: lib/Block/news.php:37 config/prefs.php.dist:15
+msgid "News"
+msgstr "Stire(i)"
+
+#: newsadmin.php:47 templates/menu/menu.inc:13
+msgid "News Admin"
+msgstr "Administrator stire(i)"
+
+#: lib/NewsAdmin.php:48
+msgid "News Source"
+msgstr "Sursa stire(i)"
+
+#: lib/api.php:64
+msgid "Next Phase"
+msgstr "Faza urmatoare"
+
+#: lib/NewsAdmin.php:95 lib/NewsAdmin.php:220
+msgid "No"
+msgstr "Nu"
+
+#: lib/News/sql.php:310
+msgid "No configuration information specified for SQL News Stories."
+msgstr "Nici o configuratie specificata pentru stirile SQL."
+
+#: lib/Weather/sql.php:201
+msgid "No configuration information specified for SQL Preferences."
+msgstr "Nici o configuratie specificata pentru preferintele SQL."
+
+#: lib/Block/sunrise.php:29
+msgid "No location is set."
+msgstr "Nu e setata nici o locatie."
+
+#: lib/api.php:68
+msgid "Northern Hemisphere"
+msgstr "Emisfera nordica"
+
+#: templates/menu/menu.inc:8
+msgid "Options"
+msgstr "Optiuni"
+
+#: config/prefs.php.dist:32
+msgid "Other Options"
+msgstr "Alte optiuni"
+
+#: templates/prefs/setlocation.inc:27
+msgid "Please select a Country/State..."
+msgstr "Selectati un judet/o tara..."
+
+#: templates/prefs/setlocation.inc:40
+msgid "Please select a station..."
+msgstr "Selectati o statie..."
+
+#: lib/Block/metar.php:92
+msgid "Pressure: %s%s"
+msgstr "Presiune atmosferica: %s%s"
+
+#: lib/Jonah.php:51 lib/Jonah.php:54
+msgid "Radar"
+msgstr "Radar"
+
+#: lib/NewsAdmin.php:102
+msgid "Really delete this News Source?"
+msgstr "Sigur stergeti sursa de stiri?"
+
+#: lib/NewsAdmin.php:228
+msgid "Really delete this News Story?"
+msgstr "Sigur stergeti stirea?"
+
+#: lib/News/sql.php:328
+msgid "Required 'charset' not specified in news stories configuration."
+msgstr "'charset' nespecificat in configurarea stirilor."
+
+#: lib/News/sql.php:325
+msgid "Required 'database' not specified in news stories configuration."
+msgstr "'database' nespecificat in configurarea stirilor."
+
+#: lib/Weather/sql.php:216
+msgid "Required 'database' not specified in preferences configuration."
+msgstr "'database' nespecificat in configurarea preferintelor."
+
+#: lib/News/sql.php:316
+msgid "Required 'hostspec' not specified in news stories configuration."
+msgstr "'hostspec' nespecificat in configuratia stirilor."
+
+#: lib/Weather/sql.php:207
+msgid "Required 'hostspec' not specified in preferences configuration."
+msgstr "'hostspec' nespecificat in configurarea preferintelor."
+
+#: lib/News/sql.php:322
+msgid "Required 'password' not specified in news stories configuration."
+msgstr "'password' nespecificat in configurarea stirilor."
+
+#: lib/Weather/sql.php:213
+msgid "Required 'password' not specified in preferences configuration."
+msgstr "'password' nespecificat in configurarea preferintelor."
+
+#: lib/News/sql.php:313
+msgid "Required 'phptype' not specified in news stories configuration."
+msgstr "'phptype' nespecificat in configurarea stirilor."
+
+#: lib/Weather/sql.php:204
+msgid "Required 'phptype' not specified in preferences configuration."
+msgstr "'phptype' nespecificat in configurarea preferintelor."
+
+#: lib/News/sql.php:319
+msgid "Required 'username' not specified in news stories configuration."
+msgstr "'username' nespecificat in configurarea stirilor."
+
+#: lib/Weather/sql.php:210
+msgid "Required 'username' not specified in preferences configuration."
+msgstr "'username' nespecificat in configurarea preferintelor."
+
+#: lib/Jonah.php:325
+msgid "Saturday"
+msgstr "Sambata"
+
+#: templates/channels/edit.inc:19
+msgid "Save Changes"
+msgstr "Salveaza modificari"
+
+#: config/prefs.php.dist:44
+msgid "Select your preferred language:"
+msgstr "Selectare limba favorita:"
+
+#: config/prefs.php.dist:10
+msgid "Set the your preferred display language."
+msgstr "Setare limba favorita."
+
+#: config/prefs.php.dist:28
+msgid "Set which stock ticker symbols to watch."
+msgstr "Setati ce simboluri doriti sa urmariti."
+
+#: lib/NewsAdmin.php:119 lib/NewsAdmin.php:186
+msgid "Short Description"
+msgstr "Descriere scurta"
+
+#: config/prefs.php.dist:106
+msgid "Should measurements be shown in metric values?"
+msgstr "Afisare masuri in sistem metric?"
+
+#: config/prefs.php.dist:135
+msgid "Show Dow Jones Industrial Average (INDU)?"
+msgstr "Afisare medie industriala Dow Jones (INDU)?"
+
+#: config/prefs.php.dist:142
+msgid "Show S&P 500 (SPX)?"
+msgstr "Afisare S& P500 (SPX)?"
+
+#: config/prefs.php.dist:71
+msgid "Show headlines in %s summary?"
+msgstr "Afisare titluri in cuprinsul %s?"
+
+#: config/prefs.php.dist:92
+msgid "Show headlines in main content display?"
+msgstr "Afisare titluri in fereastra principala?"
+
+#: config/prefs.php.dist:78
+msgid "Show stock quotes in %s summary?"
+msgstr "Afisare cotatii bursa in cuprinsul %s?"
+
+#: config/prefs.php.dist:99
+msgid "Show stock quotes in main content display?"
+msgstr "Afisare cotatii bursa in fereastra principala?"
+
+#: config/prefs.php.dist:64
+msgid "Show weather in %s summary?"
+msgstr "Afisare timp probabil in cuprinsul %s?"
+
+#: config/prefs.php.dist:85
+msgid "Show weather in main content display?"
+msgstr "Afisare timp probabil in fereastra principala?"
+
+#: templates/index/notconfigured.inc:39
+msgid "Some of Jonah's configuration files are missing:"
+msgstr "Anumite fisiere de configurare Jonah lipsesc:"
+
+#: lib/Block/news.php:56
+msgid "Something went wrong!"
+msgstr "Ceva nu a mers bine!"
+
+#: lib/NewsAdmin.php:99 lib/api.php:87
+msgid "Source"
+msgstr "Sursa"
+
+#: lib/NewsAdmin.php:20 lib/NewsAdmin.php:76
+msgid "Source Description"
+msgstr "Descriere sursa"
+
+#: lib/NewsAdmin.php:19 lib/NewsAdmin.php:71
+msgid "Source Name"
+msgstr "Nume sursa"
+
+#: lib/api.php:69
+msgid "Southern Hemisphere"
+msgstr "Emisfera sudica"
+
+#: config/templates.php.dist:27
+msgid "Standard"
+msgstr "Standard"
+
+#: stockquote.php:17
+msgid "Stock Quote"
+msgstr "Cotatie bursa"
+
+#: lib/Block/stocks.php:23 lib/api.php:105 content.php:30
+msgid "Stock Quotes"
+msgstr "Cotatii bursa"
+
+#: config/prefs.php.dist:27
+msgid "Stocks"
+msgstr "Burse"
+
+#: lib/NewsAdmin.php:160 lib/NewsAdmin.php:225
+msgid "Story"
+msgstr "Articol"
+
+#: lib/NewsAdmin.php:118 lib/NewsAdmin.php:183
+msgid "Story Title (Headline)"
+msgstr "Titlu Articol"
+
+#: lib/NewsAdmin.php:122 lib/NewsAdmin.php:194
+msgid "Story URL"
+msgstr "URL Articol"
+
+#: lib/News/sql.php:214
+msgid "Story not found."
+msgstr "Articol negasit."
+
+#: channels.php:168 templates/channels/subscribe.inc:45
+#: templates/channels/subscribe.inc:46
+msgid "Subscribe"
+msgstr "Abonare"
+
+#: lib/Block/sunrise.php:42
+msgid "Sun Rise"
+msgstr "Soarele rasare"
+
+#: lib/api.php:46
+msgid "Sun Rise / Set"
+msgstr "Soarele Rasare / Apune"
+
+#: lib/Block/sunrise.php:23
+msgid "Sun Rise and Set"
+msgstr "Soarele rasare si apune"
+
+#: lib/Block/sunrise.php:47
+msgid "Sun Set"
+msgstr "Soarele apune"
+
+#: lib/Jonah.php:325
+msgid "Sunday"
+msgstr "Duminica"
+
+#: lib/Block/metar.php:84
+msgid "Temperature: %s; Dewpoint: %s"
+msgstr "Temperatura: %s; Umiditate: %s"
+
+#: newsadmin.php:186
+msgid "The News Story '%s' has been added."
+msgstr "Stirea '%s' a fost adaugata."
+
+#: newsadmin.php:205
+msgid "The Story has been modified."
+msgstr "Articolul a fost modificat."
+
+#: lib/Jonah.php:405
+msgid "The UV index is a %s. "
+msgstr "Indexul de UV este %s."
+
+#: newsadmin.php:65
+msgid "The source '%s' has been added."
+msgstr "Sursa '%s' a fost adaugata."
+
+#: newsadmin.php:136
+msgid "The source has been deleted."
+msgstr "Sursa a fost stearsa."
+
+#: newsadmin.php:117
+msgid "The source has been modified."
+msgstr "Sursa a fost modificata."
+
+#: newsadmin.php:141
+msgid "The source was not deleted."
+msgstr "Sursa nu a fost stearsa."
+
+#: newsadmin.php:221
+msgid "The story has been deleted."
+msgstr "Articolul a fost sters."
+
+#: newsadmin.php:226
+msgid "The story was not deleted."
+msgstr "Articolul nu a fost sters"
+
+#: lib/Jonah.php:350
+msgid "The temperature is <b>%s</b>, "
+msgstr "Temperatura este <b>%s</b>, "
+
+#: lib/NewsAdmin.php:45
+msgid "There are no News Sources."
+msgstr "Nu exista surse de stiri."
+
+#: lib/NewsAdmin.php:156
+msgid "There are no News Stories."
+msgstr "Nu exista stiri."
+
+#: newsadmin.php:184
+msgid "There was an error adding the News Story: %s."
+msgstr "S-a produs o eroare la adaugarea stirii: %s."
+
+#: newsadmin.php:68
+msgid "There was an error adding the source: %s."
+msgstr "S-a produs o eroare la adaugarea sursei: %s."
+
+#: newsadmin.php:138
+msgid "There was an error deleting the source: %s."
+msgstr "S-a produs o eroare la stergerea sursei: %s."
+
+#: newsadmin.php:223
+msgid "There was an error deleting the story: %s."
+msgstr "S-a produs o eroare la stergerea articolului: %s."
+
+#: newsadmin.php:203
+msgid "There was an error editing the Story: %s."
+msgstr "S-a produs o eroare la editarea articolului: %s"
+
+#: newsadmin.php:120
+msgid "There was an error editing the source: %s."
+msgstr "S-a produs o eroare la editarea sursei: %s."
+
+#: templates/index/notconfigured.inc:72
+msgid "This file contains preferences for Jonah."
+msgstr "Acest fisier contine preferintele pentru Jonah."
+
+#: templates/index/notconfigured.inc:51
+msgid ""
+"This file defines all of the headline channels that you wish Jonah to "
+"display."
+msgstr ""
+"Acest fisier defineste toate titlurile canalelor pe care doriti sa le "
+"afiseze Jonah."
+
+#: templates/index/notconfigured.inc:65
+msgid ""
+"This file defines the HTML (or other) templates that are used to generate "
+"different views of the news channels that Jonah provides."
+msgstr ""
+"Acest fisier defineste modelele HTML (sau altele) care vor fi utilizate "
+"pentru a genera vizualizari diferite ale canalelor de stiri ale Jonah."
+
+#: lib/NewsAdmin.php:64
+msgid "This is not a valid News Source."
+msgstr "Aceasta nu este o sursa de stiri valida."
+
+#: lib/NewsAdmin.php:175
+msgid "This is not a valid News Story."
+msgstr "Aceasta nu este o stire valida."
+
+#: templates/index/notconfigured.inc:44
+msgid ""
+"This is the main Jonah configuration file. It contains options for all Jonah "
+"scripts."
+msgstr ""
+"Acesta este principalul fisier de configurare Jonah. Contine optiuni pentru "
+"toate scripturile Jonah."
+
+#: lib/Jonah.php:325
+msgid "Thursday"
+msgstr "Joi"
+
+#: lib/Jonah.php:325
+msgid "Tuesday"
+msgstr "Marti"
+
+#: config/templates.php.dist:54
+msgid "Ultracompact"
+msgstr "Ultracompact"
+
+#: templates/channels/edit.inc:20
+msgid "Undo Changes"
+msgstr "Anuleaza schimbari"
+
+#: channels.php:164 templates/channels/subscribe.inc:40
+#: templates/channels/subscribe.inc:41
+msgid "Unsubscribe"
+msgstr "Dezabonare"
+
+#: lib/api.php:119 templates/menu/menu.inc:12
+msgid "Update"
+msgstr "Innoire"
+
+#: templates/backend/backend.inc:11
+msgid "Update All Channels"
+msgstr "Innoire toate canalele"
+
+#: prefs.php:54
+msgid "User Options"
+msgstr "Optiuni utilizator"
+
+#: lib/api.php:96 templates/channels/edit.inc:9
+msgid "View"
+msgstr "Vizualizare"
+
+#: lib/Jonah.php:556
+msgid "View Quote Details"
+msgstr "Vizualizare detalii cotatie"
+
+#: lib/Jonah.php:385
+msgid "Visibility is %s"
+msgstr "Vizibilitatea este %s"
+
+#: lib/Block/metar.php:76
+msgid "Visibility: %s%s"
+msgstr "Vizibilitate: %s%s"
+
+#: content.php:27
+msgid "Weather"
+msgstr "Timp probabil"
+
+#: lib/Block/weather.php:23 lib/api.php:109
+msgid "Weather Forecast"
+msgstr "Prognoza vreme"
+
+#: config/prefs.php.dist:21
+msgid "Weather Station"
+msgstr "Statie meteo"
+
+#: templates/prefs/setlocation.inc:4 templates/prefs/setlocation.inc:60
+msgid "Weather station:"
+msgstr "Statie meteo:"
+
+#: lib/Block/metar.php:113
+msgid "Weather:"
+msgstr "Timp probabil:"
+
+#: lib/Jonah.php:325
+msgid "Wednesday"
+msgstr "Miercuri"
+
+#: lib/api.php:60
+msgid "Which phases?"
+msgstr "Care faze?"
+
+#: lib/Block/metar.php:59
+msgid "Wind: %s%s from %s, gusting to %s%s."
+msgstr "Vant: %s%s dinspre %s, inspre %s%s."
+
+#: lib/Block/metar.php:66
+msgid "Wind: %s%s from %s."
+msgstr "Vant: %s%s dinspre %s."
+
+#: lib/NewsAdmin.php:95 lib/NewsAdmin.php:220
+msgid "Yes"
+msgstr "Da"
+
+#: lib/NewsAdmin.php:129 lib/NewsAdmin.php:202
+msgid "You MUST enter either a URL or a full story."
+msgstr "TREBUIE introdus ori un URL ori un articol complet."
+
+#: channels.php:122
+msgid "You have edited your subscription to the channel %s."
+msgstr "Ati editat abonamentul la canalul %s."
+
+#: content.php:70
+msgid "You have no news subscriptions."
+msgstr "Nu aveti nici un abonament de stiri."
+
+#: lib/Block/stocks.php:30
+msgid "You have no ticker symbols in your portfolio at this time."
+msgstr "Nu aveti nici un simbol in portofoliu in acest moment."
+
+#: content.php:44
+msgid "You have no ticker symbols listed at this time."
+msgstr "Nu aveti nici un simbol listat in acest moment."
+
+#: channels.php:65
+msgid "You have subscribed to the channel %s."
+msgstr "Ati fost inscris la canalul %s."
+
+#: channels.php:100
+msgid "You have unsubscribed from the channel %s."
+msgstr "Ati fost dezabonat de la canalul %s."
+
+#: lib/NewsAdmin.php:120 lib/NewsAdmin.php:189
+msgid "You must enter either a URL or a full story."
+msgstr "Trebuie introdus ori un URL ori un articol integral."
+
+#: config/prefs.php.dist:14 config/prefs.php.dist:20 config/prefs.php.dist:26
+msgid "Your Content"
+msgstr "Cuprinsul vostru"
+
+#: config/prefs.php.dist:8
+msgid "Your Information"
+msgstr "Informatii personale"
+
+#: lib/Jonah.php:362
+msgid "and the wind is %s at %s. "
+msgstr "si vantul este %s la %s. "
+
+#: lib/Jonah.php:368
+msgid "and the wind is %s. "
+msgstr "si vantul este %s. "
+
+#: lib/Jonah.php:359
+msgid "and there is no wind measured. "
+msgstr "si nu este nici un vant masurat. "
+
+#: lib/Jonah.php:329
+msgid "cloudy"
+msgstr "noros"
+
+#: lib/Jonah.php:329
+msgid "cold"
+msgstr "rece"
+
+#: lib/Jonah.php:328
+msgid "dust"
+msgstr "praf"
+
+#: lib/Jonah.php:302
+msgid "error: could not open quote %s cache file for writing."
+msgstr "eroare: nu s-a putut deschide fisierul de cotatii pentru scriere."
+
+#: lib/Jonah.php:297
+msgid "error: could not update quote %s."
+msgstr "eroare: nu s-a putut reinnoi cotatia %s."
+
+#: lib/Jonah.php:307
+msgid "error: write to quote %s cache file failed."
+msgstr "eroare: scriere in fisierul de cotatii esuata."
+
+#: lib/Jonah.php:328
+msgid "flurries"
+msgstr "rafale"
+
+#: lib/Jonah.php:328
+msgid "fog"
+msgstr "ceata"
+
+#: lib/Jonah.php:329
+msgid "haze"
+msgstr "ceata"
+
+#: lib/Jonah.php:327
+msgid "heavy rain"
+msgstr "ploaie torentiala"
+
+#: lib/Jonah.php:401
+msgid "high %s. Use sunscreen"
+msgstr "%s inalta(e). Utilizati protectie contra soarelui."
+
+#: lib/Jonah.php:330
+msgid "hot"
+msgstr "fierbinte"
+
+#: lib/Jonah.php:356
+msgid "humidity is at %s"
+msgstr "umiditate este %s"
+
+#: lib/Jonah.php:387
+msgid "infinite"
+msgstr "infinit"
+
+#: lib/Jonah.php:327
+msgid "light flurries"
+msgstr "rafale usoare"
+
+#: lib/Jonah.php:397
+msgid "low %s"
+msgstr "%s scazuta(e)"
+
+#: lib/Jonah.php:395
+msgid "minimal %s"
+msgstr "%s minim(e)"
+
+#: lib/Jonah.php:399
+msgid "moderate %s"
+msgstr "%s moderat(e)"
+
+#: lib/Jonah.php:329
+msgid "mostly cloudy"
+msgstr "mai mult noros"
+
+#: lib/Jonah.php:330
+msgid "mostly sunny"
+msgstr "mai mult insorit"
+
+#: lib/Jonah.php:366
+msgid "mph"
+msgstr "mph"
+
+#: templates/content/header.inc:6
+msgid "no headlines available at this time"
+msgstr "nici un titlu disponibil in acest moment"
+
+#: lib/Jonah.php:330
+msgid "partly cloudy"
+msgstr "partial noros"
+
+#: lib/Jonah.php:326
+msgid "rain"
+msgstr "ploaie"
+
+#: lib/Jonah.php:327
+msgid "sleet"
+msgstr "lapovita"
+
+#: lib/Jonah.php:329
+msgid "smoke"
+msgstr "fum"
+
+#: lib/Jonah.php:326
+msgid "snow"
+msgstr "zapada"
+
+#: lib/Jonah.php:328
+msgid "snow and wind"
+msgstr "viscol"
+
+#: lib/Jonah.php:330
+msgid "sunny"
+msgstr "insorit(a)"
+
+#: lib/Jonah.php:326
+msgid "thunder storms"
+msgstr "furtuna de fulgere"
+
+#: lib/Jonah.php:403
+msgid "very high %s. Stay indoors"
+msgstr "%s foarte ridicata(e). Ramaneti inauntru"
+
+#: lib/Jonah.php:326
+msgid "windy"
+msgstr "vantos(asa)"
--- /dev/null
+# Turkish translations for Jonah.
+# Copyright 2002-2009 The Horde Project
+# This file is distributed under the same license as the Jonah package.
+# horde-tr team <horde-tr@metu.edu.tr>, 2008.
+#
+# story: Haber
+msgid ""
+msgstr ""
+"Project-Id-Version: Jonah 1.0-cvs\n"
+"Report-Msgid-Bugs-To: dev@lists.horde.org\n"
+"POT-Creation-Date: 2008-04-15 12:57+0300\n"
+"PO-Revision-Date: 2008-04-01 10:12+0300\n"
+"Last-Translator: A.Ozaygen, O.Kosar, E.sezginer\n"
+"Language-Team: i18n@lists.horde.org\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-9\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: lib/Block/news_popular.php:77 lib/Block/news_popular.php:80
+msgid " - Most read stories"
+msgstr " - En çok okunan haberler"
+
+#: lib/Block/delivery.php:48
+#, php-format
+msgid "\"%s\" stories in HTML"
+msgstr "\"%s\"HTML haberler"
+
+#: lib/Block/delivery_email.php:53
+#, php-format
+msgid "%s by Email"
+msgstr "%s Eposta ile"
+
+#: lists/index.php:121
+#, php-format
+msgid "%s to %s of %s"
+msgstr "%s'den %s'e, %s üstünden"
+
+#: lib/News.php:474
+msgid "1 hour"
+msgstr "1 saat"
+
+#: lib/News.php:478
+msgid "12 hours"
+msgstr "12 saat"
+
+#: lib/News.php:475
+msgid "2 hours"
+msgstr "2 saat"
+
+#: lib/News.php:479
+msgid "24 hours"
+msgstr "24 saat"
+
+#: lib/News.php:473
+msgid "30 mins"
+msgstr "30 dk"
+
+#: lib/News.php:476
+msgid "4 hours"
+msgstr "4 saat"
+
+#: lib/News.php:477
+msgid "8 hours"
+msgstr "8 saat"
+
+#: delivery/email.php:97
+#, php-format
+msgid ""
+"A confirmation message has been sent to %s. Click on the link in that "
+"message to finally subscribe to channel \"%s\"."
+msgstr ""
+"%s'e doðrulama iletisi gönderilmiþtir. Ýleti içindeki baða týklayarak\"%s\" "
+"kanalýna abone olabilirsiniz."
+
+#: stories/share.php:86
+msgid "A link to the story"
+msgstr "Habere baðlantý"
+
+#: channels/aggregate.php:56
+msgid "Add"
+msgstr "Ekle"
+
+#: content.php:34
+msgid "Add Content"
+msgstr "Ýçerik Ekle"
+
+#: lib/Forms/Story.php:37
+msgid "Add New Story"
+msgstr "Yeni Haber Ekle"
+
+#: channels/index.php:71
+msgid "Add story"
+msgstr "Haber ekle"
+
+#: lib/api.php:118
+msgid "Administrator"
+msgstr "Yönetici"
+
+#: lib/News.php:512 lib/Jonah.php:138
+msgid "Aggregated Feed"
+msgstr "Toplu Besleme"
+
+#: channels/aggregate.php:55
+#, php-format
+msgid "Aggregated channels for channel \"%s\""
+msgstr "\"%s\" kanalý için toplu kanallar"
+
+#: channels/delete.php:55
+msgid "All stories created in this channel will be lost!"
+msgstr "Bu kanalda yaratýlan tüm haberler kaybolacaktýr!"
+
+#: stories/results.php:114
+#, php-format
+msgid "All stories tagged with %s"
+msgstr "Tüm haberler %s olarak iþaretlendi"
+
+#: channels/index.php:35
+#, php-format
+msgid "An error occurred fetching channels: %s"
+msgstr "Kanallarý çekerken bir hata oluþtu: %s"
+
+#: channels/delete.php:57
+msgid "Any cached stories for this channel will be lost!"
+msgstr "Bu kanal için önbelleklenmiþ herhangi bir haber silinecektir!"
+
+#: lib/Forms/Feed.php:83 lib/Forms/Feed.php:94
+msgid "Caching"
+msgstr "Önbellekleniyor"
+
+#: stories/pdf.php:44
+msgid "Cannot generate PDFs of remote stories."
+msgstr "Uzaktaki haberin PDFi yaratýlamýyor."
+
+#: lib/Block/delivery_email.php:87
+msgid "Channel"
+msgstr "Kanal"
+
+#: lib/News/sql.php:510
+#, php-format
+msgid "Channel \"%s\" not found."
+msgstr "Kanal \"%s\" bulunamýyor."
+
+#: lists/index.php:130
+msgid "Channel Lists Admin"
+msgstr "Kanal Listeleri Yöneticisi"
+
+#: channels/aggregate.php:58
+msgid "Channel Name"
+msgstr "Kanal Ýsmi"
+
+#: lib/Forms/Feed.php:77
+msgid ""
+"Channel URL for further pages, if not the default one. %c gets replaced by "
+"the feed ID, %n by the story offset."
+msgstr ""
+"Öntanýmlý olan deðilse, daha baþka sayfalar için kanalýn URLsi. %c, besleme "
+"IDsi %n haber göreli konumu tarafýndan yer deðiþtirilmiþtir."
+
+#: lib/Forms/Feed.php:112
+msgid ""
+"Channel URL if not the default one. %c gets replaced by the feed ID, %n by "
+"the story offset."
+msgstr ""
+"Öntanýmlý olan deðilse kanalýn URLsi. %c, besleme IDsi %n haber göreli "
+"konumu tarafýndan yer deðiþtirilmiþtir."
+
+#: lib/Forms/Feed.php:76
+#, php-format
+msgid "Channel URL if not the default one. %c gets replaced by the feed ID."
+msgstr ""
+"Öntanýmlý olan deðilse kanalýn URLsi. %c, besleme IDsi tarafýndan yer "
+"deðiþtirilmiþtir."
+
+#: channels/delete.php:75
+msgid "Channel has not been deleted."
+msgstr "Kanal silinmemiþtir."
+
+#: lib/News/sql.php:174
+#, php-format
+msgid "Channel id \"%s\" not found."
+msgstr "Kanal ID \"%s\" bulunamamýþtýr."
+
+#: stories/index.php:54
+msgid "Channel refreshed."
+msgstr "Kanal yenilenmiþtir."
+
+#: templates/stories/index.html:37
+msgid "Comments"
+msgstr "Yorumlar"
+
+#: config/templates.php.dist:42
+msgid "Compact"
+msgstr "Yoðun"
+
+#: lib/News.php:515 lib/Jonah.php:141
+msgid "Composite Feed"
+msgstr "Birleþik Besleme"
+
+#: lib/Forms/Feed.php:114
+msgid "Composite feeds"
+msgstr "Birleþik besleme"
+
+#: lib/News.php:358
+#, php-format
+msgid "Could not deliver to the %s distribution list. %s"
+msgstr "%s daðýtým listesine verilemiyor. %s"
+
+#: lib/Jonah.php:87
+#, php-format
+msgid "Could not open %s."
+msgstr "%s açýlamýyor."
+
+#: lib/Block/latest.php:49
+msgid "Count reads of the latest story when this block is displayed"
+msgstr "Bu blok gösterildiðinde son haberin kaç kere okunduðunu sayýlýr"
+
+#: lib/Block/story.php:49
+msgid "Count reads of this story when this block is displayed"
+msgstr "Bu blok gösterildiðinde bu haberin kaç kere okunduðunu sayýlýr"
+
+#: lists/index.php:110
+msgid "Current Recipients"
+msgstr "Tanýmlý Alýcýlar"
+
+#: stories/index.php:136 stories/results.php:116
+msgid "Date"
+msgstr "Tarih"
+
+#: channels/delete.php:50 channels/delete.php:61 stories/delete.php:51
+#: stories/delete.php:56
+msgid "Delete"
+msgstr "Sil"
+
+#: channels/delete.php:47
+#, php-format
+msgid "Delete News Channel \"%s\"?"
+msgstr "\"%s\" Haber Kanalý Silinsin mi?"
+
+#: stories/delete.php:47
+#, php-format
+msgid "Delete News Story \"%s\"?"
+msgstr "\"%s\" Haberi Silinsin mi?"
+
+#: channels/index.php:49
+msgid "Delete channel"
+msgstr "Kanal sil"
+
+#: lists/index.php:82
+msgid "Delete recipient"
+msgstr "Alýcý sil"
+
+#: stories/index.php:97 stories/results.php:94
+msgid "Delete story"
+msgstr "Haber sil"
+
+#: lists/edit.php:57 lists/edit.php:62
+msgid "Delivery"
+msgstr "Teslimat"
+
+#: channels/index.php:60
+msgid "Delivery lists"
+msgstr "Teslimat listeleri"
+
+#: lists/edit.php:50
+#, php-format
+msgid "Delivery of \"%s\" stories"
+msgstr "\"%s\" haberlerinin teslimatý"
+
+#: lib/Forms/Feed.php:75 lib/Forms/Feed.php:93 lib/Forms/Feed.php:111
+msgid "Description"
+msgstr "Açýklama"
+
+#: channels/delete.php:50 stories/delete.php:51
+msgid "Do not delete"
+msgstr "Sime"
+
+#: lib/Forms/Feed.php:40
+msgid "Edit Feed"
+msgstr "Besleme Düzenle"
+
+#: lib/Forms/Story.php:37
+msgid "Edit Story"
+msgstr "Haber Düzenle"
+
+#: lib/Forms/Feed.php:100
+msgid "Edit aggregated feeds"
+msgstr "Toplu beslemeleri düzenle"
+
+#: channels/index.php:44
+msgid "Edit channel"
+msgstr "Kanal düzenle"
+
+#: channels/aggregate.php:25
+#, php-format
+msgid "Edit channel \"%s\""
+msgstr "\"%s\" kanalý düzenle"
+
+#: stories/index.php:92 stories/results.php:87
+msgid "Edit story"
+msgstr "Haber düzenle"
+
+#: lib/Delivery/email.php:180 lib/Delivery/email.php:195
+msgid "Email"
+msgstr "Eposta"
+
+#: delivery/email.php:64
+#, php-format
+msgid "Email Delivery for \"%s\""
+msgstr "\"%s\" için eposta teslimi"
+
+#: lib/Block/delivery_email.php:96 delivery/email.php:78
+msgid "Email address"
+msgstr "Eposta adresi"
+
+#: lib/News.php:554
+#, php-format
+msgid "Error fetching feed: %s"
+msgstr "Besleme çekmede hata: %s"
+
+#: stories/pdf.php:15 stories/share.php:66 stories/view.php:25
+#: stories/view.php:35 lib/Block/latest.php:91 lib/Block/story.php:91
+#, php-format
+msgid "Error fetching story: %s"
+msgstr "Haber çekmede hata: %s"
+
+#: lib/News.php:793
+#, php-format
+msgid "Error parsing external feed from %s: %s"
+msgstr "%s'den beslemeyi düzenlemede hata: %s"
+
+#: lib/api.php:109
+msgid "External Channels"
+msgstr "Uzak Kanallar"
+
+#: lib/News.php:506 lib/Jonah.php:135
+msgid "External Feed"
+msgstr "Uzak Besleme"
+
+#: lib/Forms/Feed.php:51
+msgid "Extra information for this feed type"
+msgstr "Bu beleme türü için fazladan bilgi"
+
+#: lib/Block/news_popular.php:32 lib/Block/news.php:3 lib/Block/news.php:30
+#: lib/Block/story.php:42
+msgid "Feed"
+msgstr "Besleme"
+
+#: lib/News.php:113
+#, php-format
+msgid "Feed \"%s\" is not authored on this system."
+msgstr "Besleme \"%s\" bu sistemde yazlýmamýþtýr."
+
+#: channels/edit.php:56
+msgid "Feed type changed."
+msgstr "Besleme tipi deðiþti."
+
+#: channels/index.php:96 lib/Block/delivery.php:3 lib/Block/delivery.php:25
+msgid "Feeds"
+msgstr "Beslemeler"
+
+#: lib/Block/news.php:54
+msgid "First Story"
+msgstr "Ýlk Haber"
+
+#: stories/share.php:78
+msgid "From"
+msgstr "Kimden"
+
+#: lib/Forms/Story.php:74 lib/Forms/Story.php:76
+msgid "Full Story Text"
+msgstr "Haberin tam metni"
+
+#: delivery/html.php:39
+#, php-format
+msgid "HTML Delivery for \"%s\""
+msgstr "\"%s\" 'ýn HTML-daðýtýmý "
+
+#: lib/News.php:693
+msgid "HTML Version of Story"
+msgstr "Haber'in HTML-versiyonu"
+
+#: lib/Block/delivery.php:67
+#, php-format
+msgid "Have \"%s\" emailed to you"
+msgstr "\"%s\" size e-posta ile gönderildi"
+
+#: lib/Delivery/email.php:72 lib/Delivery/email.php:75
+msgid "Hello"
+msgstr "Merhaba"
+
+#: lib/Forms/Story.php:82
+msgid ""
+"If you enter a URL without a full story text, clicking on the story will "
+"send the reader straight to the URL, otherwise it will be shown at the end "
+"of the full story."
+msgstr ""
+"Tam haber metni olmadan bir URL eklerseniz, bu link'e týklamak okuyucuyu "
+"direk olarak bu URL'e yönlendirecek, aksi halde bu URL tam haber'in en "
+"sonunda gösterilecek."
+
+#: channels/aggregate.php:61 lib/Forms/Feed.php:87
+msgid "Image"
+msgstr "Resim"
+
+#: stories/share.php:86
+msgid "Include"
+msgstr "Ýçer"
+
+#: config/templates.php.dist:31
+msgid "Internal"
+msgstr "Ýç"
+
+#: lib/api.php:91
+msgid "Internal Channels"
+msgstr "Ýç Kanallar"
+
+#: stories/index.php:43 stories/results.php:49
+#, php-format
+msgid "Invalid channel requested. %s"
+msgstr "Geçersiz kanal isteði. %s"
+
+#: channels/delete.php:35
+msgid "Invalid channel specified for deletion."
+msgstr "Silmek için geçersiz kanal belirtildi."
+
+#: lists/edit.php:36 lists/delete.php:37 delivery/html.php:33
+#: delivery/email.php:55
+msgid "Invalid channel."
+msgstr "Geçersiz kanal."
+
+#: lists/delete.php:27
+msgid "Invalid request to delete recipient from delivery list."
+msgstr "Alýcýyý daðýtým listesinden silmek için geçersiz istek."
+
+#: delivery/email.php:69
+msgid "Join this channel"
+msgstr "Bu kanal'a katýl"
+
+#: channels/index.php:91
+msgid "Last Update"
+msgstr "Son güncelleme"
+
+#: lib/Block/latest.php:3 lib/Block/latest.php:65
+msgid "Latest News"
+msgstr "Son haberler"
+
+#: delivery/email.php:70
+msgid "Leave this channel"
+msgstr "Bu kanal'ý býrak"
+
+#: channels/aggregate.php:60 lib/Forms/Feed.php:86
+msgid "Link"
+msgstr "Baðlantý"
+
+#: stories/index.php:128
+msgid "Lists"
+msgstr "Listeler"
+
+#: lib/News.php:509 lib/Jonah.php:132
+msgid "Local Feed"
+msgstr "Yerel Besleme"
+
+#: channels/index.php:90
+msgid "Manage Feeds"
+msgstr "Beslemeleri Yönet"
+
+#: lib/Block/news_popular.php:53 lib/Block/news.php:49
+msgid "Maximum Stories"
+msgstr "Maksimum Haber"
+
+#: config/templates.php.dist:20
+msgid "Media"
+msgstr "Medya"
+
+#: lib/Block/tree_menu.php:3
+msgid "Menu List"
+msgstr "Menü listesi"
+
+#: stories/share.php:87
+msgid "Message"
+msgstr "Mesaj"
+
+#: lists/index.php:125
+msgid "Method"
+msgstr "Metod"
+
+#: lib/News.php:74
+msgid "Missing channel id."
+msgstr "Kanal numarasý bulunamadý."
+
+#: lib/Block/news_popular.php:3
+msgid "Most Popular Stories"
+msgstr "En Popüler Haberler "
+
+#: content.php:31 templates/content/header.inc:2
+msgid "My News"
+msgstr "Haberlerim"
+
+#: content_edit.php:32
+msgid "My News :: Add Content"
+msgstr "Haberlerim :: Ýçerik Ekle "
+
+#: lists/index.php:125 channels/index.php:91 lib/Delivery/email.php:197
+#: lib/Block/delivery_email.php:97 lib/Forms/Feed.php:50 delivery/email.php:83
+msgid "Name"
+msgstr "Ad"
+
+#: lib/Forms/Feed.php:40
+msgid "New Feed"
+msgstr "Yeni Besleme"
+
+#: stories/index.php:122 lib/Block/news_popular.php:86
+msgid "New Story"
+msgstr "Yeni Haber"
+
+#: lists/index.php:106
+msgid "New recipient"
+msgstr "Yeni Alýcý"
+
+#: lib/api.php:90 lib/api.php:108
+msgid "News"
+msgstr "Haberler"
+
+#: lib/Block/delivery_email.php:29
+msgid "News Channel"
+msgstr "Haber Kanalý"
+
+#: lib/Block/latest.php:33
+msgid "News Source"
+msgstr "Haber Kaynaðý"
+
+#: lib/Block/delivery_email.php:3
+msgid "News by Email"
+msgstr "E-posta aracýlýðý ile Haber"
+
+#: lists/index.php:40 channels/index.php:25
+msgid "News is not enabled."
+msgstr "Haberler aktif edilemedi."
+
+#: stories/index.php:51 stories/results.php:57
+msgid "No available stories."
+msgstr "Uygun Haber bulunamadý."
+
+#: lists/index.php:26 stories/index.php:23
+msgid "No channel requested."
+msgstr "Kanal isteði yok."
+
+#: lib/Block/latest.php:86
+msgid "No channel specified."
+msgstr "Kanal belirtilmedi."
+
+#: templates/channels/index.html:41
+msgid "No channels are available."
+msgstr "Hiçbir kanal eriþilebilir deðil."
+
+#: lib/Block/delivery_email.php:67
+msgid "No channels available."
+msgstr "Hiçbir kanal eriþilebilir deðil."
+
+#: lib/Block/news_popular.php:99 lib/Block/news.php:92
+msgid "No feed specified."
+msgstr "Hiçbir besleme belirtilmedi."
+
+#: lib/Block/delivery.php:76
+msgid "No feeds are available."
+msgstr "Hiçbir besleme eriþilebilir deðil."
+
+#: lists/index.php:79
+msgid "No recipients."
+msgstr "Alýcý yok."
+
+#: lib/News.php:584
+msgid "No stories are currently available."
+msgstr "Hiçbir haber þu an eriþilebilir deðil."
+
+#: lib/Block/story.php:86
+msgid "No story is selected."
+msgstr "Hiçbir haber seçilmedi."
+
+#: lib/Delivery.php:215
+#, php-format
+msgid "No such action \"%s\" found"
+msgstr "Hiçbir \"%s\" iþlemi bulunamadý"
+
+#: lib/News.php:743
+#, php-format
+msgid "No such backend \"%s\" found"
+msgstr "Hiçbir \"%s\" arka araç'ý bulunamadý"
+
+#: stories/results.php:40
+msgid "No tag requested."
+msgstr "Hiçbir etiket istenmedi."
+
+#: stories/delete.php:36
+msgid "No valid story requested for deletion."
+msgstr "Silmek için geçerli bir haber yok."
+
+#: stories/share.php:25
+msgid "Note"
+msgstr "Not"
+
+#: lib/Forms/Story.php:58
+msgid ""
+"Note: this story won't be delivered to distribution lists automatically if "
+"this time is in the future."
+msgstr ""
+"Uyarý: belirtilen zaman ileri bir tarihe ait ise bu haber daðýtým "
+"listelerine otomatik olarak daðýtýlmayacaktýr."
+
+#: lib/Forms/Story.php:55
+msgid "Or publish on this date:"
+msgstr "Ya da bu tarihte yayýnla:"
+
+#: stories/index.php:87 stories/results.php:81
+msgid "PDF version"
+msgstr "PDF versiyonu"
+
+#: channels/delete.php:22
+msgid "Permission Denied."
+msgstr "Ýzin verilmedi."
+
+#: lib/News.php:688
+msgid "Plaintext Version of Story"
+msgstr "Haberin basit metin versiyonu"
+
+#: lib/Forms/Story.php:45
+msgid "Publish Now?"
+msgstr "Þimdi yayýnla?"
+
+#: lib/Block/delivery.php:59
+#, php-format
+msgid "RSS Feed of \"%s\""
+msgstr "\"%s\" 'in RSS-beslemesi"
+
+#: templates/stories/index.html:32
+msgid "Read"
+msgstr "Oku"
+
+#: channels/delete.php:53
+msgid "Really delete this News Channel?"
+msgstr "Gerçekten bu kanal silinsin mi?"
+
+#: stories/delete.php:54
+msgid "Really delete this News Story?"
+msgstr "Gerçekten bu haber silinsin mi?"
+
+#: lists/index.php:125
+msgid "Recipient"
+msgstr "Alýcý"
+
+#: lists/edit.php:86
+msgid "Recipient saved."
+msgstr "Alýcý kaydedildi."
+
+#: lists/delete.php:52
+msgid "Recipient successfully removed."
+msgstr "Alýcý baþarý ile kaldýrýldý."
+
+#: stories/index.php:135
+msgid "Refresh Channel"
+msgstr "Kanal'ý yenile"
+
+#: channels/index.php:79
+msgid "Refresh channel"
+msgstr "Kanal'ý yenile"
+
+#: lists/delete.php:54
+msgid "Removal of recipient failed."
+msgstr "Alýcýnýn kaldýrýlmasý baþarýsýz."
+
+#: channels/aggregate.php:26
+#, php-format
+msgid "Remove channel \"%s\""
+msgstr "Kanal \"%s\"'ý kaldýr"
+
+#: delivery/email.php:44
+msgid "Request confirmed."
+msgstr "Ýstek onaylandý."
+
+#: lib/Delivery/email.php:69
+#, php-format
+msgid "Request to receive news channel \"%s\""
+msgstr "\"%s\" haber kanallarýný almak için istekte bulun"
+
+#: lib/Delivery/email.php:74
+#, php-format
+msgid "Request to stop receiving news channel \"%s\""
+msgstr "\"%s\" haber kanallarýnýn alýmýný durdurmak için istekte bulun"
+
+#: lib/Jonah.php:218
+msgid "Rich Text"
+msgstr "Rich Text formatý"
+
+#: lists/edit.php:52 lib/Block/delivery_email.php:82 lib/Forms/Story.php:39
+#: delivery/email.php:66
+msgid "Save"
+msgstr "Sakla"
+
+#: templates/delivery/html.html:13
+msgid "Select a format:"
+msgstr "Bir biçim seçiniz:"
+
+#: stories/share.php:75
+msgid "Send"
+msgstr "Gönder"
+
+#: stories/share.php:84
+msgid "Separate multiple email addresses with commas."
+msgstr "Trennen Sie mehrere Emailadressen durch Kommas."
+
+#: stories/share.php:73
+msgid "Share Story"
+msgstr "Haberi Paylaþ"
+
+#: stories/view.php:87
+msgid "Share this story"
+msgstr "Bu haberi paylaþ"
+
+#: lib/Forms/Story.php:44
+msgid "Short Description"
+msgstr "Kýsa Taným"
+
+#: channels/aggregate.php:59 lib/Forms/Feed.php:85
+msgid "Source URL"
+msgstr "Kaynak URL"
+
+#: lib/Forms/Feed.php:100
+msgid "Source URLs"
+msgstr "Kaynak URL ler"
+
+#: config/templates.php.dist:9
+msgid "Standard"
+msgstr "Standard"
+
+#: templates/stories/index.html:9
+msgid "Stories"
+msgstr "Haberler"
+
+#: stories/results.php:112 delivery/rss.php:58
+#, php-format
+msgid "Stories tagged with %s in %s"
+msgstr "%s ile iþaretlenmiþ %s içindeki haberler"
+
+#: stories/index.php:136 stories/results.php:116 lib/Block/story.php:3
+#: lib/Block/story.php:46 lib/Block/story.php:65
+msgid "Story"
+msgstr "Haber"
+
+#: lib/News.php:764
+#, php-format
+msgid "Story \"%s\" not found in \"%s\"."
+msgstr "Haber \"%s\", \"%s\" içerisinde bulunamadý."
+
+#: stories/share.php:109
+msgid "Story Link"
+msgstr "Haber Baðý"
+
+#: lib/Forms/Story.php:43
+msgid "Story Title (Headline)"
+msgstr "Haber Baþlýðý(Manþet)"
+
+#: lib/Forms/Story.php:82
+msgid "Story URL"
+msgstr "Haber URL i"
+
+#: lib/News/sql.php:456
+#, php-format
+msgid "Story URL \"%s\" not found."
+msgstr "Haber URL I \"%s\" bulunamadý."
+
+#: lib/Forms/Feed.php:78 lib/Forms/Feed.php:113
+#, php-format
+msgid ""
+"Story URL if not the default one. %c gets replaced by the feed ID, %s by the "
+"story ID."
+msgstr ""
+"Haber URL i -öntanýmlý olan deðilse- %c besleme numarasý ile, %s ise haber "
+"numarasý ile deðiþtirilir."
+
+#: lib/Forms/Story.php:61
+msgid "Story body type"
+msgstr "Haber gövde tipi"
+
+#: stories/edit.php:34
+#, php-format
+msgid "Story editing failed: %s"
+msgstr "Haber düzenleme baþarýsýz oldu: %s"
+
+#: stories/delete.php:71
+msgid "Story has not been deleted."
+msgstr "Haber silinmedi."
+
+#: lib/News/sql.php:424
+#, php-format
+msgid "Story id \"%s\" not found."
+msgstr "Haber numarasý \"%s\" bulunamadý."
+
+#: stories/share.php:85
+msgid "Subject"
+msgstr "Konu"
+
+#: lib/News.php:363
+#, php-format
+msgid "Successfully delivered to the %s distribution list."
+msgstr "%s daðýtým listesine baþarýyla iletildi."
+
+#: lib/Block/cloud.php:3 lib/Block/cloud.php:24
+msgid "Tag Cloud"
+msgstr "Ýþaret Bulutu"
+
+#: lib/News.php:834 lib/News.php:839 lib/News.php:844 lib/News.php:850
+#: lib/News.php:855
+msgid "Tag support not enabled in backend."
+msgstr "Arka araçta iþaretleme desteði faal faal deðil."
+
+#: lib/Forms/Story.php:79
+msgid "Tags"
+msgstr "Ýþaretler"
+
+#: lib/Block/story.php:105 templates/stories/story.html:6
+msgid "Tags: "
+msgstr "Ýþaretler: "
+
+#: lib/Jonah.php:225
+msgid "Text"
+msgstr "Metin"
+
+#: channels/aggregate.php:95
+#, php-format
+msgid "The channel \"%s\" has been removed."
+msgstr "Kanal \"%s\" silindi."
+
+#: channels/aggregate.php:76
+#, php-format
+msgid "The channel \"%s\" has been saved."
+msgstr "Kanal \"%s\" saklandý."
+
+#: channels/aggregate.php:84 channels/aggregate.php:102
+#, php-format
+msgid "The channel \"%s\" has been updated."
+msgstr "Kanal \"%s\" güncellendi."
+
+#: channels/delete.php:68
+msgid "The channel has been deleted."
+msgstr "Kanal silindi."
+
+#: stories/share.php:86
+msgid "The complete text of the story"
+msgstr "Haberin tam metni"
+
+#: channels/edit.php:70
+#, php-format
+msgid "The feed \"%s\" has been saved."
+msgstr "Besleme \"%s\" saklandý."
+
+#: lib/Forms/Feed.php:94
+msgid ""
+"The interval before stories aggregated into this feeds are rechecked for "
+"updates. If none, then stories will always be refetched from the sources."
+msgstr ""
+"Haberlerin, bu beslemede toplanmadan önce, güncellemeler için tekrar kontrol "
+"sýklýðý. Belirtilmezse, haberler her zaman kaynaktan yeniden çekilecektir."
+
+#: lib/Forms/Feed.php:83
+msgid ""
+"The interval before stories in this feed are rechecked for updates. If none, "
+"then stories will always be refetched from the source."
+msgstr ""
+"Bu beslemedeki haberlerin güncellemeler için tekrar kontrol sýklýðý."
+"Belirtilmezse, haberler her zaman kaynaktan yeniden çekilecektir."
+
+#: stories/edit.php:63
+#, php-format
+msgid "The story \"%s\" has been saved."
+msgstr "Haber \"%s\" saklandý."
+
+#: stories/delete.php:63
+msgid "The story has been deleted."
+msgstr "Haber silindi."
+
+#: stories/share.php:120
+msgid "The story was sent successfully."
+msgstr "Haber baþarýyla gönderildi."
+
+#: channels/aggregate.php:59 lib/Forms/Feed.php:85
+msgid ""
+"The url to use to fetch the stories, for example 'http://www.example.com/"
+"stories.rss'"
+msgstr ""
+"Haberleri çekmek için kullanýlacak url. Örneðin 'http://www.example.com/"
+"stories.rss'."
+
+#: channels/delete.php:66
+#, php-format
+msgid "There was an error deleting the channel: %s"
+msgstr "Kanalý silerken bir hata oluþtu: %s"
+
+#: stories/delete.php:61
+#, php-format
+msgid "There was an error deleting the story: %s"
+msgstr "Haberi silerken bir hata oluþtu: %s"
+
+#: channels/aggregate.php:93
+#, php-format
+msgid "There was an error removing the channel: %s"
+msgstr "Kanal silinirken bir hata oluþtur: %s"
+
+#: channels/aggregate.php:74
+#, php-format
+msgid "There was an error saving the channel: %s"
+msgstr "Kanal saklanýrken bir hata oluþtu: %s"
+
+#: channels/edit.php:68
+#, php-format
+msgid "There was an error saving the feed: %s"
+msgstr "Besleme saklanýrken bir hata oluþtu: %s"
+
+#: stories/edit.php:61
+#, php-format
+msgid "There was an error saving the story: %s"
+msgstr "Haberi saklarken bir hata oluþtu: %s"
+
+#: channels/aggregate.php:82 channels/aggregate.php:100
+#, php-format
+msgid "There was an error updating the channel: %s"
+msgstr "Kanal güncellenirken bir hata oluþtu: %s"
+
+#: lib/Delivery/email.php:181
+msgid ""
+"This driver allows the delivery of news stories via email to one or more "
+"recipients."
+msgstr ""
+"Bu sürücü, haberlerin bir ya da birden fazla alýcýya, eposta aracýlýðý ile "
+"ulaþtýrýlmasýný saðlar. "
+
+#: channels/aggregate.php:43
+msgid "This is no aggregated channel."
+msgstr "Toplanmýþ kanal yok."
+
+#: stories/share.php:84
+msgid "To"
+msgstr "Kime"
+
+#: channels/index.php:91 lib/Forms/Feed.php:45
+msgid "Type"
+msgstr "Tip"
+
+#: config/templates.php.dist:53
+msgid "Ultracompact"
+msgstr "Çok yoðun"
+
+#: delivery/email.php:42
+#, php-format
+msgid "Unable to confirm request."
+msgstr "Talep doðrulanamadý."
+
+#: stories/share.php:118
+#, php-format
+msgid "Unable to send story: %s"
+msgstr "Haber gönderilemedi: %s"
+
+#: channels/aggregate.php:107
+msgid "Update"
+msgstr "Güncelle"
+
+#: lib/Block/news_popular.php:45 lib/Block/news.php:41
+msgid "View"
+msgstr "Görünüm"
+
+#: delivery/email.php:67
+msgid "What do you want to do?"
+msgstr "Ne yapmak istiyorsunuz?"
+
+#: lists/index.php:34 lists/edit.php:44 lists/delete.php:45
+#: channels/aggregate.php:50 channels/index.php:19 channels/edit.php:43
+#: stories/index.php:31 stories/edit.php:42 stories/delete.php:22
+#: stories/results.php:24
+msgid "You are not authorised for this action."
+msgstr "Bu eylem için yetkili deðilsiniz."
+
+#: channels/edit.php:72
+msgid "You can now edit the sub-feeds."
+msgstr "Þimdi alt beslemeleri düzenleyebilirsiniz."
+
+#: lib/Delivery/email.php:72
+#, php-format
+msgid ""
+"You have requested to receive \"%s\" stories by email. Click on this link to "
+"confirm the request: %s"
+msgstr ""
+"\"%s\" haberlerini eposta ile almayý talep ettiniz. Talebi onaylamak için "
+"linki týklayýnýz: %s"
+
+#: lib/Delivery/email.php:75
+#, php-format
+msgid ""
+"You have requested to stop receiving \"%s\" stories by email. Click on this "
+"link to confirm the request: %s"
+msgstr ""
+"\"%s\" haberlerini eposta ile almayý durdurma talebinde bulundunuz. Talebi "
+"onaylamak için linki týklayýnýz: %s"
+
+#: lib/Delivery.php:272
+msgid "You must configure a Horde Datatree backend to use Jonah."
+msgstr ""
+"Haberleri kullanmak için bir Horde Veri Aðacý arka aracý yapýlandýrmalýsýnýz."
+
+#: lib/News.php:806
+msgid "[No title]"
+msgstr "[Baþlýksýz]"
+
+#: lib/Jonah.php:266
+msgid "_Feeds"
+msgstr "_Besleme"
+
+#: lib/Jonah.php:262
+msgid "_My News"
+msgstr "_Haberlerim"
+
+#: lib/Jonah.php:267
+msgid "_New Feed"
+msgstr "_Yeni Besleme"
+
+#: lib/News.php:472
+msgid "none"
+msgstr "hiçbiri"
--- /dev/null
+# Chinese translations for Jonah package.
+# Copyright 2003-2009 The Horde Project
+# This file is distributed under the same license as the Jonah package.
+# Automatically generated, 2003.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Jonah 0.0.3-cvs\n"
+"POT-Creation-Date: 2003-06-15 11:23+0800\n"
+"PO-Revision-Date: 2003-06-15 11:23+0800\n"
+"Last-Translator: <cwyeh@ccca.nctu.edu.tw>\n"
+"Language-Team: Traditional Chinese <i18n@lists.horde.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=BIG5\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: lib/Jonah.php:217
+#, c-format
+msgid " and the UV index is a %s. "
+msgstr " ¦P®Éµµ¥~½u«ü¼Æ¬O %s."
+
+#: lib/Block/metar.php:105
+#, c-format
+msgid " at %s %s"
+msgstr ""
+
+#: lib/Jonah.php:201
+msgid " km"
+msgstr "¤½¨½"
+
+#: lib/Jonah.php:176
+msgid " km/h"
+msgstr "¤½¨½/¤p®É"
+
+#: lib/Jonah.php:202
+msgid " miles"
+msgstr "^ù"
+
+#: lib/Jonah.php:156
+#, c-format
+msgid "%s at %s"
+msgstr ""
+
+#: newsfeed.php:41
+#, c-format
+msgid "'%s' stories in XML"
+msgstr "%s XML ®æ¦¡¤å³¹"
+
+#: lib/News.php:216
+msgid "1 hour"
+msgstr "1 ¤p®É"
+
+#: lib/News.php:220
+msgid "12 hours"
+msgstr "12 ¤p®É"
+
+#: lib/News.php:217
+msgid "2 hours"
+msgstr "2 ¤p®É"
+
+#: lib/News.php:221
+msgid "24 hours"
+msgstr "24 ¤p®É"
+
+#: lib/News.php:215
+msgid "30 mins"
+msgstr "30 ¤ÀÄÁ"
+
+#: lib/News.php:218
+msgid "4 hours"
+msgstr "4 ¤p®É"
+
+#: lib/Jonah.php:223
+msgid "5-Day Forecast"
+msgstr "5 ¤Ñ¹w³ø"
+
+#: stockquote.php:93
+msgid "52-week high"
+msgstr "52 ¶g°ªÂI"
+
+#: stockquote.php:97
+msgid "52-week low"
+msgstr "52 ¶g§CÂI"
+
+#: lib/News.php:219
+msgid "8 hours"
+msgstr "8 ¤p®É"
+
+#: lib/Form/EditChannel.php:33
+msgid "Add New Channel"
+msgstr "·s¼WÀW¹D"
+
+#: lib/Form/EditStory.php:31
+msgid "Add New Story"
+msgstr "·s¼W¤å³¹"
+
+#: lib/Form/DeleteChannel.php:37
+msgid "All stories created in this channel will be lost!"
+msgstr "©Ò¦³¥»ÀW¹D¤¤ªº¤å³¹±N·|¬y¥¢!"
+
+#: news/index.php:40
+#, c-format
+msgid "An error occurred fetching channels: %s"
+msgstr "§ì¨úÀW¹D¤º®e®Éµo¥Í¿ù»~: %s"
+
+#: lib/Form/DeleteChannel.php:39
+msgid "Any cached stories for this channel will be lost!"
+msgstr "©Ò¦³¥»ÀW¹Dªº¼È¦s¤å³¹±N·|¬y¥¢!"
+
+#: news/index.php:85
+msgid "Available Channels"
+msgstr "¥i¿ï¾ÜªºÀW¹D"
+
+#: newsfeed.php:28
+#, c-format
+msgid "Available Channels from %s (%s)"
+msgstr "¨Ó¦Û %s ªºÀW¹D (%s)"
+
+#: lib/Jonah.php:190
+#, c-format
+msgid "Barometer shows %s. "
+msgstr "®ðÀ£¬O %s. "
+
+#: lib/Form/EditChannel.php:58
+msgid "Caching"
+msgstr "§Ö¨ú"
+
+#: stockquote.php:57 templates/channels/column_headers.inc:13
+#: lib/Jonah.php:303
+msgid "Change"
+msgstr "×§ï"
+
+#: stockquote.php:71
+msgid "Change %"
+msgstr "º¦¶^´T %"
+
+#: config/prefs.php.dist:19
+msgid "Change the location for which weather is obtained."
+msgstr "×§ï¨ú±o®ð¶H¸ê®Æªº¦ì§}"
+
+#: config/prefs.php.dist:31
+msgid "Change your content display options."
+msgstr "×§ï§Aªº¤º®eÅã¥Ü¤è¦¡"
+
+#: news/stories.php:103
+#, c-format
+msgid "Channel '%s'"
+msgstr "ÀW¹D '%s'"
+
+#: lib/News.php:88
+#, c-format
+msgid "Channel '%s' is not an internally authored channel."
+msgstr "ÀW¹D '%s' ¨Ã¤£¬O¤º³¡³\¥iªºÀW¹D."
+
+#: content.php:15
+msgid "Channel Listing"
+msgstr "ÀW¹D¦Cªí"
+
+#: templates/channels/column_headers.inc:7 lib/Form/EditChannel.php:43
+msgid "Channel Name"
+msgstr "ÀW¹D¦WºÙ"
+
+#: channels.php:132
+msgid "Channel Subscriptions"
+msgstr "q¾\ÀW¹D"
+
+#: channels.php:125
+msgid "Channel display failed."
+msgstr "ÀW¹DÅã¥Ü¥¢±Ñ."
+
+#: news/deletechannel.php:68
+msgid "Channel has not been deleted."
+msgstr "ÀW¹D¨Ã¥¼³Q§R°£."
+
+#: lib/News/sql.php:165
+#, c-format
+msgid "Channel id '%s' not found."
+msgstr "§ä¤£¨ìÀW¹D¥N¸¹ '%s'"
+
+#: news/stories.php:56
+msgid "Channel refreshed."
+msgstr "ÀW¹D¤º®e¤w§ó·s."
+
+#: channels.php:85
+msgid "Channel subscription editing failed."
+msgstr "q¾\ÀW¹D½s¿è¥¢±Ñ."
+
+#: channels.php:41
+msgid "Channel subscription failed."
+msgstr "q¾\ÀW¹D¥¢±Ñ."
+
+#: news/editchannel.php:59
+msgid "Channel type changed."
+msgstr "ÀW¹D«¬ºA¤w§ïÅÜ."
+
+#: channels.php:72
+msgid "Channel unsubscription failed."
+msgstr "¨ú®øq¾\ÀW¹D¥¢±Ñ."
+
+#: lib/Jonah.php:303
+msgid "Chart"
+msgstr "¨«¶Õ¹Ï"
+
+#: config/prefs.php.dist:13
+msgid "Choose which news channels you are subscribed to."
+msgstr "¿ï¾Ü§An·sq¾\ªºÀW¹D."
+
+#: lib/Block/metar.php:103
+#, c-format
+msgid "Clouds: %s"
+msgstr ""
+
+#: config/templates.php.dist:36
+msgid "Compact"
+msgstr "²¼ä"
+
+#: lib/Weather/intercept.php:95
+msgid "Could not find XML parser handle."
+msgstr "µLªk§ä¨ì¥i¥H³B²zªº XML ¸ÑªR¾¹."
+
+#: lib/Stocks/nasdaq.php:98
+msgid "Could not find xml parser handle."
+msgstr "µLªk§ä¨ì¥i¥H³B²zªº XML ¸ÑªR¾¹."
+
+#: lib/Jonah.php:72
+#, c-format
+msgid "Could not open %s."
+msgstr "µLªk¶}±Ò %s."
+
+#: lib/api.php:57
+msgid "Current Phase"
+msgstr "¥Ø«e¬ÕÁ«"
+
+#: lib/api.php:68 lib/Block/metar.php:25
+msgid "Current Weather"
+msgstr "¥Ø«e¤Ñ®ð"
+
+#: lib/Block/metar.php:47 lib/Block/metar.php:49
+#, c-format
+msgid "Current Weather for: %s"
+msgstr "¥Ø«e¤Ñ®ð¦b: %s"
+
+#: news/deletechannel.php:52 news/deletestory.php:53
+#: lib/Form/DeleteChannel.php:32 lib/Form/DeleteStory.php:32
+msgid "Delete"
+msgstr "§R°£"
+
+#: lib/Form/DeleteChannel.php:29
+#, c-format
+msgid "Delete News Channel '%s'?"
+msgstr "§R°£·s»DÀW¹D '%s'?"
+
+#: lib/Form/DeleteStory.php:29
+#, c-format
+msgid "Delete News Story '%s'?"
+msgstr "§R°£·s»D¤å³¹ '%s'?"
+
+#: news/stories.php:82
+msgid "Delete story"
+msgstr "§R°£¤å³¹"
+
+#: lib/Form/EditChannel.php:54
+msgid "Description"
+msgstr "´yz"
+
+#: config/templates.php.dist:9
+msgid "Detailed"
+msgstr "²Ó¸`"
+
+#: config/prefs.php.dist:30
+msgid "Display Options"
+msgstr "Åã¥Ü¿ï¶µ"
+
+#: lib/Form/DeleteChannel.php:32 lib/Form/DeleteStory.php:32
+msgid "Do not delete"
+msgstr "¨ú®ø§R°£"
+
+#: stockquote.php:117
+msgid "Earnings"
+msgstr "¬Õ¾l"
+
+#: templates/channels/column_headers.inc:15
+#: templates/channels/subscribe.inc:56
+msgid "Edit"
+msgstr "½s¿è"
+
+#: lib/Form/EditChannel.php:33
+msgid "Edit Channel"
+msgstr "½s¿èÀW¹D"
+
+#: templates/channels/header.inc:3
+msgid "Edit Channel Subscriptions"
+msgstr "½s¿èÀW¹Dq¾\³]©w"
+
+#: stockquote.php:149
+msgid "Edit My Portfolio"
+msgstr "½s¿è§Úªº§ë¸ê²Õ¦X"
+
+#: lib/Form/EditStory.php:31
+msgid "Edit Story"
+msgstr "½s¿è¤å³¹"
+
+#: templates/channels/subscribe.inc:56
+msgid "Edit Subscription Details"
+msgstr "½s¿èq¾\¸ê®Æ²Ó¸`"
+
+#: newsfeed.php:35
+#, c-format
+msgid "Edit channel '%s'"
+msgstr "½s¿èÀW¹D '%s'"
+
+#: news/stories.php:77
+msgid "Edit story"
+msgstr "½s¿è¤å³¹"
+
+#: templates/channels/edit.inc:5
+#, c-format
+msgid "Editing Channel %s"
+msgstr "½s¿èÀW¹D %s"
+
+#: lib/Jonah.php:130
+msgid ""
+"Either no weather data is available for the location you have selected, or "
+"you have not selected a valid location."
+msgstr "¥i¯à¥Ø«e¨S¦³§A©Ò¿ï¾Ü¦ì¸mªº¤Ñ®ð¸ê®Æ, ©Î¬O§AÁÙ¨S¿ï¨ú¦³®Äªº¦ì¸m."
+
+#: templates/prefs/portfolio_select.inc:1
+msgid ""
+"Enter NASDAQ/AMEX/NYSE ticker symbols to watch, and the number of shares you "
+"own, if any:"
+msgstr "¿é¤Jn¬d¬Ýªº NASDAQ/AMEX/NYSE ªÑ²¼¥N¸¹, ¥H¤Î§A©Ò¾Ö¦³ªºªÑ²¼±i¼Æ:"
+
+#: lib/News.php:281
+#, c-format
+msgid "Error fetching channel. %s"
+msgstr "§ì¨úÀW¹Dµo¥Í¿ù»~. %s"
+
+#: newsfeed.php:58
+#, c-format
+msgid "Error fetching story: %s"
+msgstr "§ì¨ú¤å³¹µo¥Í¿ù»~: %s"
+
+#: lib/Jonah.php:383
+msgid "External"
+msgstr "¥~³¡ªº"
+
+#: lib/Form/EditChannel.php:44
+msgid "Extra information for this channel type"
+msgstr "¦¹ÃþÀW¹D«¬ºAªºÃB¥~¸ê°T"
+
+#: lib/Block/moon.php:143
+msgid "First Half"
+msgstr "¤W¥b¤ë"
+
+#: lib/Block/moon.php:53 lib/Block/moon.php:55
+msgid "First Quater"
+msgstr "¤W©¶¤ë"
+
+#: lib/Jonah.php:133
+msgid "Friday"
+msgstr "¶g¤"
+
+#: lib/Block/moon.php:61
+msgid "Full Moon"
+msgstr "º¡¤ë"
+
+#: lib/Form/EditStory.php:39
+msgid "Full Story Text"
+msgstr "¥þ³¡¤å³¹¤º®e"
+
+#: lib/Block/moon.php:153
+msgid "Full moon"
+msgstr "º¡¤ë"
+
+#: content.php:69 lib/api.php:83
+msgid "Headlines"
+msgstr "ÀY±ø®ø®§"
+
+#: templates/menu/menu.inc:24
+msgid "Help"
+msgstr "¨D§U"
+
+#: lib/api.php:59
+msgid "Hemisphere:"
+msgstr "«n¥_¥b²y:"
+
+#: config/prefs.php.dist:54
+msgid "How many columns would you like to use to show headlines?"
+msgstr "§An¨Ï¥Î´XÄæ¨ÓÅã¥ÜÀY±ø·s»D?"
+
+#: lib/Form/EditChannel.php:62
+msgid "Image"
+msgstr "¹Ï¥Ü"
+
+#: lib/Jonah.php:380
+msgid "Internal"
+msgstr "¤º³¡ªº"
+
+#: news/stories.php:46
+#, c-format
+msgid "Invalid channel requested. %s"
+msgstr "n¨DªºÀW¹D¬OµL®Äªº. %s"
+
+#: lib/Jonah.php:184
+#, c-format
+msgid "It feels like %s. "
+msgstr "·Pı¹³¬O %s."
+
+#: templates/index/notconfigured.inc:4
+msgid "Jonah is not properly configured"
+msgstr "Jonah ©|¥¼³]©w"
+
+#: config/prefs.php.dist:6
+msgid "Language"
+msgstr "»y¨¥"
+
+#: lib/Block/moon.php:146
+msgid "Last Half"
+msgstr "¤U¥b¤ë"
+
+#: lib/Block/moon.php:67 lib/Block/moon.php:69
+msgid "Last Quater"
+msgstr "¤U©¶¤ë"
+
+#: newsfeed.php:46 news/index.php:86
+msgid "Last Update"
+msgstr "¤W¦¸§ó·s"
+
+#: templates/channels/column_headers.inc:10
+msgid "Last Updated"
+msgstr "¤W¦¸§ó·s"
+
+#: lib/Block/metar.php:54
+#, c-format
+msgid "Last Updated: %s"
+msgstr "¤W¦¸§ó·s: %s"
+
+#: stockquote.php:121
+msgid "Last dividend"
+msgstr "¤W¦¸°£Åv"
+
+#: stockquote.php:126
+msgid "Last dividend date"
+msgstr "¤W¦¸°£Åv¤é´Á"
+
+#: stockquote.php:53
+msgid "Last price"
+msgstr "³Ìªñ¦¨¥æ»ù"
+
+#: stockquote.php:18
+#, c-format
+msgid "Latest Data for %s"
+msgstr "%s ³Ì·sªº¸ê®Æ"
+
+#: lib/api.php:41
+msgid "Latitude"
+msgstr "½n«×"
+
+#: lib/Form/EditChannel.php:61
+msgid "Link"
+msgstr "³sµ²"
+
+#: templates/index/notconfigured.inc:51
+msgid "List of METAR weather stations."
+msgstr "®ð¶H¯¸¦Cªí"
+
+#: lib/api.php:69
+msgid "Location"
+msgstr "¦ì¸m"
+
+#: stockquote.php:49
+msgid "Market Center"
+msgstr ""
+
+#: stockquote.php:139
+msgid "Market cap"
+msgstr "¸ê¥»ÃB"
+
+#: lib/News.php:52
+msgid "Missing channel id."
+msgstr "¿ò¥¢ÀW¹D¥N¸¹."
+
+#: lib/Jonah.php:133
+msgid "Monday"
+msgstr "¶g¤@"
+
+#: lib/api.php:52 lib/Block/moon.php:19
+msgid "Moon Phases"
+msgstr "¤ë«G¬ÕÁ«"
+
+#: templates/menu/menu.inc:6
+msgid "My Content"
+msgstr "§Úªº·s»D¤º®e"
+
+#: stockquote.php:18 stockquote.php:149
+msgid "My Portfolio"
+msgstr "§Úªº§ë¸ê²Õ¦X"
+
+#: newsfeed.php:46 stockquote.php:37 news/index.php:86 lib/Jonah.php:303
+msgid "Name"
+msgstr "¦WºÙ"
+
+#: lib/Block/moon.php:47
+msgid "New Moon"
+msgstr "·s¤ë"
+
+#: news/index.php:81
+msgid "New channel"
+msgstr "·sÀW¹D"
+
+#: lib/Block/moon.php:31 lib/Block/moon.php:151
+msgid "New moon"
+msgstr "·s¤ë"
+
+#: lib/Block/news.php:43 config/prefs.php.dist:12
+msgid "News"
+msgstr "·s»D"
+
+#: templates/menu/menu.inc:15
+msgid "News Admin"
+msgstr "·s»DºÞ²z"
+
+#: templates/menu/menu.inc:12
+msgid "News Feed"
+msgstr "·s»D¨Ó·½"
+
+#: news/index.php:28
+msgid "News is not enabled."
+msgstr "·s»D¥\¯à¨Ã¥¼¶}±Ò."
+
+#: lib/api.php:58
+msgid "Next Phase"
+msgstr ""
+
+#: newsfeed.php:43 news/index.php:75
+msgid "No Data"
+msgstr "¨S¦³¸ê®Æ"
+
+#: news/index.php:38
+msgid "No available channels."
+msgstr "¨S¦³ÀW¹D."
+
+#: news/stories.php:53
+msgid "No available stories."
+msgstr "¨S¦³¤å³¹."
+
+#: news/stories.php:31
+msgid "No channel requested."
+msgstr "¨S¦³n¨DªºÀW¹D."
+
+#: lib/Block/news.php:53
+msgid "No channel specified."
+msgstr "¨S¦³«ü©wªºÀW¹D."
+
+#: lib/News/sql.php:319
+msgid "No configuration information specified for SQL News Stories."
+msgstr "¨S¦³ SQL ·s»D¤å³¹ªº³]©w¸ê®Æ."
+
+#: lib/Weather/sql.php:201
+msgid "No configuration information specified for SQL Preferences."
+msgstr "¨S¦³ SQL °¾¦nªº³]©w¸ê®Æ."
+
+#: lib/Block/sunrise.php:29
+msgid "No location is set."
+msgstr "¦ì¸m¸ê°T©|¥¼³]©w"
+
+#: lib/News.php:285
+msgid "No stories currently available."
+msgstr "¥Ø«e¨S¦³¤å³¹."
+
+#: lib/News.php:326 lib/Stocks.php:137
+#, c-format
+msgid "No such backend '%s' found"
+msgstr "¨S¦³¦¹«áºÝ¥¥x '%s'"
+
+#: news/deletechannel.php:39
+msgid "No valid channel requested for deletion."
+msgstr "¨S¦³¥¿½TªºÀW¹D¥i¨Ñ§R°£."
+
+#: news/deletestory.php:40
+msgid "No valid story requested for deletion."
+msgstr "¨S¦³¥¿½Tªº¤å³¹¥i¨Ñ§R°£."
+
+#: lib/api.php:62
+msgid "Northern Hemisphere"
+msgstr "¥_¥b²y"
+
+#: templates/menu/menu.inc:19
+msgid "Options"
+msgstr "¿ï¶µ"
+
+#: config/prefs.php.dist:29
+msgid "Other Options"
+msgstr "¨ä¥L¿ï¶µ"
+
+#: stockquote.php:109
+msgid "P-E ratio"
+msgstr "¥»¯q¤ñ"
+
+#: templates/prefs/setlocation.inc:27
+msgid "Please select a Country/State..."
+msgstr "½Ð¿ï¾Ü°ê®a/¬Ù¥÷..."
+
+#: templates/prefs/setlocation.inc:40
+msgid "Please select a station..."
+msgstr "½Ð¿ï¾Ü¤@Ó®ð¶H¯¸..."
+
+#: templates/menu/menu.inc:8
+msgid "Portfolio"
+msgstr "§ë¸ê²Õ¦X"
+
+#: lib/Block/metar.php:94
+#, c-format
+msgid "Pressure: %s%s"
+msgstr "®ðÀ£: %s%s"
+
+#: stockquote.php:101
+msgid "Previous close"
+msgstr "¬Q¦¬"
+
+#: lib/Jonah.php:303
+msgid "Price"
+msgstr "ªÑ»ù"
+
+#: stockquote.php:135
+msgid "Quote date"
+msgstr "³ø»ù¤é´Á"
+
+#: lib/Jonah.php:122 lib/Jonah.php:125
+msgid "Radar"
+msgstr "½Ã¬P¶³¹Ï"
+
+#: lib/Form/DeleteChannel.php:35
+msgid "Really delete this News Channel?"
+msgstr "½T©w§R°£¦¹·s»DÀW¹D?"
+
+#: lib/Form/DeleteStory.php:35
+msgid "Really delete this News Story?"
+msgstr "½T©w§R°£¦¹·s»D¤å³¹?"
+
+#: lib/News/sql.php:337
+msgid "Required 'charset' not specified in news stories configuration."
+msgstr "¥²¶ñÄæ¦ì 'charset' ¨Ã¥¼¦b·s»D¤å³¹³]©w¤¤«ü©w."
+
+#: lib/News/sql.php:334
+msgid "Required 'database' not specified in news stories configuration."
+msgstr "¥²¶ñÄæ¦ì 'database' ¨Ã¥¼¦b·s»D¤å³¹³]©w¤¤«ü©w."
+
+#: lib/Weather/sql.php:216
+msgid "Required 'database' not specified in preferences configuration."
+msgstr "¥²¶ñÄæ¦ì '¸ê®Æ®w' ¨Ã¥¼¦b°¾¦n³]©w¤¤«ü©w."
+
+#: lib/News/sql.php:325
+msgid "Required 'hostspec' not specified in news stories configuration."
+msgstr "¥²¶ñÄæ¦ì 'hostspec' ¨Ã¥¼¦b·s»D¤å³¹³]©w¤¤«ü©w."
+
+#: lib/Weather/sql.php:207
+msgid "Required 'hostspec' not specified in preferences configuration."
+msgstr "¥²¶ñÄæ¦ì 'hostspec' ¨Ã¥¼¦b°¾¦n³]©w¤¤«ü©w."
+
+#: lib/News/sql.php:331
+msgid "Required 'password' not specified in news stories configuration."
+msgstr "¥²¶ñÄæ¦ì 'password' ¨Ã¥¼¦b·s»D¤å³¹³]©w¤¤«ü©w."
+
+#: lib/Weather/sql.php:213
+msgid "Required 'password' not specified in preferences configuration."
+msgstr "¥²¶ñÄæ¦ì 'password' ¨Ã¥¼¦b°¾¦n³]©w¤¤«ü©w."
+
+#: lib/News/sql.php:322
+msgid "Required 'phptype' not specified in news stories configuration."
+msgstr "¥²¶ñÄæ¦ì 'phptype' ¨Ã¥¼¦b·s»D¤å³¹³]©w¤¤«ü©w."
+
+#: lib/Weather/sql.php:204
+msgid "Required 'phptype' not specified in preferences configuration."
+msgstr "¥²¶ñÄæ¦ì 'phptype' ¨Ã¥¼¦b°¾¦n³]©w¤¤«ü©w."
+
+#: lib/News/sql.php:328
+msgid "Required 'username' not specified in news stories configuration."
+msgstr "¥²¶ñÄæ¦ì 'username' ¨Ã¥¼¦b·s»D¤å³¹³]©w¤¤«ü©w."
+
+#: lib/Weather/sql.php:210
+msgid "Required 'username' not specified in preferences configuration."
+msgstr "¥²¶ñÄæ¦ì 'username' ¨Ã¥¼¦b·s»D¤å³¹³]©w¤¤«ü©w."
+
+#: stockquote.php:131
+msgid "S&P 500 beta"
+msgstr ""
+
+#: lib/Jonah.php:133
+msgid "Saturday"
+msgstr "¶g¤»"
+
+#: lib/Form/EditStory.php:33
+msgid "Save"
+msgstr "Àx¦s"
+
+#: config/prefs.php.dist:40
+msgid "Select your preferred language:"
+msgstr "¿ï¾Ü§Aªº°¾¦n»y¨¥: "
+
+#: config/prefs.php.dist:7
+msgid "Set the your preferred display language."
+msgstr "¿ï¾Ü°¾¦nÅã¥Ü»y¨t."
+
+#: config/prefs.php.dist:25
+msgid "Set which stock ticker symbols to watch."
+msgstr "³]©wn°lÂܪºªÑ²¼¥N½X."
+
+#: templates/prefs/portfolio_select.inc:6
+msgid "Shares"
+msgstr "ªÑ¼Æ"
+
+#: lib/Form/EditStory.php:37
+msgid "Short Description"
+msgstr "²µu´yz"
+
+#: config/prefs.php.dist:83
+msgid "Should measurements be shown in metric values?"
+msgstr "n¥Î¤Q¶i¦ì¨î¨ÓÅã¥Ü¸ê®Æ¶Ü?"
+
+#: config/prefs.php.dist:117
+msgid "Show Dow Jones Industrial Average?"
+msgstr "Åã¥Ü¹Dã¤u·~«ü¼Æ?"
+
+#: config/prefs.php.dist:133
+msgid "Show Nasdaq Composite Index?"
+msgstr "Åã¥Ü Nasdaq «ü¼Æ?"
+
+#: config/prefs.php.dist:125
+msgid "Show S&P 500?"
+msgstr "Åã¥Ü S&P 500 «ü¼Æ?"
+
+#: config/prefs.php.dist:69
+msgid "Show headlines in main content display?"
+msgstr "¦b¥Dn¤º®e¤¤Åã¥ÜÀY±ø®ø®§?"
+
+#: config/prefs.php.dist:76
+msgid "Show stock quotes in main content display?"
+msgstr "¦b¥Dn¤º®e¤¤Åã¥ÜªÑ¥«¦æ±¡?"
+
+#: config/prefs.php.dist:62
+msgid "Show weather in main content display?"
+msgstr "¦b¥Dn¤º®e¤¤Åã¥Ü¤Ñ®ð¸ê°T?"
+
+#: templates/index/notconfigured.inc:39
+msgid "Some of Jonah's configuration files are missing:"
+msgstr "³¡¤Àªº Jonah ³]©wÀɮ׿ò¥¢:"
+
+#: lib/api.php:84
+msgid "Source"
+msgstr "®ø®§¨Ó·½"
+
+#: lib/Form/EditChannel.php:60
+msgid "Source url"
+msgstr "®ø®§¨Ó·½ url"
+
+#: lib/api.php:63
+msgid "Southern Hemisphere"
+msgstr "«n¥b²y"
+
+#: config/templates.php.dist:24
+msgid "Standard"
+msgstr "¼Ð·Ç"
+
+#: content.php:30 lib/api.php:102 lib/Block/stocks.php:23
+msgid "Stock Quotes"
+msgstr "ªÑ»ù"
+
+#: config/prefs.php.dist:24
+msgid "Stocks"
+msgstr "ªÑ²¼"
+
+#: news/stories.php:104
+msgid "Story"
+msgstr "¤å³¹"
+
+#: lib/Form/EditStory.php:36
+msgid "Story Title (Headline)"
+msgstr "¤å³¹¼ÐÃD (ÀY±ø)"
+
+#: lib/Form/EditStory.php:40
+msgid "Story URL"
+msgstr "¤å³¹ºô§}"
+
+#: news/editstory.php:40
+#, c-format
+msgid "Story editing failed. %s"
+msgstr "½s¿è¤å³¹¥¢±Ñ. %s"
+
+#: news/deletestory.php:69
+msgid "Story has not been deleted."
+msgstr "¤å³¹©|¥¼³Q§R°£."
+
+#: lib/News/sql.php:286
+#, c-format
+msgid "Story id '%s' not found."
+msgstr "§ä¤£¨ì¤å³¹½s¸¹ '%s'"
+
+#: channels.php:120 templates/channels/subscribe.inc:47
+msgid "Subscribe"
+msgstr "q¾\"
+
+#: content.php:88
+#, c-format
+msgid ""
+"Subscribed channel '%s' is no longer available, it will be removed from your "
+"subscriptions."
+msgstr "q¾\ªºÀW¹D '%s' ¤w¤£¦A¦s¦b, ¨Ã±N±q§Aªºq¾\¸ê®Æ¤¤²¾°£."
+
+#: lib/Block/sunrise.php:42
+msgid "Sun Rise"
+msgstr "¤é¥X"
+
+#: lib/api.php:40
+msgid "Sun Rise / Set"
+msgstr "¤é¥X/¸¨"
+
+#: lib/Block/sunrise.php:23
+msgid "Sun Rise and Set"
+msgstr "¤é¥X»P¤é¸¨"
+
+#: lib/Block/sunrise.php:47
+msgid "Sun Set"
+msgstr "¤é¸¨"
+
+#: lib/Jonah.php:133
+msgid "Sunday"
+msgstr "¶g¤é"
+
+#: templates/prefs/portfolio_select.inc:5 lib/Jonah.php:303
+msgid "Symbol"
+msgstr "ªÑ²¼¥N¸¹"
+
+#: lib/Block/metar.php:86
+#, c-format
+msgid "Temperature: %s; Dewpoint: %s"
+msgstr "·Å«×: %s; Àã«×: %s"
+
+#: lib/Jonah.php:217
+#, c-format
+msgid "The UV index is a %s. "
+msgstr "µµ¥~½u«ü¼Æ¬O %s."
+
+#: news/editchannel.php:75
+#, c-format
+msgid "The channel '%s' has been saved."
+msgstr "ÀW¹D '%s' ¤w¸gÀx¦s."
+
+#: news/deletechannel.php:61
+msgid "The channel has been deleted."
+msgstr "ÀW¹D '%s' ¤w¸g§R°£."
+
+#: lib/Form/EditChannel.php:58
+msgid ""
+"The interval before stories in this channel are rechecked for updates. If "
+"none, then stories will always be refetched from the source."
+msgstr ""
+
+#: news/editstory.php:68
+#, c-format
+msgid "The story '%s' has been saved."
+msgstr "¤å³¹ '%s' ¤w¸gÀx¦s."
+
+#: news/deletestory.php:62
+msgid "The story has been deleted."
+msgstr "¤å³¹¤w¸g§R°£."
+
+#: lib/Jonah.php:160
+#, c-format
+msgid "The temperature is <b>%s</b>, "
+msgstr "·Å«×¬O <b>%s</b>,"
+
+#: lib/Form/EditChannel.php:60
+msgid ""
+"The url to use to fetch the stories, for example 'http://www.example.com/"
+"stories.rss'"
+msgstr "¥Î¨Ó§ì¨ú¤å³¹ªººô§}, ¨Ò¦p 'http://www.example.com/stories.rss'"
+
+#: news/deletechannel.php:59
+#, c-format
+msgid "There was an error deleting the channel. %s"
+msgstr "§R°£ÀW¹D®Éµo¥Í¿ù»~: %s"
+
+#: news/deletestory.php:60
+#, c-format
+msgid "There was an error deleting the story. %s"
+msgstr "§R°£¤å³¹®Éµo¥Í¿ù»~. %s"
+
+#: news/editchannel.php:73
+#, c-format
+msgid "There was an error saving the channel. %s"
+msgstr "Àx¦sÀW¹D®Éµo¥Í¿ù»~. %s"
+
+#: news/editstory.php:66
+#, c-format
+msgid "There was an error saving the story. %s"
+msgstr "Àx¦s¤å³¹®Éµo¥Í¿ù»~. %s"
+
+#: templates/index/notconfigured.inc:65
+msgid "This file contains preferences for Jonah."
+msgstr "¦¹ÀÉ®×¥]§t Jonah ªº°¾¦n³]©w."
+
+#: templates/index/notconfigured.inc:58
+msgid ""
+"This file defines the HTML (or other) templates that are used to generate "
+"different views of the news channels that Jonah provides."
+msgstr ""
+
+#: templates/index/notconfigured.inc:44
+msgid ""
+"This is the main Jonah configuration file. It contains options for all Jonah "
+"scripts."
+msgstr ""
+
+#: lib/Jonah.php:133
+msgid "Thursday"
+msgstr "¶g¥|"
+
+#: stockquote.php:85
+msgid "Today's high"
+msgstr "¥»¤é³Ì°ª"
+
+#: stockquote.php:89
+msgid "Today's low"
+msgstr "¥»¤é³Ì§C"
+
+#: lib/Jonah.php:133
+msgid "Tuesday"
+msgstr "¶g¤G"
+
+#: news/index.php:86 lib/Form/EditChannel.php:38
+msgid "Type"
+msgstr "«¬ºA"
+
+#: config/templates.php.dist:48
+msgid "Ultracompact"
+msgstr "·¥Â²¼ä"
+
+#: channels.php:116 templates/channels/subscribe.inc:43
+msgid "Unsubscribe"
+msgstr "¨ú®øq¾\"
+
+#: news/stories.php:104
+msgid "Updated"
+msgstr "¤w§ó·s"
+
+#: prefs.php:74
+msgid "User Options"
+msgstr "¨Ï¥ÎªÌ¿ï¶µ"
+
+#: lib/Jonah.php:303
+msgid "Value"
+msgstr ""
+
+#: templates/channels/edit.inc:9 lib/api.php:93
+msgid "View"
+msgstr "À˵ø"
+
+#: lib/Jonah.php:323 lib/Jonah.php:326
+msgid "View Chart"
+msgstr "À˵ø¨«¶Õ¹Ï"
+
+#: lib/Jonah.php:330
+msgid "View Quote Details"
+msgstr "À˵øªÑ»ù²Ó¸`"
+
+#: lib/Jonah.php:197
+#, c-format
+msgid "Visibility is %s"
+msgstr "¯à¨£«×¬O %s"
+
+#: lib/Block/metar.php:78
+#, c-format
+msgid "Visibility: %s%s"
+msgstr "¯à¨£«×: %s%s"
+
+#: stockquote.php:105
+msgid "Volume"
+msgstr "¦¨¥æ¶q"
+
+#: content.php:27
+msgid "Weather"
+msgstr "¤Ñ®ð"
+
+#: lib/api.php:106 lib/Block/weather.php:23
+msgid "Weather Forecast"
+msgstr "¤Ñ®ð¹w´ú"
+
+#: config/prefs.php.dist:18
+msgid "Weather Station"
+msgstr "®ð¶H¯¸"
+
+#: templates/prefs/setlocation.inc:4 templates/prefs/setlocation.inc:60
+msgid "Weather station:"
+msgstr "®ð¶H¯¸:"
+
+#: lib/Block/metar.php:115
+msgid "Weather:"
+msgstr "¤Ñ®ð:"
+
+#: lib/Jonah.php:133
+msgid "Wednesday"
+msgstr "¶g¤T"
+
+#: lib/api.php:54
+msgid "Which phases?"
+msgstr ""
+
+#: lib/Block/metar.php:61
+#, c-format
+msgid "Wind: %s%s from %s, gusting to %s%s."
+msgstr "·¤O: %s%s ¨Ó¦Û %s, °}· %s%s."
+
+#: lib/Block/metar.php:68
+#, c-format
+msgid "Wind: %s%s from %s."
+msgstr "·¤O: %s%s ¨Ó¦Û %s."
+
+#: lib/Weather/intercept.php:102 lib/Stocks/nasdaq.php:102
+#, c-format
+msgid "XML error: %s at line %d"
+msgstr ""
+
+#: stockquote.php:113
+msgid "Yield %"
+msgstr ""
+
+#: lib/Form/EditStory.php:47
+msgid "You MUST enter either a URL or a full story."
+msgstr "§A¥²¶·¿é¤J¤@Óºô§}©Î¬O§¹¾ãªº¤å³¹."
+
+#: news/deletechannel.php:25 news/deletestory.php:25 news/editchannel.php:25
+#: news/editstory.php:25 news/index.php:22 news/stories.php:22
+msgid "You are not authorised for this action."
+msgstr "¥¼³Q±ÂÅv°õ¦æ¦¹°Ê§@."
+
+#: channels.php:83
+#, c-format
+msgid "You have edited your subscription to the channel %s."
+msgstr "§A¤w¸g½s¿èÀW¹D %s ªºq¾\¸ê®Æ."
+
+#: content.php:77
+msgid "You have no news subscriptions."
+msgstr "§A©|¥¼q¾\¥ô¦ó·s»D."
+
+#: lib/Block/stocks.php:30
+msgid "You have no ticker symbols in your portfolio at this time."
+msgstr "§A¥Ø«eªº§ë¸ê²Õ¦X¤¤¨S¦³ªÑ²¼¥N½X."
+
+#: content.php:44 stockquote.php:146
+msgid "You have no ticker symbols in your portfolio."
+msgstr "§Aªº§ë¸ê²Õ¦X¤¤¨S¦³ªÑ²¼¥N½X."
+
+#: channels.php:39
+#, c-format
+msgid "You have subscribed to the channel '%s'."
+msgstr "§A¤wq¾\ÀW¹D '%s'."
+
+#: channels.php:61
+#, c-format
+msgid "You have unsubscribed from the channel '%s'."
+msgstr "§A¤w¨ú®øq¾\ÀW¹D '%s'"
+
+#: lib/Form/EditStory.php:38
+msgid "You must enter either a URL or a full story."
+msgstr "§A¥²¶·¿é¤Jºô§}©Î¬O§¹¾ãªº¤å³¹."
+
+#: config/prefs.php.dist:11 config/prefs.php.dist:17 config/prefs.php.dist:23
+msgid "Your Content"
+msgstr "¤º®e"
+
+#: config/prefs.php.dist:5
+msgid "Your Information"
+msgstr "Ó¤H¸ê°T"
+
+#: lib/Jonah.php:173
+#, c-format
+msgid "and the wind is %s at %s. "
+msgstr "¦P®É·¤O¬O %s at %s."
+
+#: lib/Jonah.php:179
+#, c-format
+msgid "and the wind is %s. "
+msgstr "¦P®É·¤O¬O %s."
+
+#: lib/Jonah.php:170
+msgid "and there is no wind measured. "
+msgstr "¦P®É¥Ø«e¨S¦³·¤O¸ê°T."
+
+#: news/stories.php:94
+msgid "channels"
+msgstr "ÀW¹D"
+
+#: lib/Jonah.php:137
+msgid "cloudy"
+msgstr "¦h¶³"
+
+#: lib/Jonah.php:137
+msgid "cold"
+msgstr "´H§N"
+
+#: lib/Jonah.php:136
+msgid "dust"
+msgstr "¹Ð"
+
+#: lib/News.php:248
+msgid "external"
+msgstr "¥~³¡ªº"
+
+#: lib/Jonah.php:136
+msgid "flurries"
+msgstr "°}«B"
+
+#: lib/Jonah.php:136
+msgid "fog"
+msgstr "¦³Ãú"
+
+#: lib/Jonah.php:137
+msgid "haze"
+msgstr "·ÏÃú°gº©"
+
+#: lib/Jonah.php:135
+msgid "heavy rain"
+msgstr "»¨«B"
+
+#: lib/Jonah.php:213
+#, c-format
+msgid "high %s. Use sunscreen"
+msgstr "°ª¶q¯Å %s. ½Ðª`·N¨¾ÅÎ"
+
+#: lib/Jonah.php:138
+msgid "hot"
+msgstr "ª¢¼ö"
+
+#: lib/Jonah.php:166
+#, c-format
+msgid "humidity is at %s"
+msgstr "·Ã«×¬O %s"
+
+#: lib/Jonah.php:199
+msgid "infinite"
+msgstr "µLªýꪺ"
+
+#: lib/News.php:251
+msgid "internal"
+msgstr "¤º³¡ªº"
+
+#: lib/Jonah.php:135
+msgid "light flurries"
+msgstr "°¸°}«B"
+
+#: lib/Jonah.php:209
+#, c-format
+msgid "low %s"
+msgstr "§C %s"
+
+#: lib/Jonah.php:207
+#, c-format
+msgid "minimal %s"
+msgstr "§C¶q¯Å %s"
+
+#: lib/Jonah.php:211
+#, c-format
+msgid "moderate %s"
+msgstr "¾A¶q¯Å %s"
+
+#: lib/Jonah.php:137
+msgid "mostly cloudy"
+msgstr "®É³±"
+
+#: lib/Jonah.php:138
+msgid "mostly sunny"
+msgstr "®É´¸"
+
+#: lib/Jonah.php:177
+msgid "mph"
+msgstr ""
+
+#: news/stories.php:98
+msgid "new story"
+msgstr "·s¤å³¹"
+
+#: lib/News.php:214
+msgid "none"
+msgstr ""
+
+#: lib/Jonah.php:138
+msgid "partly cloudy"
+msgstr "¦h¶³"
+
+#: lib/Jonah.php:134
+msgid "rain"
+msgstr "«B"
+
+#: lib/Jonah.php:135
+msgid "sleet"
+msgstr "¦B¹r"
+
+#: lib/Jonah.php:137
+msgid "smoke"
+msgstr "·ÏÃú"
+
+#: lib/Jonah.php:134
+msgid "snow"
+msgstr "¦³³·"
+
+#: lib/Jonah.php:136
+msgid "snow and wind"
+msgstr "·³·"
+
+#: lib/Jonah.php:138
+msgid "sunny"
+msgstr "´¸®Ô"
+
+#: lib/Jonah.php:134
+msgid "thunder storms"
+msgstr "¼É·³·"
+
+#: lib/Jonah.php:215
+#, c-format
+msgid "very high %s. Stay indoors"
+msgstr "·¥°ª %s. Á×§K¥Xªù"
+
+#: lib/Jonah.php:134
+msgid "windy"
+msgstr "±j·"
--- /dev/null
+Deny from all
--- /dev/null
+#!/usr/bin/php -q
+<?php
+/**
+ * $Horde: jonah/scripts/feed_tester.php,v 1.7 2009/07/09 06:08:48 slusarz Exp $
+ *
+ * See the enclosed file LICENSE for license information (BSD). If you
+ * did not receive this file, see http://cvs.horde.org/co.php/jonah/LICENSE.
+ */
+
+// Find the base file path of Horde.
+@define('HORDE_BASE', dirname(__FILE__) . '/../..');
+
+// Find the base file path of Jonah.
+@define('JONAH_BASE', dirname(__FILE__) . '/..');
+
+// Do CLI checks and environment setup first.
+require_once HORDE_BASE . '/lib/core.php';
+
+// Make sure no one runs this from the web.
+if (!Horde_Cli::runningFromCLI()) {
+ exit("Must be run from the command line\n");
+}
+
+// Load the CLI environment - make sure there's no time limit, init
+// some variables, etc.
+Horde_Cli::init();
+$cli = &Horde_Cli::singleton();
+
+// Now load the Registry and setup conf, etc.
+$registry = Horde_Registry::singleton();
+$registry->pushApp('jonah', false);
+
+// Include needed libraries.
+require_once JONAH_BASE . '/lib/Jonah.php';
+require_once JONAH_BASE . '/lib/FeedParser.php';
+
+/* Make sure there's no compression. */
+@ob_end_clean();
+
+if (empty($argv[1]) || !file_exists($argv[1])) {
+ exit("Need a valid filename.\n");
+}
+
+$data = file_get_contents($argv[1]);
+
+if (preg_match('/.*;\s?charset="?([^"]*)/', 'text/xml', $match)) {
+ $charset = $match[1];
+} elseif (preg_match('/<\?xml[^>]+encoding=["\']?([^"\'\s?]+)[^?].*?>/i', $data, $match)) {
+ $charset = $match[1];
+} else {
+ $charset = 'utf-8';
+}
+
+$parser = new Jonah_FeedParser($charset);
+if (!$parser->parse($data)) {
+ $cli->writeln($cli->red(_("Parse failed:")));
+ var_dump($parser->error);
+} else {
+ $cli->writeln($cli->green(_("Parse succeeded, structure is:")));
+ var_dump($parser->structure);
+}
--- /dev/null
+-- $Horde: jonah/scripts/sql/jonah.mssql.sql,v 1.9 2009/10/20 21:28:29 jan Exp $
+
+CREATE TABLE jonah_channels (
+ channel_id INT NOT NULL,
+ channel_slug VARCHAR(64) NOT NULL,
+ channel_name VARCHAR(255) NOT NULL,
+ channel_type SMALLINT NOT NULL,
+ channel_full_feed SMALLINT DEFAULT 0 NOT NULL,
+ channel_desc VARCHAR(255),
+ channel_interval INT,
+ channel_url VARCHAR(255),
+ channel_link VARCHAR(255),
+ channel_page_link VARCHAR(255),
+ channel_story_url VARCHAR(255),
+ channel_img VARCHAR(255),
+ channel_updated INT,
+--
+ PRIMARY KEY (channel_id)
+);
+
+CREATE TABLE jonah_stories (
+ story_id INT NOT NULL,
+ channel_id INT NOT NULL,
+ story_author VARCHAR(255) NOT NULL,
+ story_title VARCHAR(255) NOT NULL,
+ story_desc VARCHAR(MAX),
+ story_body_type VARCHAR(255) NOT NULL,
+ story_body VARCHAR(MAX),
+ story_url VARCHAR(255),
+ story_permalink VARCHAR(255),
+ story_published INT,
+ story_updated INT NOT NULL,
+ story_read INT NOT NULL,
+--
+ PRIMARY KEY (story_id)
+);
+
+CREATE TABLE jonah_tags (
+ tag_id INT NOT NULL,
+ tag_name VARCHAR(255) NOT NULL,
+--
+ PRIMARY KEY (tag_id)
+);
+
+CREATE TABLE jonah_stories_tags (
+ story_id INT NOT NULL,
+ channel_id INT NOT NULL,
+ tag_id INT NOT NULL,
+--
+ PRIMARY KEY (story_id, channel_id, tag_id)
+);
+
+CREATE INDEX jonah_stories_channel_idx ON jonah_stories (channel_id);
+CREATE INDEX jonah_stories_published_idx ON jonah_stories (story_published);
+CREATE INDEX jonah_stories_url_idx ON jonah_stories (story_url);
+CREATE INDEX jonah_channels_type_idx ON jonah_channels (channel_type);
--- /dev/null
+-- $Horde: jonah/scripts/sql/jonah.sql,v 1.17 2009/10/20 21:28:30 jan Exp $
+
+CREATE TABLE jonah_channels (
+ channel_id INT NOT NULL,
+ channel_slug VARCHAR(64) NOT NULL,
+ channel_name VARCHAR(255) NOT NULL,
+ channel_type SMALLINT NOT NULL,
+ channel_full_feed SMALLINT DEFAULT 0 NOT NULL,
+ channel_desc VARCHAR(255),
+ channel_interval INT,
+ channel_url VARCHAR(255),
+ channel_link VARCHAR(255),
+ channel_page_link VARCHAR(255),
+ channel_story_url VARCHAR(255),
+ channel_img VARCHAR(255),
+ channel_updated INT,
+--
+ PRIMARY KEY (channel_id)
+);
+
+CREATE TABLE jonah_stories (
+ story_id INT NOT NULL,
+ channel_id INT NOT NULL,
+ story_author VARCHAR(255) NOT NULL,
+ story_title VARCHAR(255) NOT NULL,
+ story_desc TEXT,
+ story_body_type VARCHAR(255) NOT NULL,
+ story_body TEXT,
+ story_url VARCHAR(255),
+ story_permalink VARCHAR(255),
+ story_published INT,
+ story_updated INT NOT NULL,
+ story_read INT NOT NULL,
+--
+ PRIMARY KEY (story_id)
+);
+
+CREATE TABLE jonah_tags (
+ tag_id INT NOT NULL,
+ tag_name VARCHAR(255) NOT NULL,
+--
+ PRIMARY KEY (tag_id)
+);
+
+CREATE TABLE jonah_stories_tags (
+ story_id INT NOT NULL,
+ channel_id INT NOT NULL,
+ tag_id INT NOT NULL,
+--
+ PRIMARY KEY (story_id, channel_id, tag_id)
+);
+
+CREATE INDEX jonah_stories_channel_idx ON jonah_stories (channel_id);
+CREATE INDEX jonah_stories_published_idx ON jonah_stories (story_published);
+CREATE INDEX jonah_stories_url_idx ON jonah_stories (story_url);
+CREATE INDEX jonah_channels_type_idx ON jonah_channels (channel_type);
--- /dev/null
+-- SQL script for adding a column for the story release date.
+
+ALTER TABLE jonah_stories ADD COLUMN story_release INT;
+UPDATE jonah_stories SET story_release = 1;
\ No newline at end of file
--- /dev/null
+-- SQL script for setting the release date to the story date if it was "1".
+
+UPDATE jonah_stories SET story_release = story_date WHERE story_release = 1;
--- /dev/null
+-- SQL script for adapting to updated story date handling.
+
+ALTER TABLE jonah_stories ADD COLUMN story_published INT;
+ALTER TABLE jonah_stories ADD COLUMN story_updated INT NOT NULL;
+UPDATE jonah_stories SET story_published = story_release;
+UPDATE jonah_stories SET story_updated = story_date;
+ALTER TABLE jonah_stories DROP COLUMN story_release;
+ALTER TABLE jonah_stories DROP COLUMN story_date;
--- /dev/null
+-- SQL script for adding a column for the channel story pages.
+
+ALTER TABLE jonah_channels ADD COLUMN channel_page_link VARCHAR(255);
--- /dev/null
+-- SQL script for adding a column for the stories' permalinks.
+
+ALTER TABLE jonah_stories ADD COLUMN story_permalink VARCHAR(255);
--- /dev/null
+-- SQL script for adding the tag tables to Jonah.
+
+CREATE TABLE jonah_tags (
+ tag_id INT NOT NULL,
+ tag_name VARCHAR(255) NOT NULL,
+---
+ PRIMARY KEY (tag_id)
+);
+
+CREATE TABLE jonah_stories_tags (
+ story_id INT NOT NULL,
+ channel_id INT NOT NULL,
+ tag_id INT NOT NULL,
+---
+ PRIMARY KEY (story_id, channel_id, tag_id)
+);
--- /dev/null
+--
+-- $Horde: jonah/scripts/upgrades/2008-08-22_add_channel_slugs.sql,v 1.1 2008/08/23 02:02:37 bklang Exp $
+--
+ALTER TABLE jonah_channels ADD COLUMN `channel_slug` VARCHAR(64);
+UPDATE jonah_channels SET channel_slug=channel_id;
+ALTER TABLE jonah_channels CHANGE channel_slug channel_slug VARCHAR(64) NOT NULL;
--- /dev/null
+--
+-- $Horde: jonah/scripts/upgrades/2008-08-22_add_story_authors.sql,v 1.1 2008/08/23 02:48:34 bklang Exp $
+--
+ALTER TABLE jonah_stories ADD COLUMN story_author VARCHAR(255);
+UPDATE jonah_stories SET story_author='Anonymous';
+ALTER TABLE jonah_stories CHANGE COLUMN story_author story_author VARCHAR(255) NOT NULL;
--- /dev/null
+--
+-- $Horde: jonah/scripts/upgrades/2008-12-17_add_full_feed.sql,v 1.2 2009/10/20 21:28:30 jan Exp $
+--
+ALTER TABLE jonah_channels ADD COLUMN `channel_full_feed` SMALLINT;
+UPDATE jonah_channels SET channel_full_feed = 0;
+ALTER TABLE jonah_channels CHANGE channel_full_feed channel_full_feed SMALLINT DEFAULT 0 NOT NULL;
--- /dev/null
+<?php
+/**
+ * $Horde: jonah/stories/delete.php,v 1.40 2009/11/24 04:15:38 chuck Exp $
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (BSD). If you
+ * did not receive this file, see http://cvs.horde.org/co.php/jonah/LICENSE.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Marko Djukic <marko@oblo.com>
+ */
+
+define('JONAH_BASE', dirname(__FILE__) . '/..');
+require_once JONAH_BASE . '/lib/base.php';
+require_once JONAH_BASE . '/lib/News.php';
+require_once 'Horde/Form.php';
+require_once 'Horde/Form/Renderer.php';
+
+/* Set up the form variables. */
+$vars = Horde_Variables::getDefaultVariables();
+$form_submit = $vars->get('submitbutton');
+$channel_id = $vars->get('channel_id');
+$story_id = $vars->get('story_id');
+
+/* Driver */
+$news = Jonah_News::factory();
+
+/* Fetch the channel details, needed for later and to check if valid
+ * channel has been requested. */
+$channel = $news->isChannelEditable($channel_id);
+if (is_a($channel, 'PEAR_Error')) {
+ $notification->push(sprintf(_("Story editing failed: %s"), $channel->getMessage()), 'horde.error');
+ $url = Horde::applicationUrl('channels/index.php', true);
+ header('Location: ' . $url);
+ exit;
+}
+
+/* Check permissions. */
+if (!Jonah::checkPermissions(Jonah::typeToPermName($channel['channel_type']), Horde_Perms::DELETE, $channel_id)) {
+ $notification->push(_("You are not authorised for this action."), 'horde.warning');
+ Horde::authenticationFailureRedirect();
+}
+
+$story = $news->getStory($channel_id, $story_id);
+if (is_a($story, 'PEAR_Error')) {
+ $notification->push(_("No valid story requested for deletion."), 'horde.message');
+ $url = Horde::applicationUrl('channels/index.php', true);
+ header('Location: ' . $url);
+ exit;
+}
+
+/* If not yet submitted set up the form vars from the fetched story. */
+if (empty($form_submit)) {
+ $vars = new Horde_Variables($story);
+}
+
+$title = sprintf(_("Delete News Story \"%s\"?"), $vars->get('story_title'));
+
+$form = new Horde_Form($vars, $title);
+
+$form->setButtons(array(_("Delete"), _("Do not delete")));
+$form->addHidden('', 'channel_id', 'int', true, true);
+$form->addHidden('', 'story_id', 'int', true, true);
+$form->addVariable(_("Really delete this News Story?"), 'confirm', 'description', false);
+
+if ($form_submit == _("Delete")) {
+ if ($form->validate($vars)) {
+ $form->getInfo($vars, $info);
+ $delete = $news->deleteStory($info['channel_id'], $info['story_id']);
+ if (is_a($delete, 'PEAR_Error')) {
+ $notification->push(sprintf(_("There was an error deleting the story: %s"), $delete->getMessage()), 'horde.error');
+ } else {
+ $notification->push(_("The story has been deleted."), 'horde.success');
+ $url = Horde::applicationUrl('stories/index.php', true);
+ $url = Horde_Util::addParameter($url, 'channel_id', $channel_id, false);
+ header('Location: ' . $url);
+ exit;
+ }
+ }
+} elseif (!empty($form_submit)) {
+ $notification->push(_("Story has not been deleted."), 'horde.message');
+ $url = Horde::applicationUrl('stories/index.php', true);
+ $url = Horde_Util::addParameter($url, 'channel_id', $channel_id, false);
+ header('Location: ' . $url);
+ exit;
+}
+
+$template = new Horde_Template();
+$template->set('main', Horde_Util::bufferOutput(array($form, 'renderActive'), new Horde_Form_Renderer(), $vars, 'delete.php', 'post'));
+$template->set('menu', Jonah::getMenu('string'));
+$template->set('notify', Horde_Util::bufferOutput(array($notification, 'notify'), array('listeners' => 'status')));
+
+require JONAH_TEMPLATES . '/common-header.inc';
+echo $template->fetch(JONAH_TEMPLATES . '/main/main.html');
+require $registry->get('templates', 'horde') . '/common-footer.inc';
--- /dev/null
+<?php
+/**
+ * Script to add/edit stories.
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * $Horde: jonah/stories/edit.php,v 1.48 2009/11/24 04:15:38 chuck Exp $
+ *
+ * See the enclosed file LICENSE for license information (BSD). If you did not
+ * did not receive this file, see http://cvs.horde.org/co.php/jonah/LICENSE.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Marko Djukic <marko@oblo.com>
+ */
+
+require_once dirname(__FILE__) . '/../lib/base.php';
+require_once JONAH_BASE . '/lib/News.php';
+require_once JONAH_BASE . '/lib/Forms/Story.php';
+require_once 'Horde/Form/Action.php';
+require_once 'Horde/Form/Renderer.php';
+
+$news = Jonah_News::factory();
+
+/* Set up the form variables. */
+$vars = Horde_Variables::getDefaultVariables();
+$channel_id = $vars->get('channel_id');
+
+/* Fetch the channel details, needed for later and to check if valid
+ * channel has been requested. */
+$channel = $news->isChannelEditable($channel_id);
+if (is_a($channel, 'PEAR_Error')) {
+ $notification->push(sprintf(_("Story editing failed: %s"), $channel->getMessage()), 'horde.error');
+ $url = Horde::applicationUrl('channels/index.php', true);
+ header('Location: ' . $url);
+ exit;
+}
+
+/* Check permissions. */
+if (!Jonah::checkPermissions(Jonah::typeToPermName($channel['channel_type']), Horde_Perms::EDIT, $channel_id)) {
+ $notification->push(_("You are not authorised for this action."), 'horde.warning');
+ Horde::authenticationFailureRedirect();
+}
+
+/* Check if a story is being edited. */
+$story_id = $vars->get('story_id');
+if ($story_id && !$vars->get('formname')) {
+ $story = $news->getStory($channel_id, $story_id);
+ $story['story_tags'] = implode(',', array_values($story['story_tags']));
+ $vars = new Horde_Variables($story);
+}
+
+/* Set up the form. */
+$form = new StoryForm($vars);
+if ($form->validate($vars)) {
+ $form->getInfo($vars, $info);
+ $result = $news->saveStory($info);
+ if (is_a($result, 'PEAR_Error')) {
+ $notification->push(sprintf(_("There was an error saving the story: %s"), $result->getMessage()), 'horde.error');
+ } else {
+ $notification->push(sprintf(_("The story \"%s\" has been saved."), $info['story_title']), 'horde.success');
+ $url = Horde_Util::addParameter('stories/index.php', 'channel_id', $channel_id);
+ header('Location: ' . Horde::applicationUrl($url, true));
+ exit;
+ }
+}
+
+/* Needed javascript. */
+Horde::addScriptFile('open_calendar.js', 'horde');
+
+/* Render the form. */
+$template = new Horde_Template();
+$template->set('main', Horde_Util::bufferOutput(array($form, 'renderActive'), $form->getRenderer(), $vars, 'edit.php', 'post'));
+$template->set('menu', Jonah::getMenu('string'));
+$template->set('notify', Horde_Util::bufferOutput(array($notification, 'notify'), array('listeners' => 'status')));
+
+$title = $form->getTitle();
+require JONAH_TEMPLATES . '/common-header.inc';
+echo $template->fetch(JONAH_TEMPLATES . '/main/main.html');
+require $registry->get('templates', 'horde') . '/common-footer.inc';
--- /dev/null
+<?php
+/**
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * $Horde: jonah/stories/index.php,v 1.70 2009/11/24 04:15:38 chuck Exp $
+ *
+ * See the enclosed file LICENSE for license information (BSD). If you
+ * did not receive this file, see http://cvs.horde.org/co.php/jonah/LICENSE.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Marko Djukic <marko@oblo.com>
+ */
+
+@define('JONAH_BASE', dirname(__FILE__) . '/..');
+require_once JONAH_BASE . '/lib/base.php';
+require_once JONAH_BASE . '/lib/News.php';
+
+$news = Jonah_News::factory();
+
+/* Redirect to the news index if no channel_id is specified. */
+$channel_id = Horde_Util::getFormData('channel_id');
+if (empty($channel_id)) {
+ $notification->push(_("No channel requested."), 'horde.error');
+ $url = Horde::applicationUrl('channels/index.php', true);
+ header('Location: ' . $url);
+ exit;
+}
+
+$channel = $news->getChannel($channel_id);
+if (!Jonah::checkPermissions(Jonah::typeToPermName($channel['channel_type']), Horde_Perms::EDIT, $channel_id)) {
+ $notification->push(_("You are not authorised for this action."), 'horde.warning');
+ Horde::authenticationFailureRedirect();
+}
+
+/* Check if a forced refresh is being called for an external channel. */
+$refresh = Horde_Util::getFormData('refresh');
+
+/* Check if a URL has been passed. */
+$url = Horde_Util::getFormData('url');
+
+$stories = $news->getStories($channel_id, null, 0, !empty($refresh), null, true);
+if (is_a($stories, 'PEAR_Error')) {
+ $notification->push(sprintf(_("Invalid channel requested. %s"), $stories->getMessage()), 'horde.error');
+ $url = Horde::applicationUrl('channels/index.php', true);
+ header('Location: ' . $url);
+ exit;
+}
+
+/* Do some state tests. */
+if (empty($stories)) {
+ $notification->push(_("No available stories."), 'horde.warning');
+}
+if (!empty($refresh)) {
+ $notification->push(_("Channel refreshed."), 'horde.success');
+}
+if (!empty($url)) {
+ header('Location: ' . $url);
+ exit;
+}
+
+/* Get channel details, for title, etc. */
+$channel = $news->getChannel($channel_id);
+
+$allow_delete = Jonah::checkPermissions(Jonah::typeToPermName($channel['channel_type']), Horde_Perms::DELETE, $channel_id);
+
+/* Build story specific fields. */
+foreach ($stories as $key => $story) {
+ /* story_published is the publication/release date, story_updated
+ * is the last change date. */
+ if (!empty($stories[$key]['story_published'])) {
+ $stories[$key]['story_published_date'] = strftime($prefs->getValue('date_format') . ', ' . ($prefs->getValue('twentyFour') ? '%H:%M' : '%I:%M%p'), $stories[$key]['story_published']);
+ } else {
+ $stories[$key]['story_published_date'] = '';
+ }
+
+ /* Default to no links. */
+ $stories[$key]['pdf_link'] = '';
+ $stories[$key]['edit_link'] = '';
+ $stories[$key]['delete_link'] = '';
+
+ /* These links only if internal channel. */
+ if ($channel['channel_type'] == JONAH_INTERNAL_CHANNEL ||
+ $channel['channel_type'] == JONAH_COMPOSITE_CHANNEL) {
+ $stories[$key]['view_link'] = Horde::link(Horde::url($story['story_link']), $story['story_desc']) . htmlspecialchars($story['story_title']) . '</a>';
+
+ /* PDF link. */
+ $url = Horde::applicationUrl('stories/pdf.php');
+ $url = Horde_Util::addParameter($url, array('story_id' => $story['story_id'], 'channel_id' => $channel_id));
+ $stories[$key]['pdf_link'] = Horde::link($url, _("PDF version")) . Horde::img('mime/pdf.png', _("PDF version"), '', $registry->getImageDir('horde')) . '</a>';
+
+ /* Edit story link. */
+ $url = Horde::applicationUrl('stories/edit.php');
+ $url = Horde_Util::addParameter($url, array('story_id' => $story['story_id'], 'channel_id' => $channel_id));
+ $stories[$key]['edit_link'] = Horde::link($url, _("Edit story")) . Horde::img('edit.png', _("Edit story"), '', $registry->getImageDir('horde')) . '</a>';
+
+ /* Delete story link. */
+ if ($allow_delete) {
+ $url = Horde::applicationUrl('stories/delete.php');
+ $url = Horde_Util::addParameter($url, array('story_id' => $story['story_id'], 'channel_id' => $channel_id));
+ $stories[$key]['delete_link'] = Horde::link($url, _("Delete story")) . Horde::img('delete.png', _("Delete story"), '', $registry->getImageDir('horde')) . '</a>';
+ }
+
+ /* Comment counter. */
+ if ($conf['comments']['allow'] &&
+ $registry->hasMethod('forums/numMessages')) {
+ $comments = $registry->call('forums/numMessages', array($stories[$key]['story_id'], 'jonah'));
+ if (!is_a($comments, 'PEAR_Error')) {
+ $stories[$key]['comments'] = $comments;
+ }
+ }
+ } else {
+ if (!empty($story['story_body'])) {
+ $stories[$key]['view_link'] = Horde::link(Horde::url($story['story_link']), $story['story_desc'], '', '_blank') . htmlspecialchars($story['story_title']) . '</a>';
+ } else {
+ $stories[$key]['view_link'] = Horde::link(Horde::externalUrl($story['story_url']), $story['story_desc'], '', '_blank') . htmlspecialchars($story['story_title']) . '</a>';
+ }
+ }
+}
+
+$template = new Horde_Template();
+$template->setOption('gettext', true);
+$template->set('header', htmlspecialchars($channel['channel_name']));
+$template->set('refresh', Horde::link(Horde_Util::addParameter(Horde::selfUrl(true), array('refresh' => 1)), _("Refresh Channel")) . Horde::img('reload.png', '', '', $registry->getImageDir('horde')) . '</a>');
+$template->set('listheaders', array(_("Story"), _("Date")));
+$template->set('stories', $stories, true);
+$template->set('read', $channel['channel_type'] == JONAH_INTERNAL_CHANNEL || $channel['channel_type'] == JONAH_COMPOSITE_CHANNEL, true);
+$template->set('comments', $conf['comments']['allow'] && $registry->hasMethod('forums/numMessages') && $channel['channel_type'] == JONAH_INTERNAL_CHANNEL, true);
+$template->set('menu', Jonah::getMenu('string'));
+$template->set('notify', Horde_Util::bufferOutput(array($notification, 'notify'), array('listeners' => 'status')));
+
+$title = $channel['channel_name'];
+require JONAH_TEMPLATES . '/common-header.inc';
+echo $template->fetch(JONAH_TEMPLATES . '/stories/index.html');
+require $registry->get('templates', 'horde') . '/common-footer.inc';
--- /dev/null
+<?php
+/**
+ * $Horde: jonah/stories/pdf.php,v 1.8 2009/06/10 17:20:11 slusarz Exp $
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (BSD). If you
+ * did not receive this file, see http://cvs.horde.org/co.php/jonah/LICENSE.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+
+function _exit($message)
+{
+ $GLOBALS['notification']->push(sprintf(_("Error fetching story: %s"), $message), 'horde.error');
+ require JONAH_TEMPLATES . '/common-header.inc';
+ $GLOBALS['notification']->notify(array('listeners' => 'status'));
+ require $GLOBALS['registry']->get('templates', 'horde') . '/common-footer.inc';
+ exit;
+}
+
+$session_control = 'readonly';
+@define('AUTH_HANDLER', true);
+@define('JONAH_BASE', dirname(__FILE__) . '/..');
+require_once JONAH_BASE . '/lib/base.php';
+require_once JONAH_BASE . '/lib/News.php';
+require_once 'File/PDF.php';
+
+$news = Jonah_News::factory();
+
+$channel_id = Horde_Util::getFormData('channel_id');
+$story_id = Horde_Util::getFormData('story_id');
+if (!$story_id) {
+ $story_id = $news->getLatestStoryId($channel_id);
+ if (is_a($story_id, 'PEAR_Error')) {
+ _exit($story_id->getMessage());
+ }
+}
+
+$story = $news->getStory($channel_id, $story_id, !$browser->isRobot());
+if (is_a($story, 'PEAR_Error')) {
+ _exit($story->getMessage());
+} elseif (empty($story['story_body']) && !empty($story['story_url'])) {
+ _exit(_("Cannot generate PDFs of remote stories."));
+}
+
+// Convert the body from HTML to text if necessary.
+if (!empty($story['story_body_type']) && $story['story_body_type'] == 'richtext') {
+ $story['story_body'] = Horde_Text_Filter::filter($story['story_body'], 'html2text');
+}
+
+// Set up the PDF object.
+$pdf = File_PDF::factory(array('format' => 'Letter', 'unit' => 'pt'));
+$pdf->setMargins(50, 50);
+
+// Enable automatic page breaks.
+$pdf->setAutoPageBreak(true, 50);
+
+// Start the document.
+$pdf->open();
+
+// Start a page.
+$pdf->addPage();
+
+// Publication date.
+if (!empty($story['story_published_date'])) {
+ $pdf->setFont('Times', 'B', 14);
+ $pdf->cell(0, 14, $story['story_published_date'], 0, 1);
+ $pdf->newLine(10);
+}
+
+// Write the header in Times 24 Bold.
+$pdf->setFont('Times', 'B', 24);
+$pdf->multiCell(0, 24, $story['story_title'], 'B', 1);
+$pdf->newLine(20);
+
+// Write the story body in Times 14.
+$pdf->setFont('Times', '', 14);
+$pdf->write(14, $story['story_body']);
+
+// Output the generated PDF.
+$browser->downloadHeaders($story['story_title'] . '.pdf', 'application/pdf');
+echo $pdf->getOutput();
--- /dev/null
+<?php
+/**
+ * Display list of articles that match a tag query from an internal
+ * channel.
+ *
+ * $Horde: jonah/stories/results.php,v 1.5 2009/11/24 04:15:38 chuck Exp $
+ */
+
+@define('JONAH_BASE', dirname(__FILE__) . '/..');
+require_once JONAH_BASE . '/lib/base.php';
+require_once JONAH_BASE . '/lib/News.php';
+
+$news = Jonah_News::factory();
+
+/* Redirect to the news index if no tag_id is specified. */
+$tag_id = Horde_Util::getFormData('tag_id');
+
+/* If a channel_id is passed in, use it - otherwise we assume this is
+ * a search for tags for ALL visible internal channels. */
+$channel_id = Horde_Util::getFormData('channel_id', null);
+if (!is_null($channel_id)) {
+ $channel = $news->getChannel($channel_id);
+ if (!Jonah::checkPermissions(Jonah::typeToPermName($channel['channel_type']), Horde_Perms::SHOW, $channel_id)) {
+ $notification->push(_("You are not authorised for this action."), 'horde.warning');
+ Horde::authenticationFailureRedirect();
+ }
+ $channel_ids = array($channel_id);
+} else {
+ $channel_ids = array();
+ $channels = $news->getChannels(JONAH_INTERNAL_CHANNEL);
+ foreach ($channels as $ch) {
+ if (Jonah::checkPermissions(Jonah::typeToPermName($ch['channel_type']), Horde_Perms::SHOW, $ch['channel_id'])) {
+ $channel_ids[] = $ch['channel_id'];
+ }
+ }
+}
+
+/* Make sure we actually requested a tag search */
+if (empty($tag_id)) {
+ $notification->push(_("No tag requested."), 'horde.error');
+ $url = Horde::applicationUrl('channels/index.php', true);
+ header('Location: ' . $url);
+ exit;
+}
+$tag_name = array_shift($news->getTagNames(array($tag_id)));
+
+$stories = $news->searchTagsById(array($tag_id), 10, 0, $channel_ids);
+if (is_a($stories, 'PEAR_Error')) {
+ $notification->push(sprintf(_("Invalid channel requested. %s"), $stories->getMessage()), 'horde.error');
+ $url = Horde::applicationUrl('channels/index.php', true);
+ header('Location: ' . $url);
+ exit;
+}
+
+/* Do some state tests. */
+if (empty($stories)) {
+ $notification->push(_("No available stories."), 'horde.warning');
+}
+
+foreach ($stories as $key => $story) {
+ /* Use the channel_id from the story hash since we might be dealing
+ with more than one channel. */
+ $channel_id = $story['channel_id'];
+
+ if (!empty($stories[$key]['story_published'])) {
+ $stories[$key]['story_published_date'] = strftime($prefs->getValue('date_format') . ', ' . ($prefs->getValue('twentyFour') ? '%H:%M' : '%I:%M%p'), $stories[$key]['story_published']);
+ } else {
+ $stories[$key]['story_published_date'] = '';
+ }
+
+ /* Default to no links. */
+ $stories[$key]['pdf_link'] = '';
+ $stories[$key]['edit_link'] = '';
+ $stories[$key]['delete_link'] = '';
+
+ $stories[$key]['view_link'] = Horde::link(Horde::url($story['story_link']), $story['story_desc']) . htmlspecialchars($story['story_title']) . '</a>';
+
+ /* PDF link. */
+ $url = Horde::applicationUrl('stories/pdf.php');
+ $url = Horde_Util::addParameter($url, array('story_id' => $story['story_id'], 'channel_id' => $channel_id));
+ $stories[$key]['pdf_link'] = Horde::link($url, _("PDF version")) . Horde::img('mime/pdf.png', _("PDF version"), '', $registry->getImageDir('horde')) . '</a>';
+
+ /* Edit story link. */
+ if (Jonah::checkPermissions(Jonah::typeToPermName(JONAH_INTERNAL_CHANNEL), Horde_Perms::EDIT, $channel_id)) {
+ $url = Horde::applicationUrl('stories/edit.php');
+ $url = Horde_Util::addParameter($url, array('story_id' => $story['story_id'], 'channel_id' => $channel_id));
+ $stories[$key]['edit_link'] = Horde::link($url, _("Edit story")) . Horde::img('edit.png', _("Edit story"), '', $registry->getImageDir('horde')) . '</a>';
+ }
+
+ /* Delete story link. */
+ if (Jonah::checkPermissions(Jonah::typeToPermName(JONAH_INTERNAL_CHANNEL), Horde_Perms::DELETE, $channel_id)) {
+ $url = Horde::applicationUrl('stories/delete.php');
+ $url = Horde_Util::addParameter($url, array('story_id' => $story['story_id'], 'channel_id' => $channel_id));
+ $stories[$key]['delete_link'] = Horde::link($url, _("Delete story")) . Horde::img('delete.png', _("Delete story"), '', $registry->getImageDir('horde')) . '</a>';
+ }
+
+ /* Comment counter. */
+ if ($conf['comments']['allow'] &&
+ $registry->hasMethod('forums/numMessages')) {
+ $comments = $registry->call('forums/numMessages', array($stories[$key]['story_id'], 'jonah'));
+ if (!is_a($comments, 'PEAR_Error')) {
+ $stories[$key]['comments'] = $comments;
+ }
+ }
+}
+
+
+/** @TODO It might be better using a new template for results? **/
+$template = new Horde_Template();
+$template->setOption('gettext', true);
+if (isset($channel)) {
+ $template->set('header', sprintf(_("Stories tagged with %s in %s"), $tag_name, htmlspecialchars($channel['channel_name'])));
+} else {
+ $template->set('header', sprintf(_("All stories tagged with %s"), $tag_name));
+}
+$template->set('listheaders', array(_("Story"), _("Date")));
+$template->set('stories', $stories, true);
+$template->set('read', true, true);
+$template->set('comments', $conf['comments']['allow'] && $registry->hasMethod('forums/numMessages'), true);
+$template->set('menu', Jonah::getMenu('string'));
+$template->set('notify', Horde_Util::bufferOutput(array($notification, 'notify'), array('listeners' => 'status')));
+
+require JONAH_TEMPLATES . '/common-header.inc';
+echo $template->fetch(JONAH_TEMPLATES . '/stories/index.html');
+require $registry->get('templates', 'horde') . '/common-footer.inc';
--- /dev/null
+<?php
+/**
+ * $Horde: jonah/stories/share.php,v 1.33 2009/09/06 17:00:39 jan Exp $
+ *
+ * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (BSD). If you
+ * did not receive this file, see http://cvs.horde.org/co.php/jonah/LICENSE.
+ */
+
+function _mail($story_part, $from, $recipients, $subject, $note)
+{
+ global $conf;
+
+ /* Create the MIME message. */
+ require_once JONAH_BASE . '/lib/version.php';
+ $mail = new Horde_Mime_Mail(array('subject' => $subject,
+ 'to' => $recipients,
+ 'from' => $from,
+ 'charset' => Horde_Nls::getCharset()));
+ $mail->addHeader('User-Agent', 'Jonah ' . JONAH_VERSION);
+
+ /* If a note has been provided, add it to the message as a text part. */
+ if (strlen($note) > 0) {
+ $message_note = new MIME_Part('text/plain', null, Horde_Nls::getCharset());
+ $message_note->setContents($message_note->replaceEOL($note));
+ $message_note->setDescription(_("Note"));
+ $mail->addMIMEPart($message_note);
+ }
+
+ /* Get the story as a MIME part and add it to our message. */
+ $mail->addMIMEPart($story_part);
+
+ /* Log the pending outbound message. */
+ Horde::logMessage(sprintf('<%s> is sending "%s" to (%s)',
+ $from, $subject, $recipients),
+ __FILE__, __LINE__, PEAR_LOG_INFO);
+
+ /* Send the message and return the result. */
+ return $mail->send(Horde::getMailerConfig());
+}
+
+$session_control = 'readonly';
+@define('AUTH_HANDLER', true);
+@define('JONAH_BASE', dirname(__FILE__) . '/..');
+require_once JONAH_BASE . '/lib/base.php';
+require_once JONAH_BASE . '/lib/News.php';
+require_once 'Horde/Form.php';
+require_once 'Horde/Form/Renderer.php';
+
+$news = Jonah_News::factory();
+
+/* Set up the form variables. */
+$vars = Horde_Variables::getDefaultVariables();
+$channel_id = $vars->get('channel_id');
+$story_id = $vars->get('story_id');
+
+if (!$conf['sharing']['allow']) {
+ $url = Horde::applicationUrl('stories/view.php', true);
+ $url = Horde_Util::addParameter($url, array('story_id' => $story_id, 'channel_id' => $channel_id));
+ header('Location: ' . $url);
+ exit;
+}
+
+$story = $news->getStory($channel_id, $story_id);
+if (is_a($story, 'PEAR_Error')) {
+ $notification->push(sprintf(_("Error fetching story: %s"), $story->getMessage()), 'horde.warning');
+ $story = '';
+}
+$vars->set('subject', $story['story_title']);
+
+/* Set up the form. */
+$form = new Horde_Form($vars);
+$title = _("Share Story");
+$form->setTitle($title);
+$form->setButtons(_("Send"));
+$form->addHidden('', 'channel_id', 'int', false);
+$form->addHidden('', 'story_id', 'int', false);
+$v = &$form->addVariable(_("From"), 'from', 'email', true, false);
+if (Horde_Auth::getAuth()) {
+ require_once 'Horde/Identity.php';
+ $identity = Identity::factory();
+ $v->setDefault($identity->getValue('from_addr'));
+}
+$form->addVariable(_("To"), 'recipients', 'email', true, false, _("Separate multiple email addresses with commas."), true);
+$form->addVariable(_("Subject"), 'subject', 'text', true);
+$form->addVariable(_("Include"), 'include', 'enum', true, false, null, array(array(_("A link to the story"), _("The complete text of the story"))));
+$form->addVariable(_("Message"), 'message', 'longtext', false, false, null, array(4, 40));
+
+if ($form->validate($vars)) {
+ $form->getInfo($vars, $info);
+
+ $channel = $news->getChannel($channel_id);
+ if (empty($channel['channel_story_url'])) {
+ $story_url = Horde::applicationUrl('stories/view.php', true);
+ $story_url = Horde_Util::addParameter($story_url, array('channel_id' => '%c', 'story_id' => '%s'));
+ } else {
+ $story_url = $channel['channel_story_url'];
+ }
+
+ $story_url = str_replace(array('%25c', '%25s'), array('%c', '%s'), $story_url);
+ $story_url = str_replace(array('%c', '%s', '&'), array($channel_id, $story['story_id'], '&'), $story_url);
+
+ if ($info['include'] == 0) {
+ require_once 'Horde/MIME/Part.php';
+
+ /* TODO: Create a "URL link" MIME part instead. */
+ $message_part = new MIME_Part('text/plain');
+ $message_part->setContents($message_part->replaceEOL($story_url));
+ $message_part->setDescription(_("Story Link"));
+ } else {
+ $message_part = Jonah_News::getStoryAsMessage($story);
+ }
+
+ $result = _mail($message_part, $info['from'], $info['recipients'],
+ $info['subject'], $info['message']);
+
+ if (is_a($result, 'PEAR_Error')) {
+ $notification->push(sprintf(_("Unable to send story: %s"), $result->getMessage()), 'horde.error');
+ } else {
+ $notification->push(_("The story was sent successfully."), 'horde.success');
+ header('Location: ' . $story_url);
+ exit;
+ }
+}
+
+$share_template = new Horde_Template();
+$share_template->set('main', Horde_Util::bufferOutput(array($form, 'renderActive'), new Horde_Form_Renderer(), $vars, 'share.php', 'post'));
+$share_template->set('notify', Horde_Util::bufferOutput(array($notification, 'notify'), array('listeners' => 'status')));
+
+require JONAH_TEMPLATES . '/common-header.inc';
+echo $share_template->fetch(JONAH_TEMPLATES . '/stories/share.html');
+require $registry->get('templates', 'horde') . '/common-footer.inc';
--- /dev/null
+<?php
+/**
+ * $Horde: jonah/stories/view.php,v 1.57 2009/12/10 17:42:36 jan Exp $
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (BSD). If you
+ * did not receive this file, see http://cvs.horde.org/co.php/jonah/LICENSE.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+
+@define('AUTH_HANDLER', true);
+@define('JONAH_BASE', dirname(__FILE__) . '/..');
+require_once JONAH_BASE . '/lib/base.php';
+require_once JONAH_BASE . '/lib/News.php';
+
+$news = Jonah_News::factory();
+
+$channel_id = Horde_Util::getFormData('channel_id');
+$story_id = Horde_Util::getFormData('story_id');
+if (!$story_id) {
+ $story_id = $news->getLatestStoryId($channel_id);
+ if (is_a($story_id, 'PEAR_Error')) {
+ $notification->push(sprintf(_("Error fetching story: %s"), $story_id->getMessage()), 'horde.warning');
+ require JONAH_TEMPLATES . '/common-header.inc';
+ $notification->notify(array('listeners' => 'status'));
+ require $registry->get('templates', 'horde') . '/common-footer.inc';
+ exit;
+ }
+}
+
+$story = $news->getStory($channel_id, $story_id, !$browser->isRobot());
+if (is_a($story, 'PEAR_Error')) {
+ $notification->push(sprintf(_("Error fetching story: %s"), $story->getMessage()), 'horde.warning');
+ require JONAH_TEMPLATES . '/common-header.inc';
+ $notification->notify(array('listeners' => 'status'));
+ require $registry->get('templates', 'horde') . '/common-footer.inc';
+ exit;
+} elseif (empty($story['story_body']) && !empty($story['story_url'])) {
+ header('Location: ' . Horde::externalUrl($story['story_url']));
+ exit;
+}
+
+/* Grab tag related content for entire channel */
+$cloud = new Horde_Ui_TagCloud();
+$allTags = $news->listTagInfo(array(), $channel_id);
+foreach ($allTags as $tag_id => $taginfo) {
+ $cloud->addElement($taginfo['tag_name'], Horde_Util::addParameter('results.php', array('tag_id' => $tag_id, 'channel_id' => $channel_id)), $taginfo['total']);
+}
+
+/* Prepare the story's tags for display */
+$tag_html = array();
+$tag_link = Horde_Util::addParameter(Horde::applicationUrl('stories/results.php'), 'channel_id', $channel_id);
+foreach ($story['story_tags'] as $id => $tag) {
+ $link = Horde_Util::addParameter($tag_link, 'tag_id', $id);
+ $tag_html[] = Horde::link($link) . $tag . '</a>';
+}
+
+/* Filter and prepare story content. */
+$story['story_title'] = htmlspecialchars($story['story_title']);
+$story['story_desc'] = htmlspecialchars($story['story_desc']);
+if (!empty($story['story_body_type']) && $story['story_body_type'] == 'text') {
+ $story['story_body'] = Horde_Text_Filter::filter($story['story_body'], 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::MICRO, 'class' => null));
+}
+if (!empty($story['story_url'])) {
+ $story['story_body'] .= "\n<p>" . Horde::link(Horde::externalUrl($story['story_url'])) . htmlspecialchars($story['story_url']) . '</a></p>';
+}
+if (empty($story['story_published_date'])) {
+ $story['story_published_date'] = false;
+}
+
+$story_template = new Horde_Template();
+$story_template->set('story', $story, true);
+$story_template->set('storytags', implode(', ', $tag_html));
+
+$view_template = new Horde_Template();
+$view_template->setOption('gettext', true);
+$view_template->set('story', $story_template->fetch(JONAH_TEMPLATES . '/stories/story.html'));
+$view_template->set('cloud', '<div class="tagSelector" ' . $cloud->buildHTML() . '</div>', true);
+/* Insert link for sharing. */
+if ($conf['sharing']['allow']) {
+ $url = Horde::applicationUrl('stories/share.php');
+ $url = Horde_Util::addParameter($url, array('story_id' => $story['story_id'], 'channel_id' => $channel_id));
+ $view_template->set('sharelink', Horde::link($url) . _("Share this story") . '</a>', true);
+} else {
+ $view_template->set('sharelink', false, true);
+}
+
+/* Insert comments. */
+if ($conf['comments']['allow']) {
+ if (!$registry->hasMethod('forums/doComments')) {
+ $err = 'User comments are enabled but the forums API is not available.';
+ Horde::logMessage($err, __FILE__, __LINE__, PEAR_LOG_ERR);
+ } else {
+ $comments = $registry->call('forums/doComments', array('jonah', $story_id, 'commentCallback'));
+ if (is_a($comments, 'PEAR_Error')) {
+ Horde::logMessage($threads, __FILE__, __LINE__, PEAR_LOG_ERR);
+ $comments = '';
+ }
+ $comments = $comments['threads'] . '<br />' . $comments['comments'];
+ $view_template->set('comments', $comments, true);
+ }
+} else {
+ $view_template->set('comments', false, true);
+}
+
+$view_template->set('menu', Jonah::getMenu('string'));
+$view_template->set('notify', Horde_Util::bufferOutput(array($notification, 'notify'), array('listeners' => 'status')));
+
+require JONAH_TEMPLATES . '/common-header.inc';
+echo $view_template->fetch(JONAH_TEMPLATES . '/stories/view.html');
+require $registry->get('templates', 'horde') . '/common-footer.inc';
--- /dev/null
+Deny from all
--- /dev/null
+<div id="menu">
+ <tag:menu />
+</div>
+
+<tag:notify />
+
+<div class="header">
+ <tag:header />
+ <a id="quicksearchL" href="#" title="<gettext>Search</gettext>" onclick="$('quicksearchL').hide(); $('quicksearch').show(); $('quicksearchT').focus(); return false;"><tag:search_img /></a>
+ <div id="quicksearch" style="display:none">
+ <input type="text" name="quicksearchT" id="quicksearchT" for="feeds-body" empty="feeds-empty" />
+ <small>
+ <a title="<gettext>Close Search</gettext>" href="#" onclick="$('quicksearch').hide(); $('quicksearchT').value = ''; QuickFinder.filter($('quicksearchT')); $('quicksearchL').show(); return false;">X</a>
+ </small>
+ </div>
+</div>
+
+<if:channels>
+<table id="feeds" width="100%" class="sortable" cellspacing="0">
+<thead>
+ <tr>
+ <th width="1%"> </th>
+ <loop:listheaders>
+ <th<tag:listheaders.attrs />>
+ <tag:listheaders.label />
+ </th>
+ </loop:listheaders>
+ </tr>
+</thead>
+
+<tbody id="feeds-body">
+ <loop:channels>
+ <tr>
+ <td class="nowrap">
+ <tag:channels.edit_link />
+ <tag:channels.refresh_link />
+ <tag:channels.addstory_link />
+ <tag:channels.lists_link />
+ <tag:channels.delete_link />
+ </td>
+ <td>
+ <a href="<tag:channels.stories_url />"><tag:channels.channel_name /></a>
+ </td>
+ <td><tag:channels.channel_type /></td>
+ <td class="linedRow"><tag:channels.channel_updated /></td>
+ </tr>
+ </loop:channels>
+</tbody>
+</table>
+<div id="feeds-empty">
+ <gettext>No feeds match</gettext>
+</div>
+<else:channels>
+<div class="text">
+ <em><gettext>No channels are available.</gettext></em>
+</div>
+</else:channels>
+</if:channels>
--- /dev/null
+<?php
+if (isset($language)) {
+ header('Content-type: text/html; charset=' . Horde_Nls::getCharset());
+ header('Vary: Accept-Language');
+}
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">
+<!-- Jonah: Copyright 1999-2009 The Horde Project. Jonah is under a Horde license. -->
+<!-- Horde Project: http://www.horde.org/ | Jonah: http://www.horde.org/jonah/ -->
+<!-- Horde Licenses: http://www.horde.org/licenses/ -->
+<?php echo !empty($language) ? '<html lang="' . strtr($language, '_', '-') . '">' : '<html>' ?>
+<head>
+<?php
+
+$page_title = $registry->get('name');
+if (!empty($title)) $page_title .= ' :: ' . $title;
+if (!empty($refresh_time) && ($refresh_time > 0) && !empty($refresh_url)) {
+ echo "<meta http-equiv=\"refresh\" content=\"$refresh_time;url=$refresh_url\">\n";
+}
+
+Horde::includeScriptFiles();
+
+if ($channel_id = Horde_Util::getFormData('channel_id')) {
+ $rss_url_params = array('channel_id' => $channel_id);
+ if ($tag_id = Horde_Util::getFormData('tag_id')) {
+ $rss_url_params['tag_id'] = $tag_id;
+ }
+ echo '<link rel="alternate" type="application/rss+xml" title="RSS 0.91" href="' . Horde_Util::addParameter(Horde::applicationUrl('delivery/rss.php', true, -1), $rss_url_params) . '" />';
+}
+?>
+<title><?php echo htmlspecialchars($page_title) ?></title>
+<link href="<?php echo $GLOBALS['registry']->getImageDir()?>/favicon.ico" rel="SHORTCUT ICON" />
+<?php Horde::includeStylesheetFiles() ?>
+</head>
+
+<body<?php if ($bc = Horde_Util::nonInputVar('bodyClass')) echo ' class="' . $bc . '"' ?><?php if ($bi = Horde_Util::nonInputVar('bodyId')) echo ' id="' . $bi . '"'; ?>>
--- /dev/null
+<div id="menu">
+ <tag:menu />
+</div>
+
+<tag:notify />
+
+<table width="100%" cellspacing="0">
+ <tr>
+ <td class="item0">
+ <form action="<tag:url />" method="get">
+ <input type="hidden" name="channel_id" value="<tag:channel_id />" />
+ <tag:session />
+ <gettext>Select a format:</gettext>
+ <select onchange="form.submit()" name="format">
+ <loop:options>
+ <tag:options />
+ </loop:options>
+ </select>
+ </form>
+ </td>
+ </tr>
+ <tr>
+ <td class="item1">
+ <tag:stories />
+ </td>
+ </tr>
+</table>
--- /dev/null
+<?xml version="1.0" encoding="<tag:charset />"?>
+<?xml-stylesheet href="<tag:xsl />" type="text/xsl"?>
+<!DOCTYPE rss PUBLIC "-//Netscape Communications//DTD RSS 0.91//EN" "http://my.netscape.com/publish/formats/rss-0.91.dtd">
+<rss version="0.91">
+ <channel>
+ <title><tag:channel_name /></title>
+ <description><tag:channel_desc /></description>
+ <link><tag:channel_official /></link>
+ <atom:link rel="self" type="application/rss+xml" title="<tag:channel_name />" href="<tag:channel_rss />" xmlns:atom="http://www.w3.org/2005/Atom"><tag:channel_rss /></atom:link>
+ <pubDate><tag:channel_updated /></pubDate>
+ <loop:stories>
+ <item>
+ <title><tag:stories.story_title /></title>
+ <description><tag:stories.story_desc /></description>
+ <link><tag:stories.story_link /></link>
+ </item>
+ </loop:stories>
+ </channel>
+</rss>
--- /dev/null
+<?xml version="1.0" encoding="<tag:charset />"?>
+<?xml-stylesheet href="<tag:xsl />" type="text/xsl"?>
+<rss version="2.0">
+ <channel>
+ <title><tag:channel_name /></title>
+ <link><tag:channel_official /></link>
+ <atom:link rel="self" type="application/rss+xml" title="<tag:channel_name />" href="<tag:channel_rss2 />" xmlns:atom="http://www.w3.org/2005/Atom"><tag:channel_rss /></atom:link>
+ <description><tag:channel_desc /></description>
+ <pubDate><tag:channel_updated /></pubDate>
+ <generator><tag:jonah /></generator>
+ <loop:stories>
+ <item>
+ <title><tag:stories.story_title /></title>
+ <link><tag:stories.story_link /></link>
+ <description><tag:stories.story_desc /></description>
+ <pubDate><tag:stories.story_published /></pubDate>
+ <guid isPermaLink="true"><tag:stories.story_permalink /></guid>
+ </item>
+ </loop:stories>
+ </channel>
+</rss>
--- /dev/null
+<?xml version="1.0" encoding="<tag:charset />"?>
+<?xml-stylesheet href="<tag:xsl />" type="text/xsl"?>
+<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
+ <channel>
+ <title><tag:channel_name /></title>
+ <link><tag:channel_official /></link>
+ <atom:link rel="self" type="application/rss+xml" title="<tag:channel_name />" href="<tag:channel_rss2 />" xmlns:atom="http://www.w3.org/2005/Atom"><tag:channel_rss /></atom:link>
+ <description><tag:channel_desc /></description>
+ <pubDate><tag:channel_updated /></pubDate>
+ <generator><tag:jonah /></generator>
+ <loop:stories>
+ <item>
+ <title><tag:stories.story_title /></title>
+ <link><tag:stories.story_link /></link>
+ <description><tag:stories.story_desc /></description>
+ <content:encoded><![CDATA[<tag:stories.story_body />
+]]></content:encoded>
+ <pubDate><tag:stories.story_published /></pubDate>
+ <guid isPermaLink="true"><tag:stories.story_permalink /></guid>
+ </item>
+ </loop:stories>
+ </channel>
+</rss>
--- /dev/null
+<?xml version="1.0" encoding="<tag:charset />"?>
+<?xml-stylesheet href="<tag:xsl />" type="text/xsl"?>
+<!DOCTYPE rss PUBLIC "-//Netscape Communications//DTD RSS 0.91//EN" "http://my.netscape.com/publish/formats/rss-0.91.dtd">
+<rss version="0.91" xmlns:content="http://purl.org/rss/1.0/modules/content/">
+ <channel>
+ <title><tag:channel_name /></title>
+ <description><tag:channel_desc /></description>
+ <link><tag:channel_official /></link>
+ <atom:link rel="self" type="application/rss+xml" title="<tag:channel_name />" href="<tag:channel_rss />" xmlns:atom="http://www.w3.org/2005/Atom"><tag:channel_rss /></atom:link>
+ <pubDate><tag:channel_updated /></pubDate>
+ <loop:stories>
+ <item>
+ <title><tag:stories.story_title /></title>
+ <description><tag:stories.story_desc /></description>
+ <content:encoded><![CDATA[<tag:stories.story_body />
+]]></content:encoded>
+ <link><tag:stories.story_link /></link>
+ </item>
+ </loop:stories>
+ </channel>
+</rss>
--- /dev/null
+<div id="menu">
+ <tag:menu />
+</div>
+
+<tag:notify />
+
+<tag:main />
--- /dev/null
+<div id="menu">
+ <tag:menu />
+</div>
+
+<tag:notify />
+
+<div class="header">
+ <tag:header /> <tag:refresh />
+</div>
+
+<if:stories>
+<table width="100%" cellspacing="0" class="linedRow nowrap">
+ <tr class="item">
+ <th width="1%"> </th>
+ <loop:listheaders>
+ <th class="leftAlign">
+ <tag:listheaders />
+ </th>
+ </loop:listheaders>
+ <if:read>
+ <th class="leftAlign">
+ <gettext>Read</gettext>
+ </th>
+ </if:read>
+ <if:comments>
+ <th class="leftAlign">
+ <gettext>Comments</gettext>
+ </th>
+ </if:comments>
+ </tr>
+
+ <loop:stories>
+ <tr>
+ <td>
+ <tag:stories.pdf_link />
+ <tag:stories.edit_link />
+ <tag:stories.delete_link />
+ </td>
+ <td>
+ <tag:stories.view_link />
+ </td>
+ <td>
+ <tag:stories.story_published_date />
+ </td>
+ <if:read>
+ <td>
+ <tag:stories.story_read />
+ </td>
+ </if:read>
+ <if:comments>
+ <td>
+ <tag:stories.comments />
+ </td>
+ </if:comments>
+ </tr>
+ </loop:stories>
+</table>
+</if:stories>
--- /dev/null
+<tag:notify />
+<tag:main />
--- /dev/null
+<h1 class="header">
+ <span class="storyDate"><tag:story.story_published_date /></span> <tag:story.story_title />
+</h1>
+
+<p class="storyTags">
+ <gettext>Tags: </gettext><tag:storytags />
+</p>
+
+<p class="storySubtitle">
+ <tag:story.story_desc />
+</p>
+
+<div class="storyBody">
+ <tag:story.story_body />
+</div>
--- /dev/null
+<div id="menu">
+ <tag:menu />
+</div>
+
+<tag:notify />
+<if:cloud>
+ <div style="float:right;"><tag:cloud /></div>
+ <div style="margin-right:170px;">
+<else:cloud>
+ <div>
+</else:cloud>
+</if:cloud>
+<tag:story />
+<div class="storyLinks">
+<if:sharelink>
+ <tag:sharelink />
+</if:sharelink>
+</div>
+
+</div>
+<if:comments>
+<div class="storyComments">
+<tag:comments />
+</div>
+</if:comments>
--- /dev/null
+<?php
+/**
+ * $Horde: jonah/test.php,v 1.30 2009/11/11 01:30:59 mrubinsk Exp $
+ *
+ * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ */
+
+/* Include Horde's core.php file. */
+include_once '../lib/core.php';
+
+/* We should have loaded the String class, from the Horde_Util
+ * package, in core.php. If Horde_String:: isn't defined, then we're not
+ * finding some critical libraries. */
+if (!class_exists('Horde_String')) {
+ echo '<br /><h2 style="color:red">The Horde_Util package was not found. If PHP\'s error_reporting setting is high enough and display_errors is on, there should be error messages printed above that may help you in debugging the problem. If you are simply missing these files, then you need to get the <a href="http://cvs.horde.org/cvs.php/framework">framework</a> module from <a href="http://www.horde.org/source/">Horde CVS</a>, and install the packages in it with the install-packages.php script.</h2>';
+ exit;
+}
+
+/* Initialize the Horde_Test:: class. */
+if (!is_readable('../lib/Test.php')) {
+ echo 'ERROR: You must install Horde before running this script.';
+ exit;
+}
+require_once '../lib/Test.php';
+$horde_test = new Horde_Test();
+
+/* Jonah definitions. */
+$module = 'Jonah';
+require_once dirname(__FILE__) . '/lib/Application.php';
+$app = new Jonah_Application();
+$module_version = $app->version;
+
+
+/* PHP module capabilities. */
+$module_list = array(
+ 'gettext' => array(
+ 'descrip' => 'Gettext Support',
+ 'error' => 'Jonah will not run without gettext support. Compile php <code>--with-gettext</code> before continuing.'
+ ),
+ 'xml' => array(
+ 'descrip' => 'XML Support',
+ 'error' => 'Without XML support, Jonah WILL NOT WORK. You must fix this before going any further.'
+ )
+);
+
+/* Jonah configuration files. */
+$file_list = array(
+ 'config/conf.php' => 'The file <code>./config/conf.php</code> appears to be missing. You probably just forgot to copy <code>./config/conf.php.dist</code> over. While you do that, take a look at the settings and make sure they are appropriate for your site.'
+);
+
+require TEST_TEMPLATES . 'header.inc';
+require TEST_TEMPLATES . 'version.inc';
+
+/* Display PHP Version information. */
+$php_info = $horde_test->getPhpVersionInformation();
+require TEST_TEMPLATES . 'php_version.inc';
+
+?>
+
+<h1>PHP Modules</h1>
+<ul>
+ <?php echo $horde_test->phpModuleCheck($module_list) ?>
+</ul>
+
+<h1>Jonah Configuration Files</h1>
+<ul>
+ <?php echo $horde_test->requiredFileCheck($file_list) ?>
+</ul>
+
+<?php
+require TEST_TEMPLATES . 'footer.inc';
--- /dev/null
+<?xml version="1.0"?>
+
+<xsl:stylesheet version="1.0"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:rss="http://purl.org/rss/1.0/"
+ xmlns:atom="http://www.w3.org/2005/Atom">
+
+ <xsl:output indent="yes" encoding="UTF-8"/>
+
+ <xsl:template match="/rss|/atom:feed">
+ <html>
+ <head>
+ <title>
+ <xsl:value-of select="/rss/channel/title"/>
+ </title>
+ <style type="text/css">
+ img {
+ border: 0;
+ padding: 5px;
+ }
+ </style>
+ </head>
+ <body>
+ <p>
+ You're viewing an XML content feed which is
+ intended to be viewed within a feed aggregator.
+ </p>
+
+ <xsl:variable name="link" select="/rss/channel/link"/>
+ <h3>Subscribe to <a href="{$link}"><xsl:value-of select="/rss/channel/title"/></a></h3>
+ <xsl:variable name="cimage" select="/rss/channel/image/url"/>
+ <div style="float:right;"><img src="{$cimage}"/></div>
+ <p>
+ Subscribe now in your favorite RSS aggregator:
+ </p>
+
+ <xsl:variable name="resource" select="/rss/channel/atom:link"/>
+
+ <div>
+ <a href="http://www.rojo.com/add-subscription?resource={$resource}">
+ <img src="http://www.rojo.com/skins/static/images/add-to-rojo.gif" alt="Subscribe in Rojo"/>
+ </a>
+
+ <a href="http://add.my.yahoo.com/rss?url={$resource}">
+ <img src="http://us.i1.yimg.com/us.yimg.com/i/us/my/addtomyyahoo4.gif" alt="Add to My yahoo" />
+ </a>
+
+ <a href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url={$resource}">
+ <img src="http://www.newsgator.com/images/ngsub1.gif" alt="Subscribe in NewsGator Online"/>
+ </a>
+
+ <a href="http://www.bloglines.com/sub/{$resource}">
+ <img src="http://www.bloglines.com/images/sub_modern5.gif" alt="Subscribe with Bloglines"/>
+ </a>
+
+ <a href="http://fusion.google.com/add?feedurl={$resource}">
+ <img src="http://buttons.googlesyndication.com/fusion/add.gif" alt="Subscribe with Google Reader"/>
+ </a>
+ </div>
+
+ <p>
+ <h3>Preview</h3>
+ </p>
+
+ <xsl:apply-templates select="/rss/channel/item" />
+
+ </body>
+ </html>
+ </xsl:template>
+
+ <xsl:template match="item">
+ <xsl:variable name="link" select="link"/>
+ <p>
+ <a href="{$link}">
+ <xsl:value-of select="title"/>
+ </a>
+ <br />
+ <xsl:value-of select="description"/>
+ </p>
+ </xsl:template>
+
+</xsl:stylesheet>
--- /dev/null
+/**
+ * $Horde: jonah/themes/screen.css,v 1.9 2008/05/26 21:33:11 chuck Exp $
+ */
+
+.storySubtitle {
+ font-style: italic;
+ font-size: 90%;
+ color: #999;
+ margin: 5px 10px;
+ padding: 2px;
+ background: #fff;
+}
+
+.storyBody {
+ margin: 5px 10px;
+ padding: 2px;
+ color: #000;
+ background: #fff;
+}
+.storyBody ul, .storyBody ol {
+ margin: 1em 0;
+ -moz-padding-start: 40px;
+}
+
+.storyLinks, .storyTags {
+ margin: 5px 10px;
+ padding: 2px;
+ font-size: 90%;
+ background: #fff;
+}
+.storyLinks {
+ border-top: 1px solid #ccc;
+}
+
+.storyComments {
+ margin-top: 8px;
+}
+
+.tagSelector {
+ border: 1px solid #ccc;
+ margin: 4px;
+ padding: 5px;
+ background: #fff;
+ width: 150px;
+}
+
+a.earliest:link, a.earliest:visited, a.earliest:hover, a.earliest:active {
+ color: #ccc;
+}
+
+a.earlier:link, a.earlier:visited, a.earlier:hover, a.earlier:active {
+ color: #99c;
+}
+
+a.later:link, a.later:visited, a.later:hover, a.later:active {
+ color: #99f;
+}
+
+a.latest:link, a.latest:visited, a.latest:hover, a.latest:active {
+ color: #00f;
+}
+
+/* Tables. */
+table#feeds {
+ width: 99%;
+ margin: 0 0 8px 5px;
+ border-top: 1px solid #ddd;
+ border-left: 1px solid #ddd;
+}
+table#feeds th {
+ padding: 3px;
+ background: #e9e9e9;
+ border-right: 1px solid #ccc;
+ text-align: left;
+}
+table#feeds td {
+ padding: 3px;
+ border-right: 1px solid #ddd;
+ border-bottom: 1px solid #ddd;
+}
+table#feeds th.sortup {
+ background: #bbcbff url("graphics/za.png") center left no-repeat;
+ padding-left: 10px;
+}
+table#feeds th.sortdown {
+ background: #bbcbff url("graphics/az.png") center left no-repeat;
+ padding-left: 10px;
+}
+
+/* QuickFinder */
+.QuickFinderNoMatch {
+ display: none;
+}
+#feeds-empty {
+ padding: 4px;
+ font-style: italic;
+}
+#quicksearch {
+ display: inline;
+}
+#quicksearch input {
+ background-image: url("graphics/search.png");
+ background-repeat: no-repeat;
+ background-position: 2px center;
+ padding: 1px 0 1px 20px;
+}
+#quicksearch a {
+ display: inline-block;
+ padding: 2px 4px;
+}