--- /dev/null
+<!-- // Hide from older browsers
+/* $Id$ */
+/*
+# Copyright (c) 2003-2005, Jannis Hermanns (on behalf the Serendipity Developer Team)
+# All rights reserved. See LICENSE file for licensing details
+*/
+
+/*
+ Written by chris wetherell
+ http://www.massless.org
+ chris [THE AT SIGN] massless.org
+
+ warning: it only works for IE4+/Win and Moz1.1+
+ feel free to take it for your site
+ if there are any problems, let chris know.
+*/
+
+var thisForm;
+
+function getMozSelection(txtarea) {
+ var selLength = txtarea.textLength;
+ var selStart = txtarea.selectionStart;
+ var selEnd = txtarea.selectionEnd;
+
+ if (selEnd==1 || selEnd==2) {
+ selEnd=selLength;
+ }
+ return (txtarea.value).substring(selStart, selEnd);
+}
+
+function getIESelection(txtarea) {
+ return document.selection.createRange().text;
+}
+
+function mozWrap(txtarea, lft, rgt) {
+ var selLength = txtarea.textLength;
+ var selStart = txtarea.selectionStart;
+ var selEnd = txtarea.selectionEnd;
+
+ if (txtarea.setSelectionRange) {
+ if (selEnd==1 || selEnd==2) selEnd=selLength;
+ var s1 = (txtarea.value).substring(0,selStart);
+ var s2 = (txtarea.value).substring(selStart, selEnd)
+ var s3 = (txtarea.value).substring(selEnd, selLength);
+ txtarea.value = s1 + lft + s2 + rgt + s3;
+ } else {
+ txtarea.value = txtarea.value + ' ' + lft + rgt + ' ';
+ }
+}
+
+function IEWrap(txtarea, lft, rgt) {
+ strSelection = document.selection.createRange().text;
+ if (strSelection != "") {
+ document.selection.createRange().text = lft + strSelection + rgt;
+ } else {
+ txtarea.value = txtarea.value + lft + rgt;
+ }
+}
+
+function wrapSelection(txtarea, lft, rgt) {
+ if (document.all) {
+ IEWrap(txtarea, lft, rgt);
+ } else if (document.getElementById) {
+ mozWrap(txtarea, lft, rgt);
+ }
+}
+
+function wrapSelectionWithLink(txtarea) {
+ var my_link = prompt("Enter URL:","http://");
+
+ if (document.all && getIESelection(txtarea) == "" ||
+ document.getElementById && getMozSelection(txtarea) == "") {
+ var my_desc = prompt("Enter Description", '');
+ }
+
+ var my_title = prompt("Enter title/tooltip:", "");
+
+ html_title = "";
+ if (my_title != "") {
+ html_title = ' title="' + my_title + '"';
+ }
+
+ if (my_link != null) {
+ lft = "<a href=\"" + my_link + "\" " + html_title + ">";
+ if (my_desc != null && my_desc != "") {
+ rgt = my_desc + "</a>";
+ } else {
+ rgt = "</a>";
+ }
+ wrapSelection(txtarea, lft, rgt);
+ }
+
+ return;
+}
+/* end chris w. script */
+
+function mozInsert(txtarea, str) {
+ var selLength = txtarea.textLength;
+ var selStart = txtarea.selectionStart;
+ var selEnd = txtarea.selectionEnd;
+ if (selEnd==1 || selEnd==2) {
+ selEnd=selLength;
+ }
+ var s1 = (txtarea.value).substring(0,selStart);
+ var s2 = (txtarea.value).substring(selStart, selEnd)
+ var s3 = (txtarea.value).substring(selEnd, selLength);
+ txtarea.value = s1 + str + s2 + s3;
+}
+
+function wrapInsImage(area) {
+ var loc = prompt('Enter the Image Location: ');
+ if (!loc) {
+ return;
+ }
+ mozInsert(area,'<img src="'+ loc + '" alt="" />');
+}
+
+/* end Better-Editor functions */
+
+function serendipity_insImage (area) {
+ var loc = prompt('Enter the Image Location: ');
+ if (!loc) {
+ area.focus();
+ return;
+ }
+
+ area.value = area.value + '<img src="' + loc + '" alt="" />';
+ area.focus();
+}
+
+function serendipity_insBasic (area, tag) {
+ area.value = area.value + "<" + tag + "></" + tag + ">";
+ area.focus();
+}
+
+function serendipity_insLink (area) {
+ var loc = prompt('Enter URL Location: ');
+ var text = prompt('Enter Description: ');
+ var my_title = prompt("Enter title/tooltip:", "");
+
+ if (!loc) {
+ area.focus();
+ return;
+ }
+
+ html_title = "";
+ if (my_title != "") {
+ html_title = ' title="' + my_title + '"';
+ }
+
+ area.value = area.value + '<a href="' + loc + '" ' + html_title + '>' + (text ? text : loc) + '</a>';
+ area.focus();
+}
+
+function serendipity_imageSelector_addToElement (str, el)
+{
+ document.getElementById(el).value = str;
+ document.getElementById(el).focus();
+ if (document.getElementById(el).onchange) {
+ document.getElementById(el).onchange();
+ }
+}
+
+function serendipity_imageSelector_addToBody (str, textarea)
+{
+ eltarget = '';
+ if (document.forms['serendipityEntry'] && document.forms['serendipityEntry']['serendipity['+ textarea +']']) {
+ eltarget = document.forms['serendipityEntry']['serendipity['+ textarea +']']
+ } else if (document.forms['serendipityEntry'] && document.forms['serendipityEntry'][textarea]) {
+ eltarget = document.forms['serendipityEntry'][textarea];
+ } else {
+ eltarget = document.forms[0].elements[0];
+ }
+
+ wrapSelection(eltarget, str, '');
+ eltarget.focus();
+}
+
+function serendipity_imageSelector_done(textarea)
+{
+ var insert = '';
+ var img = '';
+ var src = '';
+ var f = document.forms['serendipity[selForm]'].elements;
+
+ if (f['serendipity[linkThumbnail]'][0].checked == true) {
+ img = f['thumbName'].value;
+ imgWidth = f['imgThumbWidth'].value;
+ imgHeight = f['imgThumbHeight'].value;
+ } else {
+ img = f['imgName'].value;
+ imgWidth = f['imgWidth'].value;
+ imgHeight = f['imgHeight'].value;
+ }
+
+ if (f['serendipity[filename_only]'] && f['serendipity[filename_only]'].value == 'true') {
+ self.opener.serendipity_imageSelector_addToElement(img, f['serendipity[htmltarget]'].value);
+ self.close();
+ return true;
+ }
+
+ if (document.getElementById('serendipity_imagecomment').value != '') {
+ styled = false;
+ } else {
+ styled = true;
+ }
+
+ floating = 'center';
+ if (XHTML11) {
+ if (f['serendipity[align]'][0].checked == true) {
+ img = "<img width='" + imgWidth + "' height='" + imgHeight + "' " + (styled ? 'style="border: 0px; padding-left: 5px; padding-right: 5px;"' : '') + ' src="' + img + "\" alt=\"\" />";
+ } else if (f['serendipity[align]'][1].checked == true) {
+ img = "<img width='" + imgWidth + "' height='" + imgHeight + "' " + (styled ? 'style="float: left; border: 0px; padding-left: 5px; padding-right: 5px;"' : '') + ' src="' + img + "\" alt=\"\" />";
+ floating = 'left';
+ } else if (f['serendipity[align]'][2].checked == true) {
+ img = "<img width='" + imgWidth + "' height='" + imgHeight + "' " + (styled ? 'style="float: right; border: 0px; padding-left: 5px; padding-right: 5px;"' : '') + ' src="' + img + "\" alt=\"\" />";
+ floating = 'right';
+ }
+ } else {
+ if (f['serendipity[align]'][0].checked == true) {
+ img = "<img width='" + imgWidth + "' height='" + imgHeight + "' border='0' hspace='5' src='" + img + "' alt='' />";
+ } else if (f['serendipity[align]'][1].checked == true) {
+ img = "<img width='" + imgWidth + "' height='" + imgHeight + "' border='0' hspace='5' align='left' src='" + img + "' alt='' />";
+ floating = 'left';
+ } else if (f['serendipity[align]'][2].checked == true) {
+ img = "<img width='" + imgWidth + "' height='" + imgHeight + "' border='0' hspace='5' align='right' src='" + img + "' alt='' />";
+ floating = 'right';
+ }
+ }
+
+ if (f['serendipity[isLink]'][1].checked == true) {
+ insert = "<a class='serendipity_image_link' href='" + f['serendipity[url]'].value + "'>" + img + "</a>";
+ } else {
+ insert = img;
+ }
+
+ if (document.getElementById('serendipity_imagecomment').value != '') {
+ comment = f['serendipity[imagecomment]'].value;
+ block = '<div class="serendipity_imageComment_' + floating + '" style="width: ' + imgWidth + 'px">'
+ + '<div class="serendipity_imageComment_img">' + insert + '</div>'
+ + '<div class="serendipity_imageComment_txt">' + comment + '</div>'
+ + '</div>';
+ } else {
+ block = insert;
+ }
+
+ if (typeof(self.opener.htmlarea_editors) != 'undefined' && typeof(self.opener.htmlarea_editors[textarea] != 'undefined')) {
+ self.opener.htmlarea_editors[textarea].surroundHTML(block, '');
+ } else if (parent.self.opener.editorref) {
+ parent.self.opener.editorref.surroundHTML(block, '');
+ } else {
+ parent.self.opener.serendipity_imageSelector_addToBody(block, textarea);
+ }
+
+ parent.self.close();
+}
+
+// -->
--- /dev/null
+# $Id$
+
+Version 1.0.1 (August 14th, 2006)
+------------------------------------------------------------------------
+
+ * Fix problem on newer Firefox versions, where insertion of images
+ in the WYSIWYG editor did not work. It might be necessary to
+ purge your browsers cache for this to properly work. (Jay Bertrandt)
+
+ * Fix utf8 iconv conversion failing on some older PHP setups.
+ Thanks to Matthias Leisi!
+
+ * Fix multi-authors view only showing first author (garvinhicking)
+
+ * Fix bug sending comment-notification mails to subscribed users
+ without an email address (garvinhicking)
+
+ * Fixed icelandic language bug preventing upgrade (garvinhicking)
+
+Version 1.0 (June 15th, 2006)
+------------------------------------------------------------------------
+
+ * Insert logic for saving an entry that prevents the iframe for
+ trackbacks/xml-rpc pings to save an entry multiple times upon
+ failure. Many thanks to tharos from the Forums! (garvinhicking)
+
+ * Add smarty parameter $view which can be used to detect what kind
+ of page is being displayed. One of: archives, entry, feed, admin,
+ archives, plugin, categories, authors, search, css, start, 404
+ (garvinhicking)
+
+ * Change Spartacus plugin to use new SourceForge URLs (garvinhicking)
+
+ * Added polish language translation by CoSTa
+
+ * Fix media browser sorting by authorid causing trouble in SQLite
+ (Sven Oliver Moll)
+
+Version 1.0-beta3 (May 5th, 2006)
+------------------------------------------------------------------------
+
+ * Fix another (minor) XSRF for entry manager, thanks to Geoff Johnson
+ (garvinhicking)
+
+ * Support "Force Feedburner" option to the syndication plugin to
+ let rss.php only be accessible to feedburner and no clients.
+ (garvinhicking)
+
+ * Add Akismet antispam support (garvinhicking)
+
+ * Saving special crafterd configuration data as Admin superuser
+ could lead to arbitrary PHP code inclusion from
+ serendipity_config_local.inc.php. Since admins usually already have
+ superuser rights over their files, this is not considered a
+ "real-life" security issue. (garvinhicking)
+
+ * Added Pivot importer (garvinhicking)
+
+ * The spamblock plugin now continues to check any comment/trackback
+ set to MODERATE if maybe other rules override the status to
+ REJECTED. This should reduce the amount of moderation mails that
+ definitely are spam. (Bug #1467707) (garvinhicking)
+
+ * Fix UTF-8 encoding of $i18n_filename_* variables for building
+ permalinks (Bug #1461754, Thanks to Kim Sullivan) (garvinhicking)
+
+ * Fix messing up comment count when deleting a trackback from admin
+ panel (garvinhicking)
+
+ * Fix missing UTF-8 encoding of date locales on Windows setups
+ (garvinhicking)
+
+ * Fix imagepng() function call for PHP 5.1.0 versions
+ (Sebastian Mordziol)
+
+ * Fix WYSIWYG javascript referencing so that plugins can access their
+ objects (like emoticonchooser). Also replace <i> with <em> and
+ <b> with <strong>. (Jay Bertrandt)
+
+Version 1.0-beta2 (March 13th, 2006)
+------------------------------------------------------------------------
+
+ * Fixed chief-editor not being able to create editors (garvinhicking)
+
+ * Added turkish language by Ahmet Usal
+
+ * Fix also deleting thumbnail when removing an image (garvinhicking)
+
+ * Added new competition theme winner by Carl Galloway
+
+ * Enable Spamblock plugin to allow registered authors to not enter
+ captchas and other spamchecks (garvinhicking)
+
+ * Fixed spartacus DB error on postgreSQL installations, thanks to
+ volkris! (garvinhicking)
+
+ * Added partial Tamil translation by Selva
+
+ * Fix Text_Wiki problems with UTF-8 charsets (garvinhicking)
+
+ * Added PHP 5.1.3 filter awareness (Tobias Schlitt)
+
+ * Properly sort template listings alphabetically, if using themes
+ in subdirectories (garvinhicking)
+
+ * Add new plugin API method "performConfig" for performing expensive
+ configuration options that shall not impact the usual blog
+ performance. Enhance mailentry plugin to send mails depending
+ on the associated category of an entry (garvinhicking)
+
+ * Add ability to "preload" specific plugins and SQL entries into
+ Serendipity. Just create a plugins/preload.txt file containing
+ the plugins to load (one plugin per line, indicate plugin type
+ like "serendipity_event_spartacus:event"). Preloaded SQL data
+ can be put into sql/preload.sql and may only contain INSERT
+ statements. (garvinhicking, judebert)
+
+ * When the "no_create" privilege is set, deny users access to any
+ external admin plugins (garvinhicking)
+
+ * Made categories plugin default HTML output more XHTML compliant.
+ (garvinhicking)
+
+ * Added option "stronger captchas" in spamblock plugin.
+
+ * Transcode incoming trackbacks from the given charset to your blog's
+ charset. Reliably only works with the two most common formats,
+ ISO-8859-1 <-> UTF-8. (garvinhicking)
+
+ * Change karma plugin and search results to output more HTML span
+ classes for better styling (garvinhicking)
+
+ * Modified plugin API to allow deactivating the output of plugin
+ sidebars (garvinhicking)
+
+ * Adjust the function serendipity_currentURL() to properly return
+ path components without doubling any of them (garvinhicking)
+
+ * Fix serendipity_event_mailer plugin to also work when being called
+ from posting plugins like XMLRPC, popfetcher etc. (garvinhicking)
+
+ * Make templatechooser plugin store the selected template in a cookie
+ (garvinhicking)
+
+ * Added new hook "backend_header" for plugins to emit output in the
+ header section of the admin interface. (garvinhicking)
+
+ * Added experimental MySQL5 "SET NAMES" support. Needs to be enabled
+ by setting $serendipity['dbNames'] = true somehwere (like
+ serendipity_config_local.inc.php) (garvinhicking)
+
+ * Patch bundled PEAR.php so that it does not try to declare a PEAR
+ class a second time. (garvinhicking)
+
+ * RSS feeds use the permalink URL to link to extended entry, not the
+ RSS guid element. (garvinhicking)
+
+ * Remove duplicate path file name in serendipity_currentURL function,
+ preventing the right "is frontpage?" detection routine from working.
+ (garvinhicking)
+
+ * Use mysql_real_escape_string when available (garvinhicking)
+
+ * Atom feeds now use proper appliaction-mime type header, thanks to
+ Boris
+
+ * MoveableType importer no longer chokes on "FM/EM" timestamps
+ (garvinhicking)
+
+ * Fix undefined function call to "is_defined".
+
+ * Added translation helper tool plugin_lang.php to detect untranslated
+ plugin constants (garvinhicking)
+
+ * Updated french translation by Sebastian Mordziol
+
+ * Updated icelandic translation by Örn Arnarson
+
+ * Updated chinese plugin translation, by Demin Yin
+
+Version 1.0-beta1 (January 23rd, 2006)
+------------------------------------------------------------------------
+
+ * Apply changes to shared installation directory detection so that
+ it also works with Apache's mod_userdir (elf2000)
+
+ * Fix some problems with sending UTF-8 emails on some systems. Allow
+ Plugin API to handle the whole mailflow optionally.
+ (garvinhicking)
+
+ * Fixed Safari Browser issue with formatting and inserting media
+ items (garvinhicking)
+
+ * RFE #1387997 - Show amount of entries per archive period (archive
+ sidebar plugin). Side-effect are some new additions to the
+ serendipity_fetchEntries() function call for further abstraction.
+ (garvinhicking)
+
+ * Add new plugin hook "backend_http_request" which passes PEAR
+ HTTP_Request options to plugins for modification. $addData contains
+ the source of where a request is being made (garvinhicking)
+
+ * Make WordPress importer not fail on the missing "user_level" column
+ for WordPress 2.0 (garvinhicking)
+
+ * Add new option to spamblock plugin to forbid trackbacks from URLs that
+ don't have our URL in them. (garvinhicking)
+
+ * Recognize "multiselect" as new plugin item type. Calls to get_config()
+ will return a string, that needs exploding to convert it into the
+ array of multi-selected input values.
+ For the property bag object, you can set a new item "select_preselected"
+ to indicate which (if any) values shall be preselected. The item "select_size"
+ indicates the multi-select size.
+ (garvinhicking)
+
+ * Make spartacus plugin be able to filter comment bodies
+ (garvinhicking)
+
+ * Fixed bug which broke thumbnail generation with Imagemagick when
+ safe-mode was ON (tomsommer)
+
+ * New Smarty template preview_iframe.tpl for creating the entry's
+ preview in the backend panel (garvinhicking)
+
+ * Entry mail plugin can now send mails to multiple recipients, and
+ define the list of recipients per-entry (garvinhicking)
+
+ * Trackback now uses PEAR HTTP::Request class to send trackbacks.
+ This allows to use HTTP redirects of trackbacks, which seem to
+ get more common nowadays (garvinhicking)
+
+ * Fix Bug #1377095 - Not counting correct number of entries on the
+ /archives page. The page was counting entries belonging to more
+ then one categories multiple times. (garvinhicking)
+
+ * Fix MoveableType importer to better import comments (garvinhicking)
+
+ * Allow category sidebar plugin to hide the parent category, if
+ the plugin is configured to only show a leaf of a category tree
+ (garvinhicking)
+
+ * Allow to restrict not showing entries from special category via
+ $serendipity['GET']['hide_category']. (garvinhicking)
+
+ * Allow to edit a HTML nugget with a frontend link (garvinhicking)
+
+ * Added sidebar plugin to show statistical data (garvinhicking)
+
+ * Changed detection of "last_modified" timestamp, so that the date
+ only gets bumped in case of real updates. Thanks to Ivan Cenov!
+
+ * Changed interface language autodetection so that multilingual plugin
+ can override it (garvinhicking)
+
+ * Added option to turn off referrer tracking (garvinhicking)
+
+ * Improved Smarty Plugin API so that a new function
+ serendipity_fetchPrintEntries() can be used within a template to
+ print more entries, and abstracted _fetchEntries()/_printEntries()
+ calls a bit more so that future plugins can easier modify the
+ global $entries array and control smarty/plugin execution
+ (garvinhicking)
+
+ * Inserted phpDoc code documentation for all Serendipity functions
+ (garvinhicking)
+
+ * Made remote RSS plugin be able to emit debug messages, enhance
+ bbcode plugin with new [strike] command, thanks to comdoxx!
+
+ * Fix wrong URL permalink detection when categories contain "." or "_"
+ characters (garvinhicking)
+
+ * Spamblock plugin can block comments when they only contain the
+ entry title (garvinhicking)
+
+ * Make RSS feeds templatable with Smarty .tpl files (feed_*.tpl), so
+ that you can easily customize your feeds layouts, inject nagging
+ ads and so on (garvinhicking)
+
+ * Fix certain plugins to correctly behave when using entry-
+ properties caching, by re-executing plugins that were not executed
+ because they "scramble" the true content. (garvinhicking)
+
+ * Introduced CUSTOM_ADMIN_INTERFACE that tells the user if the
+ template author also made a stylesheet for the admin interface
+ when selecting a theme (flotsam)
+
+Version 0.9.2 ()
+------------------------------------------------------------------------
+
+ * Fix server locale order to always first use the charset locale
+ instead of a generic locale. Fixes bug #1384978 (garvinhicking)
+
+ * Fix entry quicksearch on postgresql, where it returned the same
+ entry multiple times if it was contained in multiple categories.
+ Fix by Robert Treat, many thanks!
+
+ * Make templatechooser plugin emit a non-cached version of the
+ stylesheet, so that user-changes will be visible without manually
+ purging the cache (garvinhicking)
+
+ * Fix personal configuration no more able to lock you out of all
+ user groups (garvinhicking)
+
+ * File CSS stylesheets occasionally not returning the right path
+ (garvinhicking)
+
+ * Fix not being able to create users of the same userlevel when
+ being admin [workaround was to first create user with lower userlevel
+ and then edit the user account, which lead to propper permission
+ checks]
+
+ * Include a template's "config.inc.php" also when previewing an entry,
+ so that custom functions can be called (garvinhicking)
+
+ * Media uploader remembers last used upload directory (garvinhicking)
+
+ * Add "Precedence: Bulk" headers to sent mails (garvinhicking)
+
+ * Fix bug #1371893: Category write permissions are not properly
+ evaluated when writing into a category that a user has no
+ access to. Thanks to cydvicious! (garvinhicking)
+
+ * Fix bug #1371630: Write permissions to category are stored with
+ input data of the 'Read permissions' author listing.
+
+Version 0.9.1 (November 23rd, 2005)
+------------------------------------------------------------------------
+
+ * Fix renaming authors and categories to also properly update permalinks
+ that have no %id% column (garvinhicking)
+
+ * Fix configuration for non-admins to not properly store values like
+ blog Title (garvinhicking)
+
+ * Fix RSS import's timezone detection for ISO-8601 dates
+ (garvinhicking)
+
+ * Fix htmlarea when using UTF-8 charset on a ISO-8859-1 language
+ (garvinhicking)
+
+ * Statistics plugin now contains entries per author. Patch #1347639
+ by SvOlli
+
+ * Fix thumbnail generation for imageMagick when target image is
+ smaller than the target size, it should not be blown up
+ (garvinhicking)
+
+ * Fix spartacus plugin to not properly indicate updatable versions
+ of plugins (garvinhicking)
+
+ * Fix multi-media upload in Safari browser (jhermanns)
+
+ * Make calendar plugin also accept links to external events
+ (garvinhicking)
+
+ * Fix mod_rewrite rules to not differentiate on case-sensitivity for
+ authors, archives and category URLs (garvinhicking)
+
+ * Fix a bug in the serendipity_currentURL function when Serendipity
+ is installed in your HTTP root. This bug only effects the plugins karma,
+ entrysplit and multilingual on these installations.
+ Thanks to Richard Davey for spotting this! (garvinhicking)
+
+ * Fix showing preview image of hotlinked images. Thanks to Thomas
+ and RobA from the forums! (garvinhicking)
+
+ * Make Onyx RSS parser interpret HTTP redirecty. (Kristian Köhntopp)
+
+ * Added blogger.com importer by Jawish Hameed
+
+ * Fix visitor tracking in statistics plugin (garvinhicking)
+
+ * Fix incorrect entry count in categories sidebar plugin (was also
+ counting drafts)
+
+ * Set the correct reply-to name when sending comment mails, thanks
+ to RobA from the forums (garvinhicking)
+
+Version 0.9 (October 28th, 2005)
+------------------------------------------------------------------------
+
+ * Spamblock plugin can now check domains against the blogg.de
+ blacklist (http://spam.blogg.de/blacklist.txt). Deactivated by
+ default, the blacklist will be cached for 60 minutes before
+ being queried again. Thanks to the guys of blogg.de for notifying
+ us about their service! (garvinhicking)
+
+ * Fix categories plugin to properly work on pgsql installations.
+ Thanks to CaptainCrunch!
+
+ * Make serendipity_makeFilename function be UTF-8 aware.
+
+ * Fix syndication plugin not allowing to configure Atom 1.0 feed.
+ Thanks to Boris from the forums!
+
+ * Fix an issue of privilege escalation for non-admins (garvinhicking)
+
+ * Fix a parse error in the Importer, introduced in beta3
+ (garvinhicking)
+
+ * Show installed plugin versions in plugin overview (garvinhicking)
+
+Version 0.9-beta3 (October 21st, 2005)
+------------------------------------------------------------------------
+
+ * Syndication plugin: Do not show E-Mail adress in RSS feed by default
+ (garvinhicking)
+
+ * Fix Bug where siteConfiguration was not available to chief
+ editors. Thanks to Jannis! :) (garvinhicking)
+
+ * Fix Bug with category/author RSS feeds not properly recognize
+ their content when URL rewriting is off (garvinhicking)
+
+ * Enhanced entryproperties plugin to support entering custom field-
+ names (RFE #1328773) (garvinhicking)
+
+ * Also fetch and display entryproperties in the results of a search.
+ Fixes bug #1329379 (garvinhicking)
+
+ * Fix some dreaded "only variables can be returned by referenced"
+ PHP 4.4 notices on some minor occasions (garvinhicking)
+
+ * Fix problem with b2evo importer when db was not in the same db
+ as serendipity. Thanks to Judebert from the forums! (garvinhicking)
+
+Version 0.9-beta2 (October 13th, 2005)
+------------------------------------------------------------------------
+
+ * Fix "easy installation" leading to an error with language charsets.
+ Thanks to Heddesheimer from the forums for spotting this!
+ (garvinhicking)
+
+ * Improve Spamblock plugin to allow configuring author/url filters
+ straight from the "Comments" interface. (garvinhicking)
+
+ * Fix a bug in the fetchEntry() function which can lead to wrong
+ author/authorid settings when editing an existing entry. Thanks to
+ Martin Eichenberg! (garvinhicking)
+
+ * The output of the category plugin can now be styled via Smarty
+ templating (plugin_categories.tpl). Also you can now enable showing
+ the number of entries per category. (garvinhicking)
+
+ * Admin entry overview: Show pagination on bottom of the list and
+ allow filtering for showing drafts/publishs (garvinhicking)
+
+ * Fix spartacus plugin to be able to upgrade plugins (garvinhicking)
+
+ * Fix track exits plugin to redirect to the right URL when no URL-ID
+ was found. (garvinhicking)
+
+ * "Recent comments" plugin can be configured whether to show
+ trackbacks, comments or both. (garvinhicking)
+
+ * Spartacus can be configured to set file owner/permissions of
+ downloaded files via chown/chmod. Also fix UTF-8 problems in
+ non-UTF-8 environments. (garvinhicking)
+
+ * Allow per-author XML feeds, added bundled sidebar plugin to show
+ (multiple) authors. (jtate)
+
+ * Enhance entryproperties plugin so that single entries can hide
+ their content from the RSS feeds. (garvinhicking)
+
+ * Fix using the "realname" of the author in entry preview instead of
+ username. Also fix printing category info when no category was
+ assigned. Thanks to Manuel Charisius! (garvinhicking)
+
+Version 0.9-beta1 (September 29th, 2005)
+------------------------------------------------------------------------
+
+ * Change Onyx RSS parser and xml_parser_* functions to already specify
+ the source charset, so that PHP functions can do the recoding on
+ their own. Functionality differed on PHP4 and PHP5, this has now
+ been unified. Thanks a lot to W-Mark Kubacki!
+
+ * Enhance XHTML Cleanup plugin to recode double-encoded UTF-8 HTML
+ entities on NON-UTF8 blogs. Patch thanks to W-Mark Kubacki!
+
+ * Fix not showing thumbnail images in media database when thumbSuffix
+ is empty. Thanks to Brian J. France!
+
+ * Spamblock plugin can now define required comment fields. Also fix
+ parameter order in mt_rand() call, thanks to Jens Kubieziel
+ (garvinhicking)
+
+ * Plugin API now allows to validate config options via a "validate"
+ method, used by the plugin configuration panel. Need to set "validate"
+ and "validate_error" property bag attributes in your custom
+ introspect_config_item() calls, documented on
+ http://www.s9y.org/index.php?node=43#A13 (garvinhicking)
+
+ * Read/Write permissions for user-groups for specific categories.
+ (garvinhicking)
+
+ * Reduce memory usage by splitting up function files, optimizing
+ spartacus parsing. New files:
+ include/functions_rss.inc.php
+ include/functions_entries_admin.inc.php
+ (garvinhicking)
+
+ * Inserting links via the Serendipity Toolbar over the entry area
+ now also asks for a link tooltip/title.
+
+ * Plugin API no longer queries the filename of a plugin if it is internal
+ (increases performance because of less SQL queries) (garvinhicking)
+
+ * Onyx RSS parser now uses PEAR::HTTP_Request instead of fopen wrappers
+ to work on allow_url_fopen disabled hosts. Plugins like remoterss and
+ aggregator can now properly fetch RSS feeds on those hosts.
+ (garvinhicking)
+
+ * Make "/archive" view also recognize selected category. Sidebar
+ archive and calendar plugins also pay strict attention to which
+ category is selected. Helps "semi-multiple" blogs based on categories
+ a lot. (garvinhicking)
+
+ * Make media insertion dialog remember settings (via Cookies) and
+ insert return of the media database on cursor position.
+ (garvinhicking)
+
+ * Admin comment panels shows comments with their content and escaped HTML
+ instead of stripping all HTML. Only summary, if longer than 200 characters
+ will have stripped HTML because of otherwise invalid markup. (garvinhicking)
+
+ * Updated statistics plugin to track seperate visitor/referrer statistics.
+ Patch by Fredrik Sandberg, thanks a lot! (garvinhicking)
+
+ * Make category selector be nicer to Opera. Load the handling functions
+ only after the DOM load is completed. Add new "addLoadEvent"
+ functionality for future use to stack onload events. (garvinhicking)
+
+ * New Plugin API Hook "frontend_calendar" which allows to hook into
+ sidebar calendar creation. Needs to be enabled in the calendar
+ plugin configuration and needs a patch to the plugin_calendar.tpl
+ file which is done in all bundled themes. (Anthem)
+
+ * Atom 1.0 feed support (garvinhicking)
+
+ * MoveableType importer now also recognizes comments and trackbacks.
+ Tested with MT 3.17. (garvinhicking)
+
+ * Make template directory allow to contain subdirectories with more
+ templates. This allows you to symbolically link the "additional_themes"
+ CVS directory within your templates path, just like you can do
+ with the "additional_plugins" directory already (garvinhicking)
+
+ * Allow UTF-8 recoding using mb_* functions (Tadashi Jokagi)
+
+ * Allow to switch charsets ("Native" / "UTF-8"). (garvinhicking)
+
+ * Permissions: The permission "adminImagesViewOthers" now controls
+ whether an author is allowed to view images or not. In older Serendipity
+ versions, any author could always view any images, but only modify those
+ belonging to him. (garvinhicking)
+
+ * Plugin API: Added plugin hooks for category properties, made smarty
+ init function accept plugin values. See the plugin "Properties for
+ Categories" for example usage. The plugin allows to create sub-blogs
+ based on categories (garvinhicking)
+
+ * Categories plugin can be configured to only show specific sub-
+ categories and hide other categories when descending the tree
+ (garvinhicking)
+
+ * "Edit entries" panel can now delete entries and returns to the
+ originating panel. Also it now utilizes Cookies (via JS) to remember
+ the last used settings (sortorder, filters) (garvinhicking)
+
+ * Added WordPress-PostgreSQL importer, by Devrim Gunduz
+
+ * RFE #1231423: Allow to change the author of an entry with the
+ "entryproperties" plugin. (garvinhicking)
+
+ * Templates can now be handled via Spartacus (garvinhicking)
+
+ * Plugin Manager: Improve Spartacus interface and include plugin
+ categories (garvinhicking)
+
+ * Support different WYSIWYG editors via new plugin hooks. TinyMCE
+ plugin available. (garvinhicking)
+
+ * Allow language files to define $i18n_filename_from and _to array
+ so that each language can individually rewrite URL characters.
+ Only the Russian language currently makes use of this.
+ (garvinhicking)
+
+ * fixed serendipity_traversePath() - PHP5 issue with array_merge()
+ Thanks to jdhawk for the fix (flotsam)
+
+ * fixed wrong display of "found X entries matching your search" in
+ genpage.inc.php (flotsam)
+
+ * Added fix for wrong language in permission groups (were created in the
+ language that the browser of the installing user has (flotsam)
+
+ * Added Voodoo Wiki/XML importer, hooks into static page plugin.
+ (Tim Putnam)
+
+ * Make comment deletion return to the originating page, allow to
+ display trackbacks and normal comments at once (new default).
+ Bugs #1226440, #1226439 (garvinhicking)
+
+ * Fix multi-category selector for Konqueror (garvinhicking)
+
+ * Support use of Boolean search mode in MySQL. Is activated when using
+ special characters like "()~*+-<>. Syntax see
+ http://dev.mysql.com/doc/mysql/en/fulltext-boolean.html.
+ (garvinhicking)
+
+ * Apply patch to allow usage of Feedburner RSS feeds, by Anders Clerwall
+
+ * Fixed using "_" instead of "-" in the approve trackback/comments
+ URLs. (garvinhicking)
+
+ * Introduce permission groups with customizable permission sets.
+ (garvinhicking)
+
+ * Make bblog importer recognize trackbacks. Thanks to Hanno!
+
+ * Spartacus plugin can now properly handle plugins which contain both
+ sidebar and event plugins in one directory (garvinhicking)
+
+ * TEMPLATES: Added div.serendipity_section_(comments|trackbacks|commentform)
+ containers inside the Default Template's entries.tpl file to be
+ able to customize certain containers via CSS.
+ Solves RFE #1210889 (garvinhicking)
+
+ * TEMPLATES: Added div.serendipity_search_* wrapping elements to be
+ able to style the search result messages specifically. Added new
+ Smarty variables $searchresult_* (default/content.tpl).
+ Solves RFE #1210676 (garvinhicking)
+
+ * Fix editing a draft article to be properly displayed as draft
+ in PostgreSQL setups. Thanks to Penny Leach! (garvinhicking)
+
+ * Localized the string "Reply" which occured inside some templates.
+ (s/Reply/{$CONST.REPLY}/) (garvinhicking)
+
+ * Added swedish translation by Torbjörn Hedberg, Added european
+ portugues translation by Joao Palhoto Matos, Added hungarian
+ translation by Posz Marton
+
+ * New configuration item: Gregorian or Jalali Calendar output.
+ Patch by Omid Mottaghi
+
+ * New personal configuration item: "Forbid creating entries" to
+ allow authors to be logged in, but not create any entries. Meant
+ to be used in conjunction with serendipity_plugin_adduser for
+ user self-registration where you want to allow posting comments
+ to registered users only. (garvinhicking)
+
+ * Added Custom Permalink URL days, months, years, etc. to the
+ Serendipity Configuration ("Paths") panel.
+ If you use the external plugin serendipity_plugin_authors or
+ serendipity_event_linktoolbar, you need to update those to
+ their latest versions to work with the new changes.
+ (garvinhicking, tomsommer)
+
+ * Importers can now properly import data if the source database is
+ not the same as the target database (garvinhicking)
+
+ * Use $smarty.const. to access constants instead of $CONST within
+ Smarty templates; this is replaced using a Smarty prefilter, so
+ that within templates you can still use the $CONST shortcut
+ (garvinhicking)
+
+ * Added new event hook to the trackback sending facility so that
+ plugins like serendipity_event_trackback (additional_plugins)
+ can send trackbacks to Blogs without RDF-metadata (garvinhicking)
+
+ * Allow to view and fetch multiple categories. Categories plugin
+ can allow viewers to select multiple categories to view.
+ (garvinhicking)
+
+ * Added hooks into the image selector admin popup for plugins to
+ support additional options. serendipity_event_imageselectorplus
+ accesses those hooks already. (garvinhicking)
+
+ * Categories plugin now offers to sort by category name, description
+ or creation date. (garvinhicking)
+
+ * Give HTML Nugget the option for a second "description" attribute
+ that is used in the Plugin Configuration section, so that HTML
+ nuggets with an empty title still display additional information
+ to distinct multiple nuggets from another (garvinhicking)
+
+ * New entryproperties plugin option: Hide an entry from the frontpage
+ (garvinhicking)
+
+ * Media manager: Allow to upload as many files as you want via
+ JavaScript interaction and "add more images" button. (garvinhicking)
+
+ * "Comments" Sidebar plugin can now have a custom title
+ (garvinhicking)
+
+Version 0.8.5 (September 29th, 2005)
+------------------------------------------------------------------------
+
+ * More Security: When changing the password in your personal preferences,
+ you need to insert the old password. Secure backend forms with extra
+ token checks to bypass XSRF attacks. Thanks to Nenad Jovanovic for
+ contacting me about this issue! (garvinhicking)
+
+ * Fix JS errors in admin comment overview for IE6 (garvinhicking)
+
+ * Fix showing any entry-id related sidebar plugins, when a selected
+ entry is not visible (permissions/drafts/...) by unsetting the
+ global $serendipity['GET']['id'] in that case. (garvinhicking)
+
+ * Fix "DATA_TRACKBACK_APPROVED" showing of constants when approving
+ trackbacks/comments instead of the properly formatted message.
+ (Would've required a template change for a "pretty solution", but
+ the current fix is a BC-compatible fix by defining constants a bit
+ differently) (garvinhicking)
+
+ * Default Admin Stylesheet no longer uses direct height: assignment,
+ but padding instead. This should get rid of occasional overlapping
+ of menu items. Thanks a lot to Ognyan Kulev for the solution to this!
+
+ * Fix putting sticky entry on the last page in postgreSQL setups.
+ Thanks to Nate Johnston for working this out! (garvinhicking)
+
+ * Fix file upload bug which limited upload filesize to 3 million bytes
+ (2.86MB) regardless of PHP configuration. Maximum upload filesize
+ should now be properly bound by PHP configuration settings.
+ (wesley)
+
+ * Fix cosmetic warning message about missing S9Y_PEAR_PATH when
+ installing. (garvinhicking)
+
+Version 0.8.4 (August 19th, 2005)
+------------------------------------------------------------------------
+
+ * Add HTML information about calendar arrows image size to bypass
+ large sizing in Internet Explorer. Thanks to frodeste from the
+ Forums!
+
+ * Hide title of an entry in the frontend, if entry is draft and not
+ displayed. Fixes Bug #1260667 (garvinhicking)
+
+ * Unbundle Serendipity XML-RPC functions from the bundled distribution.
+ You will now need to install the additional plugin "Post via XML-RPC"
+ (serendipity_event_xmlrpc) to make entry posts via this interface.
+ The ongoing security issues with this interface and the little usage
+ of this functionality by the broad public have forced this move.
+ After installing this plugin you can use the same URL and nothing
+ will change for XML-RPC users. (garvinhicking)
+
+ * Optionally allow using a local PEAR installation. Set
+ $serendipity['use_PEAR'] = true
+ in your serendipity_config_local.inc.php or serendipity_config.inc.php
+ file. The required packages can be found in the
+ bundled-libs/.current_version file. (garvinhicking)
+
+ * Append the comment id to the mail that is sent to subscribers of
+ an entry, so that they can jump to the submitted comment immediately.
+ (garvinhicking)
+
+Version 0.8.3 (August 4th, 2004)
+------------------------------------------------------------------------
+
+ * Upgraded bundled libs:
+ Cache_Lite to 1.5.1
+ HTTP_Request to 1.2.4
+ Net_CheckIP to 1.1
+ Net_Socket to 1.0.6
+ Net_URL to 1.0.14
+ PEAR to 1.3.5
+ Text_Wiki to 0.25.0
+ XML_RPC to 1.3.3
+ Fixing several bugs and vulnerabilities. (nohn)
+
+ * Make categories sidebar plugin show categories' description, not the
+ name on mouse over (garvinhicking)
+
+ * Added new config option "blogMail" that specifies the blog's used
+ From: E-Mail address. If not given, falls back to not specifying
+ From: field and using mailserver default ("wwwrun", "nobody", ...)
+ (garvinhicking)
+
+ * Fix possible exploit that sends comments to the blog owner and others
+ with arbitrary mailheaders and a link to invalid entries. Thanks to
+ Sebastian Raible! (garvinhicking)
+
+ * Allow plugins to contain more than one HTML nuggets which can be
+ WYSIWYGized. (garvinhicking)
+
+ * Fix editing a draft article to be properly displayed as draft
+ in PostgreSQL setups. Thanks to Penny Leach! (garvinhicking)
+
+ * Fixed possible XSS in comment input validation, thanks to
+ Ilia Alshanetsky
+
+ * Full Korean language support available! Translations done for:
+ - main language file
+ - all core plugins
+ - Kubrick template
+ (wesley)
+
+ * TEMPLATES: New core hook "frontend_footer" is introduced and is
+ added to index.tpl:
+ {serendipity_hookPlugin hook="frontend_footer"}
+ (wesley)
+
+ * TEMPLATES: Added plugin hooks "entries_header" and "entries_footer"
+ to the files entries_archives.tpl and entries_summary.tpl:
+ {serendipity_hookPlugin hook="entries_header"}
+ <div class='serendipity_entryFooter' style="text-align: center">
+ {serendipity_hookPlugin hook="entries_footer"}
+ </div>
+ (wesley)
+
+Version 0.8.2 (June 29th, 2005)
+------------------------------------------------------------------------
+
+ * fixed remote code execution vulnerability. Thanks to Gulftech
+ Research for pointing out that bug and Stefan Esser for helping
+ fix it (nohn)
+
+ * Updated Spartacus to most recent version (nohn)
+
+ * fixed serendipity_traversePath() - PHP5 issue with array_merge()
+ Thanks to jdhawk for the fix (flotsam)
+
+ * CSS does no longer emit cache-restricting headers, so that the
+ stylesheets can be cached by the browser for followup-requests
+ Thanks to Sencer for pointing this out! (garvinhicking)
+
+ * Patch/Bug #1209410 by swiesinger: When using shortcut admin URL,
+ use https:// when specified by user
+
+ * Fix deleting categories when having privileges but not being
+ administrator (Patch #1205347, many thanks to Penny Leach)
+
+ * Increased level of output message from the Spartacus plugin
+ (garvinhicking)
+
+ * Patched XML-RPC functions, thanks to Tim Putnam. This should enable
+ XML-RPC services to properly fetch existing articles and edit them.
+
+ * Fix Plugin API call performing too many unneeded SQL queries
+ (garvinhicking)
+
+ * Fix missing authorname when previewing entry. Thanks to winkiller,
+ aquatic, thomas, wurstprinz and hansi for fixing this!
+
+Version 0.8.1 (May 17th, 2005)
+------------------------------------------------------------------------
+
+ * Fix missing PDF thumbnail creation (imagemagick only)
+ (garvinhicking)
+
+ * Fix possible arbitrary media file upload for editors
+ (garvinhicking, sesser, nohn)
+
+ * Fix possible XSS when using the templatedropdown or shoutbox
+ plugins (garvinhicking)
+
+ * Fix pagination of "Entries by Author" (garvinhicking)
+
+ * Fix RSS 1.0 missing top-level namespace (garvinhicking)
+
+ * Deactivated gzip compression by default, as in certain server
+ setups it creates problem with double-encoding or missing
+ ob_gzhandlers. (garvinhicking)
+
+ * Allow the serendipity_event_trackback additional trackbacks plugin
+ to send trackbacks even if your entry did not contain at least one
+ link. Also fix not sending trackbacks containing "#" fragments
+ (garvinhicking)
+
+ * Do not display Sidebar titles if they are not set for the bundled
+ default, kubrick and wp templates (sidebar.tpl) (garvinhicking)
+
+ * Fix error "Warning: ob_start(): output handler 'ob_gzhandler'
+ cannot be used after 'URL-Rewriter'" which can occur on installations
+ where session.use_trans_sid is activated and Serendipity's gzip
+ compression is used. (garvinhicking)
+
+ * Adjusted checking for "convert" binary in installation for
+ Windows servers. Thanks to BobRock! (garvinhicking)
+
+ * BBCode plugin: Allow =, ~ and ! URL characters (garvinhicking)
+
+ * Added romanian translation by Alexandru Szasz
+
+ * Some IIS webserver compatibility for $_SERVER variables. This should
+ ensure most basic operations. (garvinhicking)
+
+ * RSS feed does no longer initialize a session and thus now causes
+ proper cachability for Conditional Get (garvinhicking)
+
+ * Fix issues on Windows installations: %T strftime-Option not known
+ when saving a plugin and emitting timestamp (was empty),
+ redirecting to a wrong URL for installation with '\' instead of
+ '/' path indicators. Thanks to Hendy Irawan! (garvinhicking)
+
+Version 0.8 (April, 15th 2005)
+------------------------------------------------------------------------
+
+ * Added icelandic translation by Örn Arnarson
+
+ * Fixed a possible SQL injection issue in Tracking methods. Also
+ fixed possible XSS exploit in BBCode markup, if enabled for
+ comments. Thanks to ADZ Security Team for detecting this. Next
+ time please give us more time to respond to your public announced
+ exploits. ;) (garvinhicking)
+
+ * Reverted more JavaScript-patches introduced in beta5 to make
+ the extended body text visible when using WYSIWYG editor
+ (garvinhicking)
+
+ * Fix SQLite bug when fetching authorname in quicksearch. Thanks to
+ Sven Oliver Moll!
+
+Version 0.8-beta6 (April 8th, 2005)
+------------------------------------------------------------------------
+
+ * Remove unique constraint for url_idx on the referrer suppress table
+ and replaced it by a simple index. Fixes fatal errors on postgresql
+ (garvinhicking)
+
+ * Fix inserting media manager items: Non-images were not properly
+ detected (garvinhicking)
+
+ * Fix broken SQL for inserting comments which showed in SQLite/PGSql
+ and got introduced in beta5. (garvinhicking)
+
+ * Reverted JavaScript-patches from nohn introduced in beta5 to make
+ IE6 again show the category and extended entry toggle buttons
+ (garvinhicking)
+
+Version 0.8-beta5 (April 1st, 2005)
+------------------------------------------------------------------------
+
+ * Fix XMLRPC problems for getting existing posts. Thanks a lot to
+ TimothyP from the forums! (garvinhicking)
+
+ * Fix directory creation error; the mode 1777 failed on several setups,
+ so we use 0777 again (garvinhicking)
+
+ * Fix installer not being able to go above page 2 on special setups
+ with register_globals On (garvinhicking)
+
+ * Fixed buggy referrer tracking for MySQL (Bug #1170251)
+
+ * Upgrade Smarty to v2.6.9 (tomsommer)
+
+ * Added 'Cache-Control: no-cache' header to our CSS file; this may
+ fix IE bugs where CSS was only loaded after 1-2 reloads.
+ (TomSommer)
+
+ * Fixed a XMLRPC problem for metaWeblog_newMediaObject() to properly
+ save a binary file. Thanks to marco from the forums! (garvinhicking)
+
+ * Fixed possible SQL-Injection in Pingbacks (nohn; reported by Stefan
+ Esser)
+
+ * Kubrick template: Fix comments always printed as 'Anonymous',
+ put entry/extended parts not together (garvinhicking)
+
+ * Fix installer: Serendipity does not run with magic_quotes_runtime
+ set to on. Emit warning and try to switch off the directive.
+ (garvinhicking)
+
+ * Updated chinese language files, thanks to Andrew Huang
+
+Version 0.8-beta3/4 (March 15th, 2005)
+------------------------------------------------------------------------
+
+ * Drop SQL index on comment's body. This was not used in our code,
+ and caused trouble with large comments on pgsql and MySQL.
+ (garvinhicking)
+
+ * Monthly entry summary shows correct date header (garvinhicking)
+
+ * RSS / Syndication plugin now offers to hide E-Mail adresses from
+ XML feeds (garvinhicking)
+
+ * After installing a plugin you will be either redirected to
+ plugin config or to plugin overview, so that an accidental
+ browser refresh does not install the plugin twice
+ (garvinhicking)
+
+ * Fixed wrong date format in portuguese and czech languages
+ (garvinhicking)
+
+ * Fix buggy wordfiltering of spamblock plugin (garvinhicking)
+
+ * Fixed variable name for all markup plugins. They previously used
+ localized versions like 'Extended entry' and now use a proper
+ language independent string. The upgrader takes care of replacing
+ the variables so that no options will get lost (garvinhicking)
+
+ * Fixed bug with Markup plugins not getting their right settings
+ under certain circumstances (only for autologin cookie users)
+ (garvinhicking)
+
+ * Correctly detect 500 Internal Server Error when installing
+ (garvinhicking)
+
+ * Emitting language headers only if no headers were sent yet.
+ This fixes problems with embedded serendipity installations.
+ (garvinhicking)
+
+ * Fix postgresql query problem, when using the entryproperties
+ plugin and browsing the calendar with category restriction(s).
+ Thanks to Larry Rosenman for helping out with this!
+ (garvinhicking)
+
+ * Introduce "div.container_X" CSS class for sidebar plugin items.
+ X is substituted with the plugin classname, like
+ "serendipity_plugin_categories". This class is contained in the
+ 'default' template and all templates based on the sidebar.tpl
+ file. (garvinhicking)
+
+ * Fixed broken category pagination for "Newspaper" template
+ (garvinhicking)
+
+ * Bundle Tom Sommer's port of the Kubrick template
+
+ * Fix hardcoded "templates" directory reference inside the admin
+ style selector.
+
+ * Updated czech language files, thanks to Josef Klimosz
+
+ * Updated persian language files, thanks to Omid Mottaghi
+
+ * Do not display rotation/resize image manipulation methods for
+ hotlinked images (garvinhicking)
+
+ * Do not use "layout.php" in Default template for upgrading users
+ who did not delete that file from their directory (because the
+ upgrade may not have sufficient write privileges to remove the
+ file) (garvinhicking)
+
+ * Make RSS import parse ISO-8601 dates (like
+ '2004-11-24T22:43:08-05:00'), as PHPs strtotime() does not parse
+ that. (garvinhicking)
+
+Version 0.8-beta2 (March 5th, 2005)
+------------------------------------------------------------------------
+
+ * Show "create entry" toolbar for plugins like the Emoticon Chooser
+ also when WYSIWYG editor is enabled (garvinhicking)
+
+ * Do not display template engines in the template selector
+ (tomsommer)
+
+ * Fix HTML escaping of special characters for comments
+ (garvinhicking)
+
+ * Fix wrong username displayed in RSS comment feeds (garvinhicking)
+
+ * Fixed bug that may occur on few plugins executing other plugins,
+ like serendipity_event_entrypaging [this is the only bundled or
+ internal plugin affected by the issue] (garvinhicking)
+
+Version 0.8-beta1 (March 4th, 2005)
+------------------------------------------------------------------------
+
+ * Added Persian language and template for RTL-Languages by Omid
+ Mottaghi
+
+ * Fixed Windows path problems when synching with media manager.
+ Thanks to Thomas Klinger for helping!
+ (garvinhicking)
+
+ * Media files can now be hotlinked to foreign servers and thus not
+ using bandwidth from your own server. Media files can now also
+ be searched by name. Thanks to Chris Chan for this feature!
+
+ * Added ability to define a real name for each author. This is the
+ name seen by readers of the blog. The username is now only used for
+ logging into the administration suite (tomsommer)
+
+ * Added ability to toggle the amounts of comments per page within
+ a dropdown instead of needing to edit a Serendipity core file.
+ (garvinhicking)
+
+ * New configuration directive to tell Serendipity if the baseURL
+ shall be autodetected. This can be useful if you use multiple
+ domainnames listening on one blog installation. (Note: This auto-
+ detection was previously already enabled if using 'embedded mode')
+ (garvinhicking)
+
+ * Fixed bug for postgreSQL: Comments for entries within multiple
+ categories showed up as many times as the entry was within multiple
+ categories. Thanks to rickmans from the forums for detecting this.
+ (garvinhicking)
+
+ * Added configuration directive to allow visitor language negotiation
+ via browser language. (garvinhicking)
+
+ * Added native importers to migrate entries, comments,
+ categories and authors for:
+ - geeklog 1.3.11
+ - sunlog 0.4.4
+ - b2Evolution "Paris"
+ - boastMachine 3.0
+ - Textpattern 1.0rc1
+ - pMachine Pro 2.4
+ - bBlog 0.7.4
+ - Nucleus 3.15
+ - WordPress 1.5 / 1.2
+ - phpBB
+ (garvinhicking)
+
+ * Remember last state of media library (sort order, files per page,
+ directory, sortfield), via JavaScript+Cookies (garvinhicking)
+
+ * The config option 'extCSS' has been removed. It is now re-
+ commended to set external stylesheets in the template's index.tpl
+ file, or to @import it within the templates style.css file.
+ (garvinhicking)
+
+ * Personal Configuration allows to set defaults for creating new
+ entries (allow comments, moderate comments, publish/draft)
+ (garvinhicking)
+
+ * When deleting a comment with nested comments, move nested ones
+ to the parent of the deleted comment (fixes Bug #1079349)
+ (garvinhicking)
+
+ * Spamblock plugin allows to moderate/reject comments made via APIs
+ like wfw:commentApi or trackbacks, but still allow normal comments
+ (garvinhicking)
+
+ * Fixed SQL index key creation on fresh installation (garvinhicking)
+
+ * Upgraded htmlarea WYSIWYG editor to latest version. Entering
+ links now properly works, as finally does Copy+Paste from Mozilla!
+ (garvinhicking)
+
+ * New pretty URLs "/blog/serendipity.css" and
+ "/blog/serendipity_admin.css" as links to the Stylesheets.
+ (garvinhicking)
+
+ * Chief users are not allowed to hide plugins installed by other
+ users. (garvinhicking)
+
+ * Forbid uploading of active content files (.php, .shtml, ...) as
+ user without Administrator privileges (garvinhicking)
+
+ * Allow searching of the blog with pretty URLs, allow pagination
+ of searched entries (garvinhicking)
+
+ * Added handling of CONVERT BREAKS-directive in MT-Importer. Do the
+ whole importing process in a transaction to ensure either all or
+ no entires are imported. (pilif)
+
+ * Added hook for external authentication. First implementation draft
+ of LDAP auth added to additional_plugins CVS (garvinhicking)
+
+ * Added an extended property to disable nl2br() for specific
+ entries (pilif)
+
+ * Added korean language by Erich Iseli (garvinhicking)
+
+ * Change RSS feed's <guid> element to contain isPermaLink=false.
+ Only the link of the <link> element should be used as Permalink.
+ This fixes problems with copying links to send a trackback to from
+ RSS readers/clients that used the <guid> element as Permalink.
+ (garvinhicking)
+
+ * Fixed flaw in the function used to validate path-names.
+ Thanks to raperu2000 from the forums! (garvinhicking)
+
+ * Added Finnish language by Mauri Sahlberg (garvinhicking)
+
+ * Added Japanese language by Tadashi Jokagi (garvinhicking)
+
+ * Add ability to see all entries made by a single user (tomsommer)
+
+ * The "edit entry" overview will now highlight entries to be
+ published in the future (garvinhicking)
+
+ * Allow authors to easier reset the time for each entry, by clicking
+ an icon (tomsommer)
+
+ * Added new plugin, enabled by default, to enforce maximum browser
+ compatibility. The only thing which 'serendipity_event_browsercompatibility'
+ currently does is to add the transparent PNG Behaviour for IE.
+ (garvinhicking)
+
+ * 'Track Exits' plugin will no longer be enabled by default, as
+ requested and discussed on the Mailinglist. (garvinhicking)
+
+ * Added Simplified Chinese (GB2312, UTF-8) and Traditional Chinese
+ (Big5, UTF-8) languages, thanks to skyroam!
+
+ * Split up the configuration and the installer. Add a diagnostic
+ page to the installer. Allow for simple or expert installation
+ (tomsommer)
+
+ * Fix problem in Imagemagick detection when checking the apache PATH,
+ added /usr/local/bin to possible locations of Imagemagick
+ (tomsommer)
+
+ * Spam Protector can now log failed comments to either plaintext-
+ file or database. Dedicated to all those trigger-happy logfile-
+ statistic-hunters out there... (garvinhicking)
+
+ * When using ImageMagick, create a thumbnail of a PDF for media
+ browsing (nohn, garvinhicking)
+
+ * More than one plugin cannow be saved per plugin/ subdirectory,
+ event + sidebar plugins can be put together in one directory.
+ Names of subdirectores are irrelevant now, but the filenames
+ need to match 'serendipity_(event|plugin)_*.php'. (garvinhicking)
+
+ * When entering a comment, the referring URL to the blog is saved
+ (garvinhicking)
+
+ * Use mbstring extension where available to correctly utilize PHPs
+ string functions for multibyte chars. This is needed for UTF-8
+ only languages like Chinese (garvinhicking)
+
+ * Move the page where you change your details out of the
+ configuration and into its own menuitem and page (tomsommer)
+
+ * Allow the usage of persistent database connections (tomsommer)
+
+ * New configuration directive to let user choose if entries in the
+ future are hidden (default) or displayed. (garvinhicking)
+
+ * Add ability to gzip compress pages (tomsommer)
+
+ * Separate administration CSS layout from the rest of the blog
+ (tomsommer)
+
+ * Merge Administration and Author suite into one (tomsommer)
+
+ * Allow $plugin->get_config([var]) to return the default value from
+ introspect_config_item() if no value is found (tomsommer)
+
+ * Preview/Saving entries now utilizes iframe techniques to faster
+ send visual feedback and properly be able to style the entry
+ preview independent from the Admin CSS code. [For older browsers,
+ this can be turned of in serendipity_config.inc.php]
+ (garvinhicking)
+
+ * Allow sending trackbacks and tracking links of https URLs. Fix
+ "Entry's Links" plugin to properly display trackable links.
+ (garvinhicking)
+
+ * Spamblock plugin can be configured to hide E-Mail adresses when
+ displaying comments. (garvinhicking)
+
+ * FIX: CSS classes in the comment manager was always set to uneven
+ (tomsommer)
+
+ * When a document does not exist and Serendipity has no idea how
+ to create it, a 404 (not found) is sent instead of a 200 (OK)
+ (tadpole9)
+
+ * serendipity_config_local.inc.php can now contain user-defined
+ variables, which are not overwritten when changing Configuration.
+ (garvinhicking)
+
+ * Improved overall performance of Plugin API, code improvements,
+ query optimizing. Added with the entryproperties caching plugin,
+ this boosts performance by about 15-30%. You are also adwised
+ to use opcode-caches, if available [APC, Zend Cache, Turck, ...]
+ (garvinhicking)
+
+ * Entries can be cached by the 'entryproperties' plugins. This
+ caching interacts with other plugins: When cacheable plugins are
+ added, the cache is updated, as well as when an entry is modified.
+ When the plugin is first installed, it creates the cache for the
+ latest 15 entries; you can create a cache of all entries using
+ the admin interface. (garvinhicking)
+
+ * New CSS classes for distinction of authors within entries/comments:
+ .serendipity_entry_(author|comment)_USERNAME, applied to each
+ entry container, where USERNAME is replaced with the name of the
+ author.
+ .serendipity_entry_author_self, applied to the entry container if
+ the currently logged in user is the author of an entry
+ .serendipity_comment_author_self, applied if the commenting user-
+ name matches the owner of the entry.
+ (garvinhicking)
+
+ * Rewrote importer to use import modules and class framework
+ (tomsommer)
+
+ * New language: Traditional Chinese (Big5). Thanks to Andrew Huang!
+
+ * Recent Entries plugin: New configuration directive to not display
+ items already shown on frontpage. Thanks to Sebastian Raible!
+ (garvinhicking)
+
+ * Articles per page (default 15) can now be set within Configuration
+ (garvinhicking)
+
+ * Plugin API: Event plugins using the 'entry_display' hook to set
+ an entries page to not render (via 'clean_page') now need to use
+ the new hook 'entries_header' for their output; the 'clean_page'
+ variable still needs to be set within the hook 'entry_display'
+ though. (garvinhicking)
+
+ * Support for adding timezone offsets in configuration
+ (garvinhicking)
+
+ * Serendipity cannow be used with "JustBlogIt" extension. Just use
+ this URL:
+ http://yourblog/serendipity_admin.php?serendipity[adminModule]=entries&serendipity[adminAction]=new&serendipity[title]=%TITLE%&serendipity[body]=%TEXT%&serendipity[url]=%URL%
+ (garvinhicking)
+
+ * Plugin serendipity_event_spamblock: Added SURBL
+ (http://www.surbl.org/) Support. Will now reject any comment that
+ contains a blacklisted URL, if enabled. (nohn)
+
+ * Do not allow to view extended article when item is set to "publish"
+ but timestamp is in the future (garvinhicking)
+
+ * RSS Export orders entries in reverse ID order for easier
+ importing (garvinhicking)
+
+ * Cleaned up directory structure, created new subdirectories,
+ split up functions file. See docs/CHANGED_FILES for details if
+ you have external dependencies on removed files (like custom
+ plugins, themes, or special embedded usage) (garvinhicking)
+
+ * "Recent Entries" Plugin can now have userdefined title and only
+ show entries of a specific category (+ subcategories).
+ (garvinhicking)
+
+ * Media browser now allows to immediately upload a file from the
+ entry creation screen and use that file/image easily
+ (garvinhicking)
+
+ * exit.php redirection will emit a 301 Moved Permanently header for
+ the new location. (garvinhicking)
+
+ * Reworked plugin manager for easier plugin installation (tomsommer)
+
+ * Karma plugin works with enabled entryproperties plugin and can
+ be configured to only show Hit-statistics for an entry without
+ the voting ability (garvinhicking)
+
+ * Routine for autodetecting links within an entry will now also
+ allow images to be used as description. (garvinhicking)
+
+ * Implement plugin install() and uninstall() methods that are called
+ on installation and removal of a plugin (tomsommer)
+
+ * Improve overall rewrite path syntax (tomsommer)
+
+ * Allow the display of month, weeks and days in archive sidebar
+ (tomsommer)
+
+ * Add ability to display entries based on week (tomsommer)
+
+ * Use rewrite paths in the syndication plugin (tomsommer)
+
+ * Added import tool for Movable Type data files and WordPress
+ databases. (tadpole9)
+
+ * Syndication plugin offers to show full feed including extended
+ entry (garvinhicking)
+
+ * serendipity_event_entryproperties now supports entry caching to
+ pregenerate the full article and display that instead of
+ assigning event plugins time and again (garvinhicking)
+
+ * New plugin serendipity_event_entryproperties: You can now define
+ any property to an entry and query it. Currently implemented
+ are "sticky posts" and "private/public/members-only" entries.
+ (garvinhicking) [DB Layout change - > 0.8-alpha2]
+
+ * Fixed bug #1031059 - Trackbacks to a link will not be sent more
+ than once (garvinhicking)
+
+ * Added "Ping-o-Matic!" to weblogping plugin (garvinhicking)
+
+ * RSS Feed export will not contain rewritten URLs using event
+ plugins (garvinhicking)
+
+ * Updated upgrader to use MySQL as fallback when looking for
+ appropriate database updates and none was found for the selected
+ database type (tomsommer)
+
+ * Added support for Smarty Templating. (garvinhicking, tomsommer)
+
+Version 0.7.1 (December 2nd, 2004)
+------------------------------------------------------------------------
+
+ * Fixed captcha string variation on some setups by explicitly seeding
+ the randomness (garvinhicking)
+
+ * Fixed cross site scripting vulnerability. Thanks to Stefan Esser
+ for reporting this issue. (nohn, garvinhicking)
+
+ * Do not show entries of the future when entering their direct URL
+ (garvinhicking)
+
+Version 0.7 (November 8th, 2004)
+------------------------------------------------------------------------
+
+ * Fixed concatenation syntax on PostgreSQL, only used by plugin
+ "Entrylinks". (garvinhicking)
+
+ * SQLite database name was not saved properly and could get set to
+ a wrong value when updating configuration (garvinhicking)
+
+ * Better detection if Apache ErrorDocument can be used
+ (garvinhicking)
+
+ * Fixed PostgreSQL index key names (names are now unique, and
+ combined indizes on the entries text-columns are no longer set)
+ (jtate, garvinhicking)
+
+ * Fixed bug with MySQL 3.x where the category was not properly set
+ when editing (garvinhicking)
+
+ * Fixed karma plugin to not track clicks when previewing an entry
+ (garvinhicking)
+
+ * Fixed some counting problems in statistics plugin (garvinhicking)
+
+ * Fixed comment counter for deleting non-approved comments
+ (tomsommer, griffinn)
+
+Version 0.7-rc1 (October 20th, 2004)
+------------------------------------------------------------------------
+
+ * Fixed possible HTTP Response Splitting security issue. Thanks to
+ ChaoticEvil for reporting! (jannis, garvinhicking)
+
+Version 0.7-beta4 (October 14th, 2004)
+------------------------------------------------------------------------
+
+ * Bug #1016342 - Fixed RSS UTF8 decoding for remote RSS plugin.
+ (garvinhicking)
+
+ * Fixed some wrong calculations in the statistics plugin, thanks
+ to David DeLauro! (garvinhicking)
+
+ * Fixed entry count and category association bugs using SQLite
+ (garvinhicking)
+
+ * Fixed some error notices for installation with postgreSQL regarding
+ field index names (garvinhicking)
+
+ * Fixed wrong mod_rewrite rules to properly redirect to pages.
+ (garvinhicking)
+
+ * Trackback moderation support in admin panel, make captcha plugin
+ only work for comments (garvinhicking)
+
+ * Bug #1037122: BlogPDF plugin will me less strict in file inclusion
+ (garvinhicking)
+
+ * Fixed wrong entry id displayed in RSS comment feed. Thanks to
+ romulus! (garvinhicking)
+
+ * Fixed file permission checks when removing images inside media
+ manager. Thanks to Thomas from the forums! (garvinhicking)
+
+ * RSS Import: New option 'full body only', so that imported RSS
+ contents can be forced to only show up in the full body. Fixed
+ buggy substr() logic which may cause nasty splitting from entry
+ body to extended entry. (garvinhicking)
+
+ * Extend filter in commment moderation to allow the user to select
+ which types of comments he wants displayed "All", "Only approved",
+ "Need approval" (tomsommer)
+
+ * Upgraded Spam Protector event plugin. Optionally:
+ - use Captchas - images with string sequences which a
+ user has to enter before his comment is accepted.
+ - restrict captchas to entries older than X days
+ - auto-moderate comments to entries older than X days
+ - auto-moderate comments depending on their amount of
+ contained links.
+ - reject comments depending on their amount of
+ contained links.
+ - able to log rejected/moderated comments to a plaintext file
+ - fallback method for non-gdlib-enabled hosts
+ (garvinhicking)
+
+ * Fixed wrong implode() arguments for error reporting in installer
+ (garvinhicking)
+
+ * Fixed windows detection for stripping backslashes out of
+ uploaded files. Thanks to Thomas! (garvinhicking)
+
+Version 0.7-beta3 (September 21st, 2004)
+------------------------------------------------------------------------
+
+ * Bug #1031444 - Fixed postgreSQL error (for older versions of
+ pgsql) when creating categories (garvinhicking)
+
+ * Fixed wrong XHTML entities in remoterss and shoutbox plugin. Thanks
+ to Stephan van Beerschoten! (garvinhicking)
+
+ * Fixed bug #1030581: Category sidebar plugin now properly displays
+ only categories of selected author (garvinhicking)
+
+ * Fixed XML RPC API to set categories when posting via BlogJet
+ or other interfaces (garvinhicking)
+
+ * Fixed invalid standard-SQL 'CURRENT_DATE' to use a real date
+ string. Fixes Exit-Tracking with SQLite. (garvinhicking)
+
+ * Fixed footer printing wrong amount of entries when entry was
+ assigned to more than one category. Thanks to Kris/weigon for
+ helping. (garvinhicking)
+
+ * Use htmlspecialchars/strip_tags for escaping user input on comment
+ moderation/viewing. (garvinhicking)
+
+ * Fixed warning message from emoticon plugin when emoticons.inc.php
+ does not exist (garvinhicking)
+
+ * Fixed entries pagination for special cases where quickump calendar
+ was displayed on the left sidebar (garvinhicking)
+
+Version 0.7-beta2 (September 15th, 2004)
+------------------------------------------------------------------------
+
+ * Fixed entries pagination for special cases where quickump calendar
+ was displayed on the left sidebar (garvinhicking)
+
+ * Added Italian translations, thanks to Alessandro Pellizzari
+
+ * Fixed missing CSS-class for BBCode Event-Plugin (Jez Hancock)
+
+ * Fixed possible SQL injections. Thanks to aCiDBiTS!
+
+ * Fixed postgreSQL quicksearch, thanks to Mauri Sahlberg!
+
+ * Added Norwegian translations, thanks to Jo Christian
+
+ * Fixed postgreSQL bugs for viewing comment moderation panel
+ (garvinhicking)
+
+ * Fixed postgresql bug for fetching the last entry id. Will fix bug
+ with comment counter always reporting zero and trouble with
+ assigning multiple categories. (garvinhicking)
+
+ * Fixed "no entry to display" bug when previewing entry
+ (garvinhicking)
+
+ * Fix several postgreSQL update errors. Thanks to daFool from the
+ forums! (garvinhicking)
+
+Version 0.7-beta1 (September 6th, 2004)
+------------------------------------------------------------------------
+
+ * Disable the use of popups by default (tomsommer)
+
+ * BBCode plugin can now pretty print code/php style blocks.
+ (Jez Hancock, garvinhicking)
+
+ * Threaded/Linear view of comments can be toggled by the
+ visitor. (garvinhicking, tomsommer)
+
+ * Plugin serendipity_event_spamblock: Will reject any double
+ comments, if enabled. Stub for enhancing other spam-protections
+ (kaptchas, spamassassin-integration, IP-Blacklists, whatever)
+ (garvinhicking)
+
+ * When using mod_rewrite, make use of RewriteBase for correct
+ subdirectory matching. Thanks to Martin Roell! (garvinhicking)
+
+ * Don't use the title of an entry in the RSS feed <guid></guid> tags
+ (tomsommer)
+
+ * Use '-' as separator for IDs and words in our "nice" URLs, so
+ Google and others can recognize 'a-nifty-word' as 'a nifty word'.
+ Previously this was indexed as one single word.
+ (tomsommer, garvinhicking)
+
+ * Pagination now properly works for browsing months and categories.
+ Previously paging worked through all entries and not a filter
+ by date/category. [Bug #1009715] (garvinhicking)
+
+ * Remote RSS feed can now contain a bullet image, skip blank head-
+ lines and toggle the display of the date. Thanks to Joseph Guhlin!
+ (garvinhicking)
+
+ * Karma-Plugin disallows clicking from Googlebots following java-
+ script links (garvinhicking)
+
+ * Improved link detection routine for sending trackbacks, fixes
+ problems with bbcode-transformed URLs (garvinhicking)
+
+ * Fixed missing image display for rescaling images, when file was
+ outside of parent upload directory [Bug #1007003] (garvinhicking)
+
+ * Fixed some postgreSQL-issues, thanks to Ilya A. Volynets-Evenbakh!
+
+ * Improved and fixed XHTML-Cleanup Event plugin (garvinhicking)
+
+ * Fixed .htaccess update/creation errors for shared installation
+ (garvinhicking)
+
+ * RFE #832040 - Allow for easier handling of Calendar CSS classes
+ and remove hardcoded attributes for "today" (tomsommer)
+
+ * Add detection for support of php_value directives in .htaccess
+ files (tomsommer)
+
+ * Installation will report an error, if the needed db-extension for
+ a specific database-type are not available within PHP
+ (garvinhicking)
+
+ * Added bulgariang language, thanks to Bogomil Shopov
+
+ * Fix calendar bug to not show entries available for the 1st day
+ of a month if on the first day on the next month an entry was
+ posted (garvinhicking)
+
+ * UTF-8 Encoding (for RSS-feeds) will make use of iconv-library, if
+ available (garvinhicking)
+
+ * Quicksearch no longer shows results for entries in the future
+ (garvinhicking)
+
+ * Removed {dbPrefix}css table, since it was not used for anything
+ (tomsommer) [DB Layout change - > 0.6.11]
+
+ * Redesign entry list (tomsommer)
+
+ * Display subtitle (usually blogname) in <title> tags (tomsommer)
+
+ * Don't embed comments, trackbacks and comment-form in entry CSS
+ class (tomsommer)
+
+ * RFE #996320: Added https-option to login sidebar-plugin
+ (garvinhicking)
+
+ * Removed full plaintext URI from trackback section and replaced it
+ with a link to that URI (tomsommer, garvinhicking)
+
+ * Several German spelling mistakes corrected (thanks to Timbalu)
+
+ * Show trackbacks on the full-entry page if popups are disabled
+ (tomsommer)
+
+ * If popups are disabled, link to the full-page entry, with comments
+ and trackbacks as page-anchors (tomsommer)
+
+ * Use DIV tags for trackback layout, just like comments (tomsommer)
+
+ * Make sure that the file/image manager can never remove the upload
+ directory, but all files in it (garvinhicking)
+
+ * Changed database key 'mime' to varchar(255) instead of varchar(15)
+ (garvinhicking) [DB Layout change - > 0.6.9]
+
+ * New CSS classes 'serendipity_msg_notice' and
+ 'serendipity_msg_important'. (garvinhicking)
+
+ * Added a new theme, "Idea" (tadpole9)
+
+ * Changed 'View extended entry' to 'Continue reading "[title]"'
+ (tomsommer)
+
+ * Make the non-WYSIWYG link-insertion and text-formatters work in
+ the extended entry textarea (tomsommer)
+
+ * Allow authors to toggle the extended entry textarea in the entry
+ composer when using the non-WYSIWYG editor (tomsommer)
+
+ * Better handling of unsubscriptions from entries, only show message
+ if the user was indeed unsubscribed (tomsommer)
+
+ * Added ability to moderate comments and trackbacks:
+ * Moderate by email
+ * Moderate and view comments from within Authoring Suite
+ * Mass delete of comments from administration
+ (tomsommer) [DB Layout change - > 0.6.7]
+
+ * An entries' LastModified timestamp will get updated if a comment
+ is made to it, but only if the article is newer than 7 days.
+ Make the conditional Get RSS-feed a lot more usable because old
+ entries no longer come up again. The limit of days can be set
+ within serendipity_config.inc.php. (garvinhicking)
+
+ * Plugins can register the event-hook 'external_plugin' to spawn
+ individual content outside of the blog layout (garvinhicking)
+
+ * Fixed link insertion in non-WYSIWYG-mode for Internet Explorer
+ (garvinhicking)
+
+ * Fixed retrieving multiple stored categories for Internet Explorer
+ when editing existing entry (garvinhicking)
+
+ * Added Bitflux Livesearch Javascript as plugin
+ (serendipity_event_livesearch). It will immediately search your
+ articlebase and offer an autocomplete dropdown to the quicksearch
+ box. Users with the embedded mode need to add the call to the
+ .js file manually into their headers. (chregu, garvinhicking)
+
+ * Added new versions of mt-clean, mt-georgiablue, mt-gettysburg,
+ mt-plainjane, and mt-rusty and activated them.
+
+ * RSS Import: Allow toggling of draft/publish import, category
+ association, more description. (garvinhicking)
+
+ * Allow HTML nugget to be displayable on extended article only, over-
+ view only or both (default) (garvinhicking)
+
+ * New plugin 'serendipity_event_blogpdf'. Will export your blog
+ as PDF file. Proof-of-concept, no nice formatting, no images yet.
+ If you're using 'mod_rewrite' you need to update your .htaccess
+ file. (garvinhicking)
+
+ * Fixed bug causing the .htaccess file to never get updated when
+ changing rewrite method (tomsommer)
+
+ * Track Exits plugin: Now only uses the link id for link referral.
+ Reduces URI length and makes random exit.php calling useless for
+ spammers, as no URL is tracked, which the author didn't refer to
+ (garvinhicking, isotopp)
+
+ * Actually log the IP of users who submit comments and blogs which
+ trackback (tomsommer)
+
+ * Plugin hook 'backend_publish' now executed on entries saved as
+ draft first and after that as a public. Fixes weblogpings not sent
+ to those entries. (garvinhicking)
+
+ * Added the ability to toggle the display of the Serendipity logo and
+ text in the "Powered by" plugin (tomsommer)
+
+ * Fixed problem with booleans not checking the correct radiobuttons in
+ the plugin manager (tomsommer)
+
+ * Fixed bug #983242: Missing entity encoding for blogtitle/subtitle
+ for RSS feeds, thanks to Christian Wenz!
+
+ * Added dutch and Portuguese language. Thanks to Paul de Bruyne and
+ Ranulfo Netto!
+
+ * Multi User: Fixed bug #977695 where simple editors couldn't re-edit
+ their entries (garvinhicking)
+
+ * Auto-Trackback from serendipity will now only fetch links smaller
+ than 150kb. Use socket connections instead of URL wrappers for
+ better control. (garvinhicking)
+
+ * Fixed HTTP requests from installer and htmlvalidator plugin to
+ send "\r\n" headers instead of only "\n". IIS-Servers didn't
+ respond to the latter (garvinhicking)
+
+ * Updated plugins to show default values in plugin configuration
+ immediately. New bag property 'default' introduced to Plugin API,
+ backwards compatible. (garvinhicking)
+
+ * Remaining XHTML 1.1 issues fixed:
+ - Use <div> instead of <span> for entry's contents.
+ - Removed obsolete javascript-function for comments
+ - Removed 'align' attribute from <div> tags, replaced with new
+ CSS class 'serendipity_align'
+ - Show the pingback-link element in the HEAD area. Embedded blogs
+ need to do that on their own.
+ - New plugin 'serendipity_event_xhtmlcleanup' to correct most
+ common XHTML errors (unclosed single tags, missing 'alt'
+ attribute, unescaped ampersands)
+ (garvinhicking)
+
+ * SQLite support now works. (garvinhicking)
+
+ * Fixed Bug #963248 - Calendar cannot calculate LastRow CSS class if
+ there are more than 4 rows (tomsommer)
+
+ * Use the right HTML code for trackback-link detection when using
+ markup plugins like Text_Wiki (garvinhicking)
+
+ * Display information when sending/searching for trackbacks
+ (garvinhicking)
+
+ * Add support for MySQLi (tadpole9)
+
+ * Alphabetically sort list of plugins (tadpole9)
+
+ * Tweaks to plugin configuration design (tomsommer)
+
+ * Added small comments to serendipity_functions.inc.php on how
+ to enable the spellchecker module of the WYSIWYG editor.
+ (Suggestion by Randall)
+
+ * Plugin API: Now each sidebar plugin item will get a CSS class
+ name added to the default "serendipitySideBarContent" one, which
+ is called 'sidebar_wrap_', 'sidebar_title_', 'sidebar_content_'
+ and then the original class name of the plugin appended. I.e.:
+ 'sidebar_wrap_serendipity_html_nugget_plugin'. This allows special
+ customization of any individual sidebar item. (garvinhicking)
+
+ * Finally a OPML-based blogroll importing feature added to
+ the serendipity_plugin_remoterss plugin (Richard Harrison,
+ garvinhicking)
+
+ * Simple OPML 1.0 outline feed (Richard Harrison, garvinhicking)
+
+ * Fixed variable namespace problems in Textile Event plugin
+ (Reimer Behrends, garvinhicking)
+
+ * Implemented "visitor/hits" counter for karma-plugin. Hook into
+ Statistics plugin for showing additional data of karmavotes
+ (garvinhicking)
+
+ * Fixed receiving trackbacks of an intermediate s9y CVS release,
+ which submitted wrong variables. Allow logging of incoming
+ trackbacks to a file (for developers) (garvinhicking)
+
+ * Post to multiple categories and subcategories implemented.
+ (Evan Nemerson, garvinhicking)
+
+ * Threaded comments.
+ (Evan Nemerson) [DB Layout change - > 0.6.5]
+
+ * Minor https fixes, when https is used as links from commenting
+ users or in referring/exit links. Fix for RPC-ping function submitting
+ double http://http:// URLs. (garvinhicking)
+
+ * New sidebar plugin "eventlinks". Displays all links to the
+ currently viewed article (only on full article view).
+ (garvinhicking)
+
+ * Plugin API: The method 'generate_content' of a sidebar plugin
+ can now return false to instruct the plugin API to NOT display
+ the sidebar. Allows optional sidebar plugins to be only shown
+ on specific parameters (garvinhicking)
+
+ * Updated upgrade.sh script for better checks (Jez Hancock)
+
+ * Fixed javascript error when submitting comments with "remember me"
+ checkbox. (garvinhicking)
+
+ * XML-RPC methods updated to includer blogger.getRecentPosts and
+ blogger.getPost (Till Adam)
+
+ * Upgraded karma-plugin to allow logging (IP/User-Agent) of votes
+ (garvinhicking)
+
+ * User manager: New user right 'publish' which controls whether a
+ user is allowed to publish entries or only drafts.
+ (garvinhicking) [DB Layout change -> 0.6.4]
+
+ * Added new plugin "serendipity_event_karma": Allows karma voting
+ for each article with a flexible voting period. (garvinhicking)
+
+ * Added event hook-variable for displaying an article footer.
+ Enhanced Plugin API to allow passing a second array of data to the
+ hook_event() function (backwards-compatible). Added hook for
+ embedding CSS data from within a event plugin. (garvinhicking)
+
+ * When sending trackbacks, the excerpt will now be stripped of HTML
+ code BEFORE selecting the 255 characters to be sent.
+ (garvinhicking)
+
+ * Upgraded image manager to now also accept files (pdf, doc, ...)
+ and manage/upload to/sync/browse subdirectories. Improved SQL-
+ query for fetching list of images.
+ (garvinhicking) [DB Layout change -> 0.6.3]
+
+ * PHP pre-4.3.0 compatibility: Define PATH_SEPARATOR if not available
+ (garvinhicking)
+
+ * Upgraded PEAR::Text_Wiki and serendipity_event_textwiki plugin.
+ Configuration options for wiki rules, options to use wiki/freelinks.
+ (Tobias Schlitt)
+
+ * Made RSS-feed compatible to sites running with UTF8-charset.
+ (garvinhicking)
+
+ * Fixed wrong proportions when resizing small images (tomsommer)
+
+ * Fixed bug #940239 - Wrong message printed when deleting a category
+ without any entries (tomsommer)
+
+ * Bugfix for serendipity_plugin_remoterss: Wrong caching.
+ (Richard Thomas Harrison)
+
+ * Added new CSS class .serendipity_comments for the comments block
+ on full article view (garvinhicking)
+
+ * Added new CSS class .serendipity_admin_filters for the redesigned
+ editing entries interface (tomsommer, garvinhicking)
+
+ * Plugin API: Introduced function 'is_event_plugin' for easier
+ integration (Jonathan Arkell)
+
+ * New event hook 'entry_display' inside of serendipity_printEntries().
+ Can be used to force entries not being shown by setting the
+ $eventData['clean_page'] variable to 'true'. (Jonathan Arkell)
+
+ * Don't show "expand"/"toggle all" buttons if there is only one section
+ availiable (tomsommer)
+
+ * Redesigned admin interface for editing entries: Adjust items per
+ page, sort order, filter mode, combined EDIT+DELETE interface into
+ one. Introduced new css classes "serendipity_admin_list_item_even"
+ and "serendipity_admin_list_item_uneven" for displaying entries.
+ Can now search for entries in admin panel, can edit entries with
+ empty titles. (garvinhicking)
+
+ * Fixed image comment manager's "center" alignment mode and use
+ image width/height from the image manager to format the <img>-Tag,
+ with regards to Lewe.
+ (garvinhicking)
+
+ * Plugin 'serendipity_event_weblogping' now takes manually defined
+ pinging service as parameter. (garvinhicking)
+
+ * Modified plugin 'serendipity_event_trackexits': New config
+ directive to deflect any links from commenting users using a
+ HTTP redirect. This destroys common search engine ranking and
+ will not benefit comment spammers. Notice that it also disables
+ the common linking to non-spammer authors, so use with caution.
+ (garvinhicking)
+
+ * New plugin 'serendipity_plugin_remoterss' - Can display a foreign
+ remote feed. Results are cached for a custom period of time.
+ Contributed by Udo Gerhards. (garvinhicking)
+
+ * Fixed Bug #941922 - strftime %e does not work on windows platform
+ (tomsommer)
+
+ * Fix template chooser plugin to reflect new location of template
+ variable (tadpole9)
+
+ * Image upload now strips all characters not suggested for filename
+ use (garvinhicking)
+
+ * Categories can now have a fixed image which is embedded for
+ every article. Can be styled via CSS (.serendipity_entryIcon).
+ Integrated with s9y image manager.
+ (Evan Nemerson, garvinhicking) [DB Layout change -> 0.6.2]
+
+ * Published entries from the future will only be displayed on the
+ frontend when not in the future. They will get automagically
+ displayed as soon as the publish time is effectively reached.
+ #RFE 939867 (garvinhicking)
+
+ * Exit/Referrer plugin can now limit the number of elements
+ (garvinhicking)
+
+ * Added possibility to embed a comment to an image when inserting
+ it via the s9y image manager (garvinhicking)
+
+ * Added czech language (ISO-8859-2 and Win-1250), translated by
+ Josef Klimosz.
+
+ * Added french language, translated by Renaud Lavigne.
+
+ * Fixed some issues on windows servers. Patch by Richard Thomas
+ Harrison
+
+ * Improvements for multi-user interface:
+ - Configuration is stored per-user (language, WYSIWYG-preference,
+ and possibly others)
+ - passwords/usernames/E-Mail adresses can be changed
+ - introduction of userlevels (admin, chief, editor)
+ - public/private categories
+ - plugins are stored per-user. Admin-created plugins cannot be
+ removed by chiefs/users, protected plugins (HTML nuggets) can
+ only be configured by the owner or admin users
+ - manage/create users
+ - entries are stored per-user and can only be changed by the
+ author or chief/admin users. Comments can only be toggled on/
+ off and removed by the author or chief/admin users.
+ - public/private images
+ (garvinhicking) [DB Layout change -> 0.6.1]
+
+ * Configuration is now language-dependant. (garvinhicking)
+
+Version 0.6-pl3 (June 20th, 2004)
+------------------------------------------------------------------------
+
+ * WYSIWYG-Editor: Links were prefixed with '/' wrongly (IE only).
+ Now all entered links will be put to an absolute URL consistently.
+ Fix always inserting extended body with empty '<br />'
+ (garvinhicking)
+
+ * Fix bug during installation, which can cause an endless loop
+ of connections (tomsommer)
+
+ * Removed german blogbot pinging service, it was shut down (nohn)
+
+ * Allow pinging of blogbot.dk (tomsommer)
+
+Version 0.6-pl2 (May 24th, 2004)
+------------------------------------------------------------------------
+
+ * Fixed security vulnerability on servers with Register_Globals On.
+ (garvinhicking, gschlossnagle, tomsommer)
+
+Version 0.6-pl1 (May 15th, 2004)
+------------------------------------------------------------------------
+
+ * Fixed trackbacks not associated to the right entry id (garvinhicking)
+
+ * Fixed typo in Creative Common Plugin (Jonathan Arkell)
+
+Version 0.6 (May 12th, 2004)
+------------------------------------------------------------------------
+
+ [changes since 0.6-rc2]
+ * Creative Commons plugin bugfix (wrong 'non-commercial' case)
+ (garvinhicking)
+
+ * Fixed wrong doctype header (garvinhicking)
+
+ * Fix %e date issue on windows systems. (garvinhicking, tomsommer)
+
+ * Fix serendipity_makefilename() to replace some more foreign
+ characters (garvinhicking)
+
+ * Shoutbox plugin bugfix (errors using '%' in shouts) (garvinhicking)
+
+ * Error with non-absolute image paths in RSS feed (garvinhicking)
+
+ * Fixed installer problems timeouting (garvinhicking)
+
+ * Removed possible error message about session_start for categories
+ admin page (garvinhicking)
+
+ * Fixed postgresql limit error when browsing pages (garvinhicking)
+
+ [changes before 0.6-rc2]
+ * Image upload now strips all characters not suggested for filename
+ use (garvinhicking)
+
+ * Language Charset Header are now used throughout s9y which make it
+ completely foreign-language compatible (garvinhicking)
+
+ * German translation fix (garvinhicking)
+
+ * Upgrader kills the current session for possible changes inside
+ session data on upgrade (garvinhicking)
+
+ * Fixed wrong comparison operator - bug reported by tom, where nl2br
+ plugin has "reversed" logic. (garvinhicking)
+
+ * Fixed textile plugin not enabled by default after adding it as a
+ plugin (garvinhicking)
+
+ * Comments plugin now correctly wordwraps (garvinhicking)
+
+ * Fixed wrong HTML links for WYSIWYG-editor in Internet Explorer
+ (garvinhicking)
+
+ * Fixed htmlentites acceptance for foreign language entry titles
+ (garvinhicking)
+
+ * Relaxed dependency on WIKI plugin, if the inclusion fails.
+ (garvinhicking)
+
+ * PostgreSQL SQL update files (jtate)
+
+ * Better detection of serendipityPath variable (garvinhicking)
+
+ * Adjustable WYSIWYG-language for foreign languages (garvinhicking)
+
+ * Fixed special characters for WYSIWYG-htmlarea.css file
+ (garvinhicking)
+
+ * Fixed WYSIWYG-editing error for HTML Nugget plugin (garvinhicking)
+
+ * newline fixes inside the files (cosmetic issues) (isotopp)
+
+ * Fixed losing commenting user details when previewing a comment
+ (garvinhicking)
+
+ * Added plugin for usage of CreativeCommons (creativecommons.org)
+ license for the blog's contents (Evan Nemerson)
+
+ * Templates with an "inactive.txt" or no "info.txt" inside their
+ directory are not selectable for s9y. Usable for "work in progress"
+ templates. (garvinhicking)
+
+ * Fixed $su link for users with no URL rewriting (garvinhicking)
+
+ * Fixed invalid XML for RDF trackback:ping (garvinhicking)
+
+ * Added plugin 'serendipity_event_statistics'. Hooks into the
+ admin entry-panel. (garvinhicking)
+
+ * Do some "common XHTML-mistakes" fixing for output of RSS feeds
+ (jalcorn)
+
+ * Updated WYSIWYG-Editor (htmlarea) to latest version. Integrated
+ s9y image manager in htmlarea window. Template file 'htmlarea.css'
+ can be used to adjust the editor's look to your template.
+ (garvinhicking)
+
+ * Templates: New CSS classes for better customization
+ - 'serendipity_entry_body_folded' for the entry body on the
+ weblog index page
+ - 'serendipity_entry_body_unfolded' for the entry body on
+ article page
+ - 'serendipity_entry_extended' for the extended body on article
+ page
+ (garvinhicking)
+
+ * Image upload: Can now specify alternate file name. Renamed
+ input field to not cause confusion with COOKIE-variable
+ (garvinhicking, isotopp)
+
+ * Image manager popup-window now resizable and with scrollbars
+ (garvinhicking)
+
+ * Serendipity can now be used in shared environments and act as a
+ library for VirtualHost'ed Blogs. See README. (garvinhicking)
+
+ * Fixed installer on hosts with non-standard (80) HTTP ports
+ (garvinhicking)
+
+ * Relaxed umask/chmod file and directory creation
+ (garvinhicking)
+
+ * Abstract archives/URL locations and regex-patterns in a central
+ place for easier maintenance (zem)
+
+ * Optimized db indizes on 'entries' (zem)
+
+ * Added shoutbox plugin (Matthias Lange, garvinhicking)
+
+ * Renamed "0.5.1" to "0.6" because of changes. Updated upgrade-
+ script (garvinhicking)
+
+ * Added home-link in templates for the header/subheader
+ (garvinhicking)
+
+ * Bugfix: With mod_rewrite redirection parameters to browse pages/
+ calendar was not working in non-embedded blogs
+ (garvinhicking)
+
+ * Added many new templates (several MoveableType imitations,
+ moz-modern) (sebastianbergmann, tomsommer, garvinhicking)
+
+ * Added backwards-compatible CSS ids to admin panel for better CSS
+ customization (garvinhicking)
+
+ * Moved smilies/xml buttons to template directory (img/) to be
+ customized per-template (garvinhicking)
+
+ * Added plugin to switch themes on the frontend (Evan Nemerson,
+ garvinhicking)
+
+ * Allow (multiple) dependencies for plugin API to allow pairing of
+ event/ sidebar plugins (garvinhicking)
+
+ * Added an upgrade-script to allow for easier upgrade of an existing
+ s9y installation (tomsommer)
+
+ * Redesigned the plugin manager (tomsommer)
+
+ * Added RFE #827945 - Allow for custom selection of calendar
+ beginning on week (tomsommer)
+
+ * Markup can be applied individually from a list of available
+ transformations: BBCode, Wiki, Textile, s9y markup, Emoticons,
+ nl2br. Multiple transformations are possible. (Colin Viebrock,
+ garvinhicking)
+
+ * Allow for each language to have its own charset (tomsommer)
+
+ * Now able to preview comments. (garvinhicking)
+
+ * Fixed bug that removed admin-cookie when you didn't check the
+ "remember comment" box on submitting comments to your own blog
+ (garvinhicking)
+
+ * Conditional GET logic for RSS feeds using HTTP caching methods.
+ See README for instructions, needs database schema update
+ [db_update-0.5-0.5.1.sql] (garvinhicking)
+
+ * Small XHTML-compliance fixes. (garvinhicking)
+
+ * Image manager: Allow sorting by date/file attributes, changing
+ sort order and choosing items displayed per page (garvinhicking)
+
+ * Fixed wrong link to entry when not using any rewrite rule
+ (tomsommer & AlfaTeK)
+
+ * Changed type of DB password to 'protected' to avoid it being
+ displayed as plaintext (tomsommer)
+
+ * Added danish language file (tomsommer & Jeppe Lund)
+
+ * Added notice when using a wrong username or password to gain
+ access to admin/author suites (tomsommer)
+
+ * Added date & time translation using strftime() and setlocale()
+ (tomsommer)
+
+ * HTML Validator event plugin. Can be used to validate your entry
+ upon preview (garvinhicking)
+
+ * Create example events: mailer, weblogping, contentrewrite,
+ eventwrapper (garvinhicking)
+
+ * Created event plugin API to hook on certain serendipity actions
+ (garvinhicking)
+
+ * Redesigned image manager. (tomsommer)
+
+ * Added image syncronization with database. IMPORTANT: Click on
+ "rebuild thumbs" to re-import your file-based images! (tomsommer)
+
+ * Now able to use CommentAPI to post entries to an RSS/Atom feed,
+ like from RSS Bandit (garvinhicking)
+
+ * Renamed "sebastian's weblog" theme to "blue" (sebastianbergmann)
+
+(Older NEWS see file NEWS_OLD)
--- /dev/null
+<?php # $Id$
+# Copyright (c) 2003-2005, Jannis Hermanns (on behalf the Serendipity Developer Team)
+# All rights reserved. See LICENSE file for licensing details
+
+/**
+ * Delete a category or range of categories
+ *
+ * @access public
+ * @param string Holds the SQL string to pass to the 'BETWEEN' command. Like '1 5' would delete categories 1-5.
+ * @param string Holds the optional SQL string that contains an extra safety check so that only categories can be deleted if the user is an author of the category.
+ * @return array The DB result
+ */
+function serendipity_deleteCategory($category_range, $admin_category) {
+ global $serendipity;
+
+ if (!serendipity_checkPermission('adminCategoriesDelete')) {
+ return false;
+ }
+
+ serendipity_plugin_api::hook_event('backend_category_delete', $category_range);
+
+ return serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}category WHERE category_left BETWEEN {$category_range} {$admin_category}");
+}
+
+/**
+ * Fetch a SQL ID subset of the category tree
+ *
+ * @access public
+ * @param int The Id of the parent category to fetch categorie childs from. (0: all)
+ * @return array An associative array of the left and right category next to the chosen one
+ */
+function serendipity_fetchCategoryRange($categoryid) {
+ global $serendipity;
+
+ $res = serendipity_db_query("SELECT category_left, category_right FROM {$serendipity['dbPrefix']}category WHERE categoryid='". (int)$categoryid ."'");
+ if (!is_array($res) || !isset($res[0]['category_left']) || !isset($res[0]['category_right'])) {
+ $res = array(array('category_left' => 0, 'category_right' => 0));
+ }
+
+ return array('category_left' => $res[0]['category_left'], 'category_right' => $res[0]['category_right']);
+}
+
+/**
+ * Returns SQL code to use when fetching entries that are contained within multiple categories
+ *
+ * @access public
+ * @param string A listing of category ids to check, separated by ";"
+ * @param boolean Toggle whether to include or exclude entries of this category
+ * @return string Returns the SQL code for selecting entries of the calculated categories
+ */
+function serendipity_getMultiCategoriesSQL($cats, $invert = false) {
+ global $serendipity;
+
+ $mcategories = explode(';', $cats);
+ $cat_sql_array = array();
+ foreach($mcategories AS $categoryid) {
+ $categoryid = (int)$categoryid;
+
+ if ($categoryid != 0) {
+ $cat_sql_array[] = " (c.category_left " . ($invert ? " NOT " : "") . " BETWEEN " . implode(' AND ', serendipity_fetchCategoryRange($categoryid)) . ')';
+ }
+ }
+
+ return '(' . implode(($invert ? ' AND ' : ' OR '), $cat_sql_array) . ')';
+}
+
+/**
+ * Return the category properties of a specific category
+ *
+ * Either use the first or the second parameter to select a category by ID or name. It's not
+ * meant to be used with both parameters specified.
+ *
+ * @access public
+ * @param int The ID of the category to fetch
+ * @param string The Name of a category to fetch
+ * @return array Returns an array of category properties
+ */
+function serendipity_fetchCategoryInfo($categoryid, $categoryname = '') {
+ global $serendipity;
+
+ if (!empty($categoryname)) {
+ $query = "SELECT
+ c.authorid,
+ c.categoryid,
+ c.category_name,
+ c.category_description,
+ c.category_icon,
+ c.parentid
+ FROM {$serendipity['dbPrefix']}category AS c
+ WHERE category_name = '" . serendipity_db_escape_string($categoryname) . "'";
+
+ $ret = serendipity_db_query($query);
+ return $ret[0];
+ } else {
+ $query = "SELECT
+ c.authorid,
+ c.categoryid,
+ c.category_name,
+ c.category_description,
+ c.category_icon,
+ c.parentid
+ FROM {$serendipity['dbPrefix']}category AS c
+ WHERE categoryid = " . (int)$categoryid;
+
+ $ret = serendipity_db_query($query);
+ return $ret[0];
+ }
+}
+
+/**
+ * Fetch a list of all category properties to a specific entry ID
+ *
+ * @access public
+ * @param int The ID of the entry
+ * @return array The array of associated categories to that entry
+ */
+function serendipity_fetchEntryCategories($entryid) {
+ global $serendipity;
+
+ if (is_numeric($entryid)) {
+ $query = "SELECT
+ c.categoryid,
+ c.category_name,
+ c.category_description,
+ c.category_icon,
+ c.parentid
+ FROM {$serendipity['dbPrefix']}category AS c
+ INNER JOIN {$serendipity['dbPrefix']}entrycat AS ec
+ ON ec.categoryid = c.categoryid
+ WHERE ec.entryid = {$entryid}";
+
+ $cat = serendipity_db_query($query);
+ if (!is_array($cat)) {
+ return array();
+ } else {
+ return $cat;
+ }
+ }
+}
+
+
+/**
+ * Fetch a list of entries
+ *
+ * The most central and versatile function of Serendipity, allows you to fetch entries
+ * depending on a LOT of options.
+ * One of the parameters missing is a restriction by category. You need to do that by
+ * setting the superglobal $serendipity['GET']['category'] to the category you want to fetch.
+ * Separate multiple categories by ";".
+ * Other "external" variables that affect this function are:
+ * $serendipity['short_archives'] - Indicates if the short archive listing is wanted, without the full entry text
+ * $serendipity['range'] - If $range is not used, the time restriction is fetched from this array, which holds a start timestamp and end timestamp.
+ * $serendipity['GET']['category'] - The category ID to restrict fetching entries from (can be seperated by ";")
+ * $serendipity['GET']['hide_category']- The category ID to NOT fetch entries from (can be seperated by ";")
+ * $serendipity['GET']['viewAuthor'] - Only fetch entries by this author
+ * $serendipity['GET']['page'] - The page number to show entries, for pagination
+ *
+ * If you want to use any of these options, set the variable before calling serendipity_fetchEntries(). You can reset the variables to their original content after the function call, if you need to.
+ *
+ * Several options perform different commands when different types are passed, like the $range
+ * parameter which can either be a string or an array with START/END range.
+ *
+ * @access public
+ * @param mixed Restricts fetching entries to a specific timespan. Behaves differently depending on the type:
+ * Numeric:
+ * YYYYMMDD - Shows all entries from YYYY-MM-DD.
+ * If DD is "00", it will show all entries from that month.
+ * If DD is any other number, it will show entries of that specific day.
+ * 2-Dimensional Array:
+ * Key #0 - Specifies the start timestamp (unix seconds)
+ * Key #1 - Specifies the end timestamp (unix seconds)
+ * Other (null, 3-dimensional Array, ...):
+ * Entries newer than $modified_since will be fetched
+ * @param boolean Indicates if the full entry will be fetched (body+extended: TRUE), or only the body (FALSE).
+ * @param string Holds a "Y" or "X, Y" string that tells which entries to fetch. X is the first entry offset, Y is number of entries. If not set, the global fetchLimit will be applied (15 entries by default)
+ * @param boolean Indicates whether drafts should be fetched (TRUE) or not
+ * @param int Holds a unix timestamp to be used in conjunction with $range, to fetch all entries newer than this timestamp
+ * @param string Holds the SQL "ORDER BY" statement.
+ * @param string Can contain any SQL code to inject into the central SQL statement for fetching the entry
+ * @param boolean If set to TRUE, all entries will be fetched from scratch and any caching is ignored
+ * @param boolean If set to TRUE, all sticky entries will NOT be fetched.
+ * @param string Can contain a SQL statement on which keys to select. Plugins can also set this, pay attention!
+ * @param string Can contain a SQL statement on how to group the query. Plugins can also set this, pay attention!
+ * @param string If set to "array", the array of entries will be returned. "flat-array" will only return the articles without their entryproperties. "single" will only return a 1-dimensional array. "query" will only return the used SQL.
+ * @return array Holds the super-array of all entries with all additional information
+ */
+function serendipity_fetchEntries($range = null, $full = true, $limit = '', $fetchDrafts = false, $modified_since = false, $orderby = 'timestamp DESC', $filter_sql = '', $noCache = false, $noSticky = false, $select_key = null, $group_by = null, $returncode = 'array') {
+ global $serendipity;
+
+ $cond = array();
+ $cond['orderby'] = $orderby;
+ if (isset($serendipity['short_archives']) && $serendipity['short_archives']) {
+ // In the short listing of all titles for a month, we don't want to have a limit applied. And we don't need/want toe
+ // full article body (consumes memory)
+ $limit = '';
+ $full = false;
+ }
+
+ if ($full === true) {
+ $noCache = true; // So no entryproperties related to body/extended caching will be loaded
+ $body = ', e.body, e.extended';
+ } else {
+ $body = '';
+ }
+
+ if ($fetchDrafts === false) {
+ $drafts = "isdraft = 'false'";
+ }
+
+ if ($limit != '') {
+ $serendipity['fetchLimit'] = $limit;
+ }
+
+ /* Attempt to grab range from $serendipity, if $range is not an array or null */
+ if (!is_array($range) && !is_null($range) && isset($serendipity['range'])) {
+ $range = $serendipity['range'];
+ }
+
+ if (is_numeric($range)) {
+ $year = (int)substr($range, 0, 4);
+ $month = (int)substr($range, 4, 2);
+ $day = (int)substr($range, 6, 2);
+
+ $startts = serendipity_serverOffsetHour(mktime(0, 0, 0, $month, ($day == 0 ? 1 : $day), $year), true);
+
+ if ($day == 0) {
+ $month++;
+ } else {
+ $day++;
+ }
+
+ $endts = serendipity_serverOffsetHour(mktime(0, 0, 0, $month, ($day == 0 ? 1 : $day), $year), true);
+
+ $cond['and'] = " WHERE e.timestamp >= $startts AND e.timestamp <= $endts";
+ } elseif (is_array($range) && count($range)==2) {
+ $startts = serendipity_serverOffsetHour((int)$range[0], true);
+ $endts = serendipity_serverOffsetHour((int)$range[1], true);
+ $cond['and'] = " WHERE e.timestamp >= $startts AND e.timestamp <= $endts";
+ } else {
+ if ($modified_since) {
+ $unix_modified = strtotime($modified_since);
+ if ($unix_modified != -1) {
+ $cond['and'] = ' WHERE last_modified >= ' . (int)$unix_modified;
+ if (!empty($limit)) {
+ $limit = ($limit > $serendipity['max_fetch_limit'] ? $limit : $serendipity['max_fetch_limit']);
+ }
+ $cond['orderby'] = 'last_modified DESC';
+ }
+ }
+ }
+
+ if (!empty($drafts)) {
+ if (!empty($cond['and'])) {
+ $cond['and'] .= " AND $drafts";
+ } else {
+ $cond['and'] = "WHERE $drafts";
+ }
+ }
+
+ if (isset($serendipity['GET']['viewAuthor'])) {
+ $multiauthors = explode(';', $serendipity['GET']['viewAuthor']);
+ $multiauthors_sql = array();
+ foreach($multiauthors AS $multiauthor) {
+ $multiauthors_sql[] = 'e.authorid = ' . (int)$multiauthor;
+ }
+
+ $cond['and'] .= ' AND (' . implode(' OR ', $multiauthors_sql) . ')';
+ }
+
+ $cat_sql = '';
+ if (isset($serendipity['GET']['category'])) {
+ $cat_sql = serendipity_getMultiCategoriesSQL($serendipity['GET']['category']);
+ } elseif (isset($serendipity['GET']['hide_category'])) {
+ $cat_sql = serendipity_getMultiCategoriesSQL($serendipity['GET']['hide_category'], true);
+ }
+
+ if (!empty($cat_sql)) {
+ if (!empty($cond['and'])) {
+ $cond['and'] .= " AND ($cat_sql)";
+ } else {
+ $cond['and'] = "WHERE ($cat_sql)";
+ }
+ }
+
+ if (!empty($limit)) {
+ if (isset($serendipity['GET']['page']) && $serendipity['GET']['page'] > 1 && !strstr($limit, ',')) {
+ $limit = serendipity_db_limit(($serendipity['GET']['page']-1) * $limit, $limit);
+ }
+
+ $limit = serendipity_db_limit_sql($limit);
+ }
+
+ if (isset($serendipity['GET']['adminModule']) && $serendipity['GET']['adminModule'] == 'entries' && !serendipity_checkPermission('adminEntriesMaintainOthers')) {
+ if (!empty($cond['and'])) {
+ $cond['and'] .= " AND e.authorid = '" . $serendipity['authorid'] . "'";
+ } else {
+ $cond['and'] = "WHERE e.authorid = '" . $serendipity['authorid'] . "'";
+ }
+ }
+
+ if (!isset($serendipity['GET']['adminModule']) && !serendipity_db_bool($serendipity['showFutureEntries'])) {
+ if (!empty($cond['and'])) {
+ $cond['and'] .= " AND e.timestamp <= " . time();
+ } else {
+ $cond['and'] = "WHERE e.timestamp <= " . time();
+ }
+ }
+
+ if (!empty($filter_sql)) {
+ if (!empty($cond['and'])) {
+ $cond['and'] .= ' AND ' . $filter_sql;
+ } else {
+ $cond['and'] = 'WHERE ' . $filter_sql;
+ }
+ }
+
+ serendipity_plugin_api::hook_event('frontend_fetchentries', $cond, array('noCache' => $noCache, 'noSticky' => $noSticky, 'source' => 'entries'));
+
+ if ($serendipity['dbType'] == 'postgres') {
+ $group = '';
+ $distinct = 'DISTINCT';
+ } else {
+ $group = 'GROUP BY e.id';
+ $distinct = '';
+ }
+
+ if (!is_null($group_by)) {
+ $group = $group_by;
+ }
+
+ if (is_null($select_key)) {
+ $select_key = "$distinct
+ {$cond['addkey']}
+
+ e.id,
+ e.title,
+ e.timestamp,
+ e.comments,
+ e.exflag,
+ e.authorid,
+ e.trackbacks,
+ e.isdraft,
+ e.allow_comments,
+ e.last_modified,
+
+ a.realname AS author,
+ a.email";
+ }
+
+ serendipity_ACL_SQL($cond);
+
+ // Store the unique query condition for entries for later reference, like getting the total article count.
+ $serendipity['fullCountQuery'] = "
+ FROM
+ {$serendipity['dbPrefix']}entries AS e
+ LEFT JOIN {$serendipity['dbPrefix']}authors a
+ ON e.authorid = a.authorid
+ LEFT JOIN {$serendipity['dbPrefix']}entrycat ec
+ ON e.id = ec.entryid
+ LEFT JOIN {$serendipity['dbPrefix']}category c
+ ON ec.categoryid = c.categoryid
+ {$cond['joins']}
+ {$cond['and']}";
+
+ $query = "SELECT $select_key
+ $body
+ {$serendipity['fullCountQuery']}
+ $group
+ ORDER BY {$cond['orderby']}
+ $limit";
+
+ // DEBUG:
+ // die($query);
+ $fetch_single = ($returncode == 'single' ? true: false);
+
+ if ($returncode == 'query') {
+ return $query;
+ }
+
+ $ret = serendipity_db_query($query, $fetch_single, 'assoc');
+
+ if (is_string($ret)) {
+ die("Query failed: $ret");
+ }
+
+ if (is_array($ret) && $returncode == 'array') {
+ // The article's query LIMIT operates on a flattened entries layer so that
+ // an article having 5 associated categories won't count as 5 entries.
+ // But to store the expanded list of categories, we need to send a new
+ // query once for all entries we have just fetched.
+ // First code for this was sending 15 queries for 15 fetched entries,
+ // this is now limited to just one query per fetched articles group
+
+ serendipity_fetchEntryData($ret);
+ }
+
+ return $ret;
+}
+
+/**
+ * Fetch special entry data and attach it to a superarray of entries.
+ *
+ * Fetches all additional information like plugins, extended properties, additional categories for each entry.
+ *
+ * @access private
+ * @see serendipity_fetchEntries()
+ * @param array The array of entries where the output will be merged to (referenced)
+ * @return null
+ */
+function serendipity_fetchEntryData(&$ret) {
+ global $serendipity;
+
+ $search_ids = array(); // An array to hold all ids of the entry we want to fetch.
+ $assoc_ids = array(); // A temporary key association container to not have to loop through the return array once again.
+
+ foreach($ret AS $i => $entry) {
+ $search_ids[] = $entry['id'];
+ $ret[$i]['categories'] = array(); // make sure every article gets its category association
+ $assoc_ids[$entry['id']] = $i; // store temporary reference
+ }
+
+ serendipity_plugin_api::hook_event('frontend_entryproperties', $ret, $assoc_ids);
+
+ $query = "SELECT
+ ec.entryid,
+ c.categoryid,
+ c.category_name,
+ c.category_description,
+ c.category_icon,
+ c.parentid
+ FROM {$serendipity['dbPrefix']}category AS c
+ LEFT JOIN {$serendipity['dbPrefix']}entrycat AS ec
+ ON ec.categoryid = c.categoryid
+ WHERE " . serendipity_db_in_sql('ec.entryid', $search_ids) . "
+ ORDER BY c.category_name ASC";
+
+ $search_ret = serendipity_db_query($query, false, 'assoc');
+
+ if (is_array($search_ret)) {
+ foreach($search_ret AS $i => $entry) {
+ $ret[$assoc_ids[$entry['entryid']]]['categories'][] = $entry;
+ }
+ }
+}
+
+/**
+ * Fetch a single entry by a specific condition
+ *
+ * @access public
+ * @param string The column to compare $val against (like 'id')
+ * @param string The value of the colum $key to compare with (like '4711')
+ * @param boolean Indicates if the full entry will be fetched (body+extended: TRUE), or only the body (FALSE).
+ * @param string Indicates whether drafts should be fetched
+ * @return
+ */
+function serendipity_fetchEntry($key, $val, $full = true, $fetchDrafts = 'false') {
+ global $serendipity;
+
+ $cond = array();
+ $cond['and'] = " "; // intentional dummy string to attach dummy AND parts to the WHERE clauses
+
+ if ($fetchDrafts == 'false') {
+ $cond['and'] = " AND e.isdraft = 'false' " . (!serendipity_db_bool($serendipity['showFutureEntries']) ? " AND e.timestamp <= " . time() : '');
+ }
+
+ if (isset($serendipity['GET']['adminModule']) && $serendipity['GET']['adminModule'] == 'entries' && !serendipity_checkPermission('adminEntriesMaintainOthers')) {
+ $cond['and'] = " AND e.authorid = '" . $serendipity['authorid'] . "'";
+ }
+
+ serendipity_ACL_SQL($cond, true);
+
+ serendipity_plugin_api::hook_event('frontend_fetchentry', $cond, array('noSticky' => true));
+
+ $querystring = "SELECT e.id,
+ e.title,
+ e.timestamp,
+ e.body,
+ e.comments,
+ e.trackbacks,
+ e.extended,
+ e.exflag,
+ e.authorid,
+ e.isdraft,
+ e.allow_comments,
+ e.last_modified,
+ e.moderate_comments,
+
+ a.realname AS author,
+ a.email
+ FROM
+ {$serendipity['dbPrefix']}entries e
+ LEFT JOIN {$serendipity['dbPrefix']}authors a
+ ON e.authorid = a.authorid
+ {$cond['joins']}
+ WHERE
+ e.$key LIKE '" . serendipity_db_escape_string($val) . "'
+ {$cond['and']}
+ LIMIT 1";
+
+ $ret = serendipity_db_query($querystring, true, 'assoc');
+
+ if (is_array($ret)) {
+ $ret['categories'] = serendipity_fetchEntryCategories($ret['id']);
+ $ret['properties'] = serendipity_fetchEntryProperties($ret['id']);
+ }
+
+ return $ret;
+}
+
+/**
+ * Fetches additional entry properties for a specific entry ID
+ *
+ * @access public
+ * @param int The ID of the entry to fetch additonal data for
+ * @return array The array of given properties to an entry
+ */
+function serendipity_fetchEntryProperties($id) {
+ global $serendipity;
+
+ $parts = array();
+ serendipity_plugin_api::hook_event('frontend_entryproperties_query', $parts);
+
+ $properties = serendipity_db_query("SELECT property, value FROM {$serendipity['dbPrefix']}entryproperties WHERE entryid = " . (int)$id . " " . $parts['and']);
+ if (!is_array($properties)) {
+ $properties = array();
+ }
+
+ $property = array();
+ foreach($properties AS $idx => $row) {
+ $property[$row['property']] = $row['value'];
+ }
+
+ return $property;
+}
+
+/**
+ * Fetch a list of available categories for an author
+ *
+ * @access public
+ * @param mixed If set, the list of categories will be fetched according to the author id. If not set, all categories will be fetched. If set to "all", then all categories will be fetched.
+ * @param string Restrict the list to be returned to a specific category NAME.
+ * @param string The SQL query part for ORDER BY of the categories
+ * @param string The ACL artifact condition. If set to "write" only categories will be shown that the author can write to. If set to "read", only categories will be show that the author can read or write to.
+ * @return array Returns the array of categories
+ */
+function serendipity_fetchCategories($authorid = null, $name = null, $order = null, $artifact_mode = 'write') {
+ global $serendipity;
+
+ if ($name === null) {
+ $name = '';
+ }
+
+ if ($order === null) {
+ $order = 'category_name ASC';
+ }
+
+ if (!isset($authorid) || $authorid === null) {
+ $authorid = ((isset($serendipity['authorid']) && !empty($serendipity['GET']['adminModule'])) ? $serendipity['authorid'] : 1);
+ }
+
+ if (isset($serendipity['authorid']) && !empty($serendipity['GET']['adminModule']) && $authorid != $serendipity['authorid'] && !serendipity_checkPermission('adminCategoriesMaintainOthers')) {
+ $authorid = $serendipity['authorid'];
+ }
+
+ $where = '';
+ if ($authorid != 'all' && is_numeric($authorid)) {
+ $sql_authorid = $authorid;
+ if (!serendipity_checkPermission('adminCategoriesMaintainOthers', $authorid)) {
+ $where = " WHERE (c.authorid = $authorid OR c.authorid = 0)";
+ $where .= "OR (
+ acl.artifact_type = 'category'
+ AND acl.artifact_mode = '" . serendipity_db_escape_string($artifact_mode) . "'
+ ) ";
+
+ }
+ } else {
+ $sql_authorid = 'c.authorid';
+ $where = '';
+ }
+
+ if (!empty($name)) {
+ if ($where == '') {
+ $where = ' WHERE ';
+ } else {
+ $where = ' AND ';
+ }
+
+ $where .= " c.category_name = '" . serendipity_db_escape_string($name) . "'";
+ }
+
+ if ($serendipity['dbType'] == 'postgres') {
+ $group = '';
+ $distinct = 'DISTINCT';
+ } else {
+ $group = 'GROUP BY c.categoryid';
+ $distinct = '';
+ }
+
+ $querystring = "SELECT $distinct c.categoryid,
+ c.category_name,
+ c.category_icon,
+ c.category_description,
+ c.authorid,
+ c.category_left,
+ c.category_right,
+ c.parentid,
+
+ a.username,
+ a.realname
+ FROM {$serendipity['dbPrefix']}category AS c
+ LEFT OUTER JOIN {$serendipity['dbPrefix']}authors AS a
+ ON c.authorid = a.authorid
+ LEFT OUTER JOIN {$serendipity['dbPrefix']}authorgroups AS ag
+ ON ag.authorid = $sql_authorid
+ LEFT OUTER JOIN {$serendipity['dbPrefix']}access AS acl
+ ON (ag.groupid = acl.groupid AND acl.artifact_id = c.categoryid)
+ $where
+ $group";
+ if (!empty($order)) {
+ $querystring .= "\n ORDER BY $order";
+ }
+
+ $ret = serendipity_db_query($querystring);
+ if (is_string($ret)) {
+ echo "Query failed: $ret";
+ }
+ return $ret;
+}
+
+/**
+ * Rebuild the Category Nested Set tree
+ *
+ * @access public
+ * @see Based on http://www.sitepoint.com/article/hierarchical-data-database/1
+ * @param int The ID of the parent category to rebuild
+ * @param int The ID of the next left category
+ * @return int Returns the new ID
+ */
+function serendipity_rebuildCategoryTree($parent = 0, $left = 0) {
+ global $serendipity;
+ $right = $left + 1;
+
+ $result = serendipity_db_query("SELECT categoryid FROM {$serendipity['dbPrefix']}category WHERE parentid = '" . (int)$parent . "'");
+ if ( is_array($result) ) {
+ foreach ( $result as $category ) {
+ $right = serendipity_rebuildCategoryTree($category['categoryid'], $right);
+ }
+ }
+ if ( $parent > 0 ) {
+ serendipity_db_query("UPDATE {$serendipity['dbPrefix']}category SET category_left='{$left}', category_right='{$right}' WHERE categoryid='{$parent}'");
+ }
+
+ return $right + 1;
+}
+
+/**
+ * Searches the list of entries by a specific term
+ *
+ * @todo: Allow to show results of staticpage plugins or others
+ * @access public
+ * @param string The searchterm (may contain wildcards)
+ * @param int Restrict the number of results [also uses $serendipity['GET']['page'] for pagination]
+ * @return array Returns the superarray of entries found
+ */
+function serendipity_searchEntries($term, $limit = '') {
+ global $serendipity;
+
+ if ($limit == '') {
+ $limit = $serendipity['fetchLimit'];
+ }
+
+ if (isset($serendipity['GET']['page']) && $serendipity['GET']['page'] > 1 && !strstr($limit, ',')) {
+ $limit = serendipity_db_limit(($serendipity['GET']['page']-1) * $limit, $limit);
+ }
+
+ $limit = serendipity_db_limit_sql($limit);
+
+ $term = serendipity_db_escape_string($term);
+ $cond = array();
+ if ($serendipity['dbType'] == 'postgres') {
+ $group = '';
+ $distinct = 'DISTINCT';
+ $cond['find_part'] = "(title ILIKE '%$term%' OR body ILIKE '%$term%' OR extended ILIKE '%$term%')";
+ } elseif ($serendipity['dbType'] == 'sqlite') {
+ // Very extensive SQLite search. There currently seems no other way to perform fulltext search in SQLite
+ // But it's better than no search at all :-D
+ $group = 'GROUP BY e.id';
+ $distinct = '';
+ $term = serendipity_mb('strtolower', $term);
+ $cond['find_part'] = "(lower(title) LIKE '%$term%' OR lower(body) LIKE '%$term%' OR lower(extended) LIKE '%$term%')";
+ } else {
+ $group = 'GROUP BY e.id';
+ $distinct = '';
+ $term = str_replace('"', '"', $term);
+ if (preg_match('@["\+\-\*~<>\(\)]+@', $term)) {
+ $cond['find_part'] = "MATCH(title,body,extended) AGAINST('$term' IN BOOLEAN MODE)";
+ } else {
+ $cond['find_part'] = "MATCH(title,body,extended) AGAINST('$term')";
+ }
+ }
+
+ $cond['and'] = " AND isdraft = 'false' " . (!serendipity_db_bool($serendipity['showFutureEntries']) ? " AND timestamp <= " . time() : '');
+ serendipity_plugin_api::hook_event('frontend_fetchentries', $cond, array('source' => 'search', 'term' => $term));
+
+ serendipity_ACL_SQL($cond, 'limited');
+
+ $serendipity['fullCountQuery'] = "
+ FROM
+ {$serendipity['dbPrefix']}entries e
+ LEFT JOIN {$serendipity['dbPrefix']}authors a
+ ON e.authorid = a.authorid
+ LEFT JOIN {$serendipity['dbPrefix']}entrycat ec
+ ON e.id = ec.entryid
+ {$cond['joins']}
+ WHERE
+ ({$cond['find_part']})
+ {$cond['and']}";
+
+ $querystring = "SELECT $distinct
+ e.id,
+ e.authorid,
+ a.realname AS author,
+ a.email,
+ e.timestamp,
+ e.comments,
+ e.title,
+ e.body,
+ e.extended,
+ e.trackbacks,
+ e.exflag
+ {$serendipity['fullCountQuery']}
+ $group
+ ORDER BY timestamp DESC
+ $limit";
+
+ $search = serendipity_db_query($querystring);
+
+ if (is_array($search)) {
+ serendipity_fetchEntryData($search);
+ }
+
+ return $search;
+}
+
+/**
+ * Creates the Footer below the entries, with pagination options and parses it to Smarty
+ *
+ * The list of total entries is calculated from the serendipity_getTotelEntries() function
+ *
+ * @access public
+ * @see serendipity_getTotalEntries()
+ * @return null
+ */
+function serendipity_printEntryFooter() {
+ global $serendipity;
+
+ $totalEntries = serendipity_getTotalEntries();
+ $totalPages = ceil($totalEntries / $serendipity['fetchLimit']);
+
+ if (!isset($serendipity['GET']['page'])) {
+ $serendipity['GET']['page'] = 1;
+ }
+
+ if ($totalPages <= 0 ) {
+ $totalPages = 1;
+ }
+
+ if ($serendipity['GET']['page'] > 1) {
+ $uriArguments = $serendipity['uriArguments'];
+ $uriArguments[] = 'P'. ($serendipity['GET']['page'] - 1);
+ $serendipity['smarty']->assign('footer_prev_page', serendipity_rewriteURL(implode('/', $uriArguments) .'.html'));
+ }
+
+ $uriArguments = $serendipity['uriArguments'];
+ $uriArguments[] = 'P%s';
+ $serendipity['smarty']->assign('footer_totalEntries', $totalEntries);
+ $serendipity['smarty']->assign('footer_totalPages', $totalPages);
+ $serendipity['smarty']->assign('footer_currentPage', $serendipity['GET']['page']);
+ $serendipity['smarty']->assign('footer_pageLink', serendipity_rewriteURL(implode('/', $uriArguments) . '.html'));
+ $serendipity['smarty']->assign('footer_info', sprintf(PAGE_BROWSE_ENTRIES, (int)$serendipity['GET']['page'], $totalPages, $totalEntries));
+
+ if ($serendipity['GET']['page'] < $totalPages) {
+ $uriArguments = $serendipity['uriArguments'];
+ $uriArguments[] = 'P'. ($serendipity['GET']['page'] + 1);
+ $serendipity['smarty']->assign('footer_next_page', serendipity_rewriteURL(implode('/', $uriArguments) .'.html'));
+ }
+}
+
+/**
+ * Calculates the amount of available entries.
+ *
+ * This function uses the SQL query portion of the central serendipity_fetchEntries() query
+ * and modifies it with different GROUP statements to calculate the number of entries.
+ *
+ * @access public
+ * @see serendipity_fetchEntries()
+ * @return int The number of total entries
+ */
+function serendipity_getTotalEntries() {
+ global $serendipity;
+
+ // The unique query condition was built previously in serendipity_fetchEntries()
+ if ($serendipity['dbType'] == 'sqlite') {
+ $querystring = "SELECT count(e.id) {$serendipity['fullCountQuery']} GROUP BY e.id";
+ } else {
+ $querystring = "SELECT count(distinct e.id) {$serendipity['fullCountQuery']}";
+ }
+
+ $query = serendipity_db_query($querystring);
+
+ if (is_array($query) && isset($query[0])) {
+ if ($serendipity['dbType'] == 'sqlite') {
+ return count($query);
+ } else {
+ return $query[0][0];
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Passes the list of fetched entries from serendipity_fetchEntries() on to the Smarty layer
+ *
+ * This function contains all the core logic to group and prepare entries to be shown in your
+ * $entries.tpl template. It groups them by date, so that you can easily loop on the set of
+ * entries.
+ * This function is not only used for printing all entries, but also for printing individual
+ * entries.
+ * Several central Event hooks are executed here for the whole page flow, like header+footer data.
+ *
+ * @see serendipity_fetchEntries()
+ * @see serendipity_searchEntries()
+ * @access public
+ * @param array The array of entries with all of its data
+ * @param boolean Toggle whether the extended portion of an entry is requested (via $serendipity['GET']['id'] single entry view)
+ * @param boolean Indicates if this is a preview
+ * @param string The name of the SMARTY block that this gets parsed into
+ * @param boolean Indicates whether the assigned smarty variables should be parsed
+ * @param boolean Indicates whether to apply footer/header event hooks
+ * @param boolean Indicates whether the pagination footer should be displayed
+ * @param mixed Indicates whether the input $entries array is already grouped in preparation for the smarty $entries output array [TRUE], or if it shall be grouped by date [FALSE] or if a plugin hook shall be executed to modify data ['plugin']. This setting can also be superseded by a 'entry_display' hook.
+ * @return
+ */
+function serendipity_printEntries($entries, $extended = 0, $preview = false, $smarty_block = 'ENTRIES', $smarty_fetch = true, $use_hooks = true, $use_footer = true, $use_grouped_array = false) {
+ global $serendipity;
+
+ if ($use_hooks) {
+ $addData = array('extended' => $extended, 'preview' => $preview);
+ serendipity_plugin_api::hook_event('entry_display', $entries, $addData);
+
+ if (isset($entries['clean_page']) && $entries['clean_page'] === true) {
+ $serendipity['smarty']->assign('plugin_clean_page', true);
+ serendipity_smarty_fetch($smarty_block, 'entries.tpl', true);
+ return; // no display of this item
+ }
+ }
+
+ // We shouldn't return here, because we want Smarty to handle the output
+ if (!is_array($entries) || $entries[0] == false || !isset($entries[0]['timestamp'])) {
+ $entries = array();
+ }
+
+ // A plugin executed in entry_display should be able to change the way of ordering entries. Forward-Thinking. ;)
+ if (isset($entries['use_grouped_array'])) {
+ $use_grouped_array = $entries['use_grouped_array'];
+ }
+
+ if ($use_grouped_array === false) {
+ // Use grouping by date (default)
+ $dategroup = array();
+ for ($x = 0, $num_entries = count($entries); $x < $num_entries; $x++) {
+ if (!empty($entries[$x]['properties']['ep_is_sticky']) && serendipity_db_bool($entries[$x]['properties']['ep_is_sticky'])) {
+ $entries[$x]['is_sticky'] = true;
+ $key = 'sticky';
+ } else {
+ $key = date('Ymd', serendipity_serverOffsetHour($entries[$x]['timestamp']));
+ }
+
+ $dategroup[$key]['date'] = $entries[$x]['timestamp'];
+ $dategroup[$key]['is_sticky'] = (isset($entries[$x]['is_sticky']) && serendipity_db_bool($entries[$x]['is_sticky']) ? true : false);
+ $dategroup[$key]['entries'][] = &$entries[$x];
+ }
+ } elseif ($use_grouped_array === 'plugin') {
+ // Let a plugin do the grouping
+ serendipity_plugin_api::hook_event('entry_groupdata', $entries);
+ $dategroup =& $entries;
+ } else {
+ $dategroup =& $entries;
+ }
+
+ foreach($dategroup as $properties) {
+ foreach($properties['entries'] as $x => $_entry) {
+ $entry = &$properties['entries'][$x]; // PHP4 Compat
+
+ if (!empty($entry['properties']['ep_cache_body'])) {
+ $entry['body'] = &$entry['properties']['ep_cache_body'];
+ $entry['is_cached'] = true;
+ }
+
+ if (!empty($entry['properties']['ep_cache_extended'])) {
+ $entry['extended'] = &$entry['properties']['ep_cache_extended'];
+ $entry['is_cached'] = true;
+ }
+
+ if ($preview) {
+ $entry['author'] = $entry['realname'];
+ $entry['authorid'] = $serendipity['authorid'];
+ }
+
+ $addData = array('from' => 'functions_entries:printEntries');
+ if ($entry['is_cached']) {
+ $addData['no_scramble'] = true;
+ }
+ serendipity_plugin_api::hook_event('frontend_display', $entry, $addData);
+
+ if ($preview) {
+ $entry['author'] = $entry['realname'];
+ $entry['authorid'] = $serendipity['authorid'];
+ }
+
+ $authorData = array(
+ 'authorid' => $entry['authorid'],
+ 'username' => $entry['author'],
+ 'email' => $entry['email'],
+ 'realname' => $entry['author']
+ );
+
+ $entry['link'] = serendipity_archiveURL($entry['id'], $entry['title'], 'serendipityHTTPPath', true, array('timestamp' => $entry['timestamp']));
+ $entry['commURL'] = serendipity_archiveURL($entry['id'], $entry['title'], 'baseURL', false, array('timestamp' => $entry['timestamp']));
+ $entry['rdf_ident'] = serendipity_archiveURL($entry['id'], $entry['title'], 'baseURL', true, array('timestamp' => $entry['timestamp']));
+ $entry['title'] = htmlspecialchars($entry['title']);
+
+ $entry['link_allow_comments'] = $serendipity['baseURL'] . 'comment.php?serendipity[switch]=enable&serendipity[entry]=' . $entry['id'];
+ $entry['link_deny_comments'] = $serendipity['baseURL'] . 'comment.php?serendipity[switch]=disable&serendipity[entry]=' . $entry['id'];
+ $entry['allow_comments'] = serendipity_db_bool($entry['allow_comments']);
+ $entry['moderate_comments'] = serendipity_db_bool($entry['moderate_comments']);
+ $entry['viewmode'] = ($serendipity['GET']['cview'] == VIEWMODE_LINEAR ? VIEWMODE_LINEAR : VIEWMODE_THREADED);
+ $entry['link_popup_comments'] = $serendipity['serendipityHTTPPath'] .'comment.php?serendipity[entry_id]='. $entry['id'] .'&serendipity[type]=comments';
+ $entry['link_popup_trackbacks'] = $serendipity['serendipityHTTPPath'] .'comment.php?serendipity[entry_id]='. $entry['id'] .'&serendipity[type]=trackbacks';
+ $entry['link_edit'] = $serendipity['baseURL'] .'serendipity_admin.php?serendipity[action]=admin&serendipity[adminModule]=entries&serendipity[adminAction]=edit&serendipity[id]='. $entry['id'];
+ $entry['link_trackback'] = $serendipity['baseURL'] .'comment.php?type=trackback&entry_id='. $entry['id'];
+ $entry['link_rdf'] = serendipity_rewriteURL(PATH_FEEDS . '/ei_'. $entry['id'] .'.rdf');
+ $entry['link_viewmode_threaded'] = $serendipity['serendipityHTTPPath'] . $serendipity['indexFile'] .'?url='. $entry['commURL'] .'&serendipity[cview]='. VIEWMODE_THREADED;
+ $entry['link_viewmode_linear'] = $serendipity['serendipityHTTPPath'] . $serendipity['indexFile'] .'?url='. $entry['commURL'] .'&serendipity[cview]='. VIEWMODE_LINEAR;
+ $entry['link_author'] = serendipity_authorURL($authorData);
+
+ if (is_array($entry['categories'])) {
+ foreach ($entry['categories'] as $k => $v) {
+ $entry['categories'][$k]['category_link'] = serendipity_categoryURL($entry['categories'][$k]);
+ }
+ }
+
+ if (strlen($entry['extended'])) {
+ $entry['has_extended'] = true;
+ }
+
+ if (isset($entry['exflag']) && $entry['exflag'] && ($extended || $preview)) {
+ $entry['is_extended'] = true;
+ }
+
+ if (serendipity_db_bool($entry['allow_comments']) || !isset($entry['allow_comments']) || $entry['comments'] > 0) {
+ $entry['has_comments'] = true;
+ $entry['label_comments'] = $entry['comments'] == 1 ? COMMENT : COMMENTS;
+ }
+
+ if (serendipity_db_bool($entry['allow_comments']) || !isset($entry['allow_comments']) || $entry['trackbacks'] > 0) {
+ $entry['has_trackbacks'] = true;
+ $entry['label_trackbacks'] = $entry['trackbacks'] == 1 ? TRACKBACK : TRACKBACKS;
+ }
+
+ if ($_SESSION['serendipityAuthedUser'] === true && ($_SESSION['serendipityAuthorid'] == $entry['authorid'] || serendipity_checkPermission('adminEntriesMaintainOthers'))) {
+ $entry['is_entry_owner'] = true;
+ }
+
+ $entry['display_dat'] = '';
+ serendipity_plugin_api::hook_event('frontend_display:html:per_entry', $entry);
+ $entry['plugin_display_dat'] =& $entry['display_dat'];
+
+ if ($preview) {
+ ob_start();
+ serendipity_plugin_api::hook_event('backend_preview', $entry);
+ $entry['backend_preview'] = ob_get_contents();
+ ob_end_clean();
+ }
+
+ /* IF WE ARE DISPLAYING A FULL ENTRY */
+ if (isset($serendipity['GET']['id'])) {
+ $serendipity['smarty']->assign(
+ array(
+ 'comments_messagestack' => (isset($serendipity['messagestack']['comments']) ? (array)$serendipity['messagestack']['comments'] : array()),
+ 'is_comment_added' => (isset($serendipity['GET']['csuccess']) && $serendipity['GET']['csuccess'] == 'true' ? true: false),
+ 'is_comment_moderate' => (isset($serendipity['GET']['csuccess']) && $serendipity['GET']['csuccess'] == 'moderate' ? true: false)
+ )
+ );
+
+ serendipity_displayCommentForm(
+ $entry['id'],
+ $serendipity['serendipityHTTPPath'] . $serendipity['indexFile'] . '?url=' . $entry['commURL'],
+ true,
+ $serendipity['POST'],
+ true,
+ serendipity_db_bool($entry['moderate_comments']),
+ $entry
+ );
+ } // END FULL ENTRY LOGIC
+ } // end foreach-loop (entries)
+ } // end foreach-loop (dates)
+
+ if (!isset($serendipity['GET']['id']) &&
+ (!isset($serendipity['hidefooter']) || $serendipity['hidefooter'] == false) &&
+ ($num_entries <= $serendipity['fetchLimit']) &&
+ $use_footer) {
+ serendipity_printEntryFooter();
+ }
+
+ $serendipity['smarty']->assign('entries', $dategroup);
+ unset($entries, $dategroup);
+
+ if (isset($serendipity['short_archives']) && $serendipity['short_archives']) {
+ serendipity_smarty_fetch($smarty_block, 'entries_summary.tpl', true);
+ } elseif ($smarty_fetch == true) {
+ serendipity_smarty_fetch($smarty_block, 'entries.tpl', true);
+ }
+
+} // end function serendipity_printEntries
+
+/**
+ * Deprecated: Delete some garbage when an entry was deleted, especially static pages
+ *
+ * @deprecated
+ * @access public
+ * @param int The deleted entry ID
+ * @param int A timestamp for the entry archive page
+ * @return null
+ */
+function serendipity_purgeEntry($id, $timestamp = null) {
+ global $serendipity;
+
+ // If pregenerate is not set, short circuit all this logic
+ // and remove nothing.
+ if(!isset($serendipity['pregenerate'])) {
+ return;
+ }
+
+ if (isset($timestamp)) {
+ $dated = date('Ymd', serendipity_serverOffsetHour($timestamp));
+ $datem = date('Ym', serendipity_serverOffsetHour($timestamp));
+
+ @unlink("{$serendipity['serendipityPath']}/".PATH_ARCHIVES."/{$dated}.html");
+ @unlink("{$serendipity['serendipityPath']}/".PATH_ARCHIVES."/{$datem}.html");
+ }
+
+ // Fixme (the _* part) !
+ @unlink("{$serendipity['serendipityPath']}/".PATH_ARCHIVES."/{$id}_*.html");
+ @unlink("{$serendipity['serendipityPath']}/".PATH_FEEDS."/index.rss");
+ @unlink("{$serendipity['serendipityPath']}/".PATH_FEEDS."/index.rss2");
+ @unlink("{$serendipity['serendipityPath']}/index.html");
+}
+
+/**
+ * Inserts a new entry into the database or updates an existing entry
+ *
+ * Another central function, that parses, prepares and commits changes to an entry
+ *
+ * @access public
+ * @param array The new/modified entry data.
+ * @return mixed Integer with new entry ID if successfull, a string or array if error(s).
+ */
+function serendipity_updertEntry($entry) {
+ global $serendipity;
+
+ include_once S9Y_INCLUDE_PATH . 'include/functions_entries_admin.inc.php';
+
+ $errors = array();
+ serendipity_plugin_api::hook_event('backend_entry_updertEntry', $errors, $entry);
+ if (count($errors) > 0) {
+ // Return error message(s)
+ return implode("\n", $errors);
+ }
+
+ serendipity_plugin_api::hook_event('backend_entry_presave', $entry);
+
+ $categories = $entry['categories'];
+ unset($entry['categories']);
+
+ $newEntry = 0;
+ $exflag = 0;
+
+ if (!is_numeric($entry['timestamp'])) {
+ $entry['timestamp'] = time();
+ }
+
+ /* WYSIWYG-editor inserts empty ' ' for extended body; this is reversed here */
+ if (isset($entry['extended']) && (trim($entry['extended']) == '' || trim($entry['extended']) == '<br />' || trim($entry['extended']) == '<p></p>' || str_replace(array("\r", "\n", "\t", "\0", "<br />", "<p>", "</p>", "<br>"), array('', '', '', '', '', '', '', ''), trim($entry['extended'])) == '')) {
+ $entry['extended'] = '';
+ }
+
+ if (strlen($entry['extended'])) {
+ $exflag = 1;
+ }
+
+ $entry['exflag'] = $exflag;
+
+ if (!is_numeric($entry['id'])) {
+ /* we need to insert */
+
+ unset($entry['id']);
+ $entry['comments'] = 0;
+
+ if (!isset($entry['last_modified']) || !is_numeric($entry['last_modified'])) {
+ $entry['last_modified'] = $entry['timestamp'];
+ }
+
+ // New entries need an author
+ $entry['author'] = $serendipity['user'];
+ if (!isset($entry['authorid']) || empty($entry['authorid'])) {
+ $entry['authorid'] = $serendipity['authorid'];
+ }
+
+ if (!$_SESSION['serendipityRightPublish']) {
+ $entry['isdraft'] = 'true';
+ }
+
+ if(!isset($entry['allow_comments'])){
+ $entry['allow_comments']='false';
+ }
+ if(!isset($entry['moderate_comments'])){
+ $entry['moderate_comments']='false';
+ }
+
+ $res = serendipity_db_insert('entries', $entry);
+
+ if ($res) {
+ $entry['id'] = $serendipity['lastSavedEntry'] = serendipity_db_insert_id('entries', 'id');
+ if (is_array($categories)) {
+ foreach ($categories as $cat) {
+ if (is_numeric($cat)) {
+ serendipity_db_query("INSERT INTO {$serendipity['dbPrefix']}entrycat (entryid, categoryid) VALUES ({$entry['id']}, {$cat})");
+ }
+ }
+ }
+
+ serendipity_insertPermalink($entry);
+ } else {
+ //Some error message here
+ return ENTRIES_NOT_SUCCESSFULLY_INSERTED;
+ }
+ $newEntry = 1;
+ } else {
+ /* we need to update */
+
+ // Get settings from entry if already in DB, which should not be alterable with POST methods
+ $_entry = serendipity_fetchEntry('id', $entry['id'], 1, 1);
+ $entry['authorid'] = $_entry['authorid'];
+
+ if (isset($serendipity['GET']['adminModule']) && $serendipity['GET']['adminModule'] == 'entries' && $entry['authorid'] != $serendipity['authorid'] && !serendipity_checkPermission('adminEntriesMaintainOthers')) {
+ // Only chiefs and admins can change other's entry. Else update fails.
+ return;
+ }
+
+ if (!$_SESSION['serendipityRightPublish']) {
+ unset($entry['isdraft']);
+ }
+
+ if (is_array($categories)) {
+ serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}entrycat WHERE entryid={$entry['id']}");
+ foreach ($categories as $cat) {
+ serendipity_db_query("INSERT INTO {$serendipity['dbPrefix']}entrycat (entryid, categoryid) VALUES ({$entry['id']}, {$cat})");
+ }
+ }
+
+ if ($entry['isdraft'] === 'false' && $_entry['isdraft'] === 'false') {
+ $entry['last_modified'] = time();
+ }
+
+ $res = serendipity_db_update('entries', array('id' => $entry['id']), $entry);
+ $newEntry = 0;
+ serendipity_updatePermalink($entry);
+ }
+
+ if (is_string($res)) {
+ return $res;
+ }
+
+ // Reset session data, so that a reload to this frame should not happen!
+ $_SESSION['save_entry']['id'] = (int)$entry['id'];
+
+ if (!serendipity_db_bool($entry['isdraft'])) {
+ serendipity_plugin_api::hook_event('frontend_display', $entry, array('no_scramble' => true, 'from' => 'functions_entries:updertEntry'));
+ serendipity_handle_references($entry['id'], $serendipity['blogTitle'], $entry['title'], $entry['body'] . $entry['extended'], $newEntry);
+ }
+
+ serendipity_purgeEntry($entry['id'], $entry['timestamp']);
+
+ // Send publish tags if either a new article has been inserted from scratch, or if the entry was previously
+ // stored as draft and is now published
+ $entry['categories'] =& $categories;
+ if (!serendipity_db_bool($entry['isdraft']) && ($newEntry || serendipity_db_bool($_entry['isdraft']))) {
+ serendipity_plugin_api::hook_event('backend_publish', $entry, $newEntry);
+ } else {
+ serendipity_plugin_api::hook_event('backend_save', $entry, $newEntry);
+ }
+
+ return (int)$entry['id'];
+}
+
+/**
+ * Delete an entry and everything that belongs to it (comments)
+ *
+ * @access public
+ * @param int The Entry ID to delete
+ * @return mixed FALSE or NULL on error
+ */
+function serendipity_deleteEntry($id) {
+ global $serendipity;
+
+ if (!is_numeric($id)) {
+ return false;
+ }
+
+ // Purge the daily/monthly entries so they can be rebuilt
+ $result = serendipity_db_query("SELECT timestamp, authorid FROM {$serendipity['dbPrefix']}entries WHERE id = '". (int)$id ."'", true);
+
+ if ($result[1] != $serendipity['authorid'] && !serendipity_checkPermission('adminEntriesMaintainOthers')) {
+ // Only admins and chief users can delete entries which do not belong to the author
+ return;
+ }
+
+ serendipity_purgeEntry($id, $result[0]);
+
+ serendipity_plugin_api::hook_event('backend_delete_entry', $id);
+ serendipity_db_query("DELETE FROM {$serendipity["dbPrefix"]}entries WHERE id=$id");
+ serendipity_db_query("DELETE FROM {$serendipity["dbPrefix"]}entrycat WHERE entryid=$id");
+ serendipity_db_query("DELETE FROM {$serendipity["dbPrefix"]}entryproperties WHERE entryid=$id");
+ serendipity_db_query("DELETE FROM {$serendipity["dbPrefix"]}comments WHERE entry_id=$id");
+ serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}references WHERE entry_id='$id'");
+ serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}permalinks WHERE entry_id='$id'");
+}
+
+/**
+* Return HTML containing a list of categories
+*
+* Prints a list of categories for use in forms, the sidebar, or whereever...
+*
+* @access public
+* @param array An array of categories, typically gathered by serendipity_fetchCategories()
+* @param array An array which holds IDs which are meant to be selected within a HTML SELECT form field [used for recursion]
+* @param int The type of category list (0: HTML span list, 1/2: <option>s, 3: HTML Div list, 4: CSV data) to return
+* @param int The parent ID of a category [for recursion]
+* @param int The current nesting level [for recursion]
+* @param string Tells the function, whether or not to display the XML button for each category.
+* If empty, no links to the xml feeds will be displayed; If you want to, you can
+* pass an image here (this setting is only used, when type==3).
+* @param string The character to use for blank indenting
+* @see serendipity_fetchCategories()
+*/
+function serendipity_generateCategoryList($cats, $select = array(0), $type = 0, $id = 0, $level = 0, $xmlImg = '', $blank_char = ' ') {
+ global $serendipity;
+
+ if ( !is_array($cats) || !count($cats) )
+ return;
+
+ $ret = '';
+ foreach ($cats as $cat) {
+ if ($cat['parentid'] == $id) {
+ switch ($type) {
+ case 0:
+ $ret .= str_repeat(' ', $level * 2).'• <span id="catItem_' . $cat['categoryid'] . '"' . (($cat['categoryid'] && in_array($cat['categoryid'], $select)) ? ' selected="selected"' : '') . '><a href="?serendipity[adminModule]=category&serendipity[cat][catid]=' . $cat['categoryid'] . '">' . (!empty($cat['category_icon']) ? '<img style="vertical-align: middle;" src="' . $cat['category_icon'] . '" border="0" alt="' . $cat['category_name'] . '"/> ' : '') . htmlspecialchars($cat['category_name']) . (!empty($cat['category_description']) ? ' - ' . htmlspecialchars($cat['category_description']) : '') . '</a></span><br/>' . "\n";
+ break;
+ case 1:
+ case 2:
+ $ret .= '<option value="' . $cat['categoryid'] . '"' . (($cat['categoryid'] && in_array($cat['categoryid'], $select)) ? ' selected="selected"' : '') . '>';
+ $ret .= str_repeat(' ', $level * 2) . htmlspecialchars($cat['category_name']) . ($type == 1 && !empty($cat['category_description']) ? (' - ' . htmlspecialchars($cat['category_description'])) : '');
+ $ret .= '</option>';
+ break;
+ case 3:
+ $category_id = serendipity_makeFilename($cat['category_name']);
+ if (!empty($xmlImg)) {
+ $ret .= sprintf(
+ '<div style="padding-bottom: 2px;">' .
+ '<a href="%s" title="%s"><img alt="xml" src="%s" style="vertical-align: bottom; display: inline; border: 0px" /></a> %s' .
+ '<a href="%s" title="%s">%s</a>' .
+ '</div>',
+ $serendipity['serendipityHTTPPath'] . 'rss.php?category=' . $cat['categoryid'] . '_' . $category_id,
+ htmlspecialchars($cat['category_description']),
+ $xmlImg,
+ str_repeat(' ', $level * 3),
+ serendipity_categoryURL($cat, 'serendipityHTTPPath'),
+ htmlspecialchars($cat['category_description']),
+ htmlspecialchars($cat['category_name']));
+ } else {
+ $ret .= sprintf(
+ '%s<a href="%s" title="%s">%s</a><br />',
+ str_repeat(' ', $level * 3),
+ serendipity_categoryURL($cat, 'serendipityHTTPPath'),
+ htmlspecialchars($cat['category_description']),
+ htmlspecialchars($cat['category_name']));
+ }
+ break;
+ case 4:
+ $ret .= $cat['categoryid'] . '|||' . str_repeat($blank_char, $level * 2) . $cat['category_name'] . '@@@';
+ break;
+ }
+ $ret .= serendipity_generateCategoryList($cats, $select, $type, $cat['categoryid'], $level + 1, $xmlImg, $blank_char);
+ }
+ }
+ return $ret;
+}
+
+/**
+ * Set category associations of a specific entry
+ *
+ * @access public
+ * @param int The ID of the entry
+ * @param array An array of category IDs that this entry is associated to.
+ * @return null
+ */
+function serendipity_updateEntryCategories($postid, $categories) {
+ global $serendipity;
+
+ if (!$postid || !$categories) {
+ return;
+ }
+
+ $query = "DELETE FROM $serendipity[dbPrefix]entrycat WHERE entryid = " . (int)$postid;
+ serendipity_db_query($query);
+
+ if (!is_array($categories)) {
+ $categories = array(0 => $categories);
+ }
+
+ foreach($categories AS $idx => $cat) {
+ $query = "INSERT INTO $serendipity[dbPrefix]entrycat (categoryid, entryid) VALUES (" . (int)$cat . ", " . (int)$postid . ")";
+ serendipity_db_query($query);
+ }
+}
+
+/**
+ * Gather an archive listing of older entries and passes it to Smarty
+ *
+ * The archives are created according to the current timestamp and show the current year.
+ * $serendipity['GET']['category'] is honoured like in serendipity_fetchEntries()
+ *
+ * @access public
+ * @return null
+ */
+function serendipity_printArchives() {
+ global $serendipity;
+
+ $f = serendipity_db_query("SELECT timestamp FROM {$serendipity['dbPrefix']}entries ORDER BY timestamp ASC LIMIT 1");
+ switch($serendipity['calendar']) {
+ case 'gregorian':
+ default:
+ $lastYear = date('Y', serendipity_serverOffsetHour($f[0][0]));
+ $lastMonth = date('m', serendipity_serverOffsetHour($f[0][0]));
+ $thisYear = date('Y', serendipity_serverOffsetHour());
+ $thisMonth = date('m', serendipity_serverOffsetHour());
+ break;
+ case 'jalali-utf8':
+ require_once S9Y_INCLUDE_PATH . 'include/functions_calendars.inc.php';
+ $lastYear = jalali_date_utf('Y', serendipity_serverOffsetHour($f[0][0]));
+ $lastMonth = jalali_date_utf('m', serendipity_serverOffsetHour($f[0][0]));
+ $thisYear = jalali_date_utf('Y', serendipity_serverOffsetHour());
+ $thisMonth = jalali_date_utf('m', serendipity_serverOffsetHour());
+ break;
+ }
+ $max = 0;
+
+ if (isset($serendipity['GET']['category'])) {
+ $cat_sql = serendipity_getMultiCategoriesSQL($serendipity['GET']['category']);
+ $cat_get = '/C' . (int)$serendipity['GET']['category'];
+ } else {
+ $cat_sql = '';
+ $cat_get = '';
+ }
+
+ $output = array();
+ for ($y = $thisYear; $y >= $lastYear; $y--) {
+ $output[$y]['year'] = $y;
+ for ($m = 12; $m >= 1; $m--) {
+
+ /* If the month we are checking are in the future, we drop it */
+ if ($m > $thisMonth && $y == $thisYear) {
+ continue;
+ }
+
+ /* If the month is lower than the lowest month containing entries, we're done */
+ if ($m < $lastMonth && $y <= $lastYear) {
+ break;
+ }
+
+ switch($serendipity['calendar']) {
+ case 'gregorian':
+ default:
+ $s = serendipity_serverOffsetHour(mktime(0, 0, 0, $m, 1, $y), true);
+ $e = serendipity_serverOffsetHour(mktime(23, 59, 59, $m, date('t', $s), $y), true);
+ break;
+ case 'jalali-utf8':
+ require_once S9Y_INCLUDE_PATH . 'include/functions_calendars.inc.php';
+ $s = serendipity_serverOffsetHour(jalali_mktime(0, 0, 0, $m, 1, $y), true);
+ $e = serendipity_serverOffsetHour(jalali_mktime(23, 59, 59, $m, date('t', $s), $y), true);
+ break;
+ }
+
+ $entries = serendipity_db_query("SELECT count(id)
+ FROM {$serendipity['dbPrefix']}entries e
+ LEFT JOIN {$serendipity['dbPrefix']}entrycat ec
+ ON e.id = ec.entryid
+ LEFT JOIN {$serendipity['dbPrefix']}category c
+ ON ec.categoryid = c.categoryid
+ WHERE isdraft = 'false'
+ AND timestamp >= $s
+ AND timestamp <= $e "
+ . (!serendipity_db_bool($serendipity['showFutureEntries']) ? " AND timestamp <= " . time() : '')
+ . (!empty($cat_sql) ? ' AND ' . $cat_sql : '') . "
+ GROUP BY ec.entryid", false, 'assoc');
+ if (is_array($entries)) {
+ $entry_count = count($entries);
+ } else {
+ $entry_count = 0;
+ }
+
+ /* A silly hack to get the maximum amount of entries per month */
+ if ($entry_count > $max) {
+ $max = $entry_count;
+ }
+
+ $data = array();
+ $data['entry_count'] = $entry_count;
+ $data['link'] = serendipity_archiveDateUrl($y . '/'. sprintf('%02s', $m) . $cat_get);
+ $data['link_summary'] = serendipity_archiveDateUrl($y . '/'. sprintf('%02s', $m) . $cat_get, true);
+ $data['date'] = $s;
+ $output[$y]['months'][] = $data;
+ }
+ }
+ $serendipity['smarty']->assign(array('archives' => $output,
+ 'max_entries' => $max));
+
+ serendipity_smarty_fetch('ARCHIVES', 'entries_archives.tpl', true);
+}
+
+/**
+ * Get total count for specific objects
+ *
+ * @access public
+ * @param string The type of count to show: "entries", "trackbacks", "comments"
+ * @return string The number
+ */
+function serendipity_getTotalCount($what) {
+ global $serendipity;
+
+ switch($what) {
+ case 'comments':
+ $res = serendipity_db_query("SELECT SUM(e.comments) AS sum
+ FROM {$serendipity['dbPrefix']}entries AS e
+ WHERE e.isdraft = 'false'
+ " . (!serendipity_db_bool($serendipity['showFutureEntries']) ? " AND e.timestamp <= " . time() : ''), true, 'assoc');
+ return $res['sum'];
+ case 'trackbacks':
+ $res = serendipity_db_query("SELECT SUM(e.trackbacks) AS sum
+ FROM {$serendipity['dbPrefix']}entries AS e
+ WHERE e.isdraft = 'false'
+ " . (!serendipity_db_bool($serendipity['showFutureEntries']) ? " AND e.timestamp <= " . time() : ''), true, 'assoc');
+ return $res['sum'];
+ case 'entries':
+ $res = serendipity_db_query("SELECT COUNT(e.id) AS sum
+ FROM {$serendipity['dbPrefix']}entries AS e
+ WHERE e.isdraft = 'false'
+ " . (!serendipity_db_bool($serendipity['showFutureEntries']) ? " AND e.timestamp <= " . time() : ''), true, 'assoc');
+ return $res['sum'];
+
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php # $Id: functions_entries.inc.php 435 2005-08-25 12:36:39Z garvinhicking $
+# Copyright (c) 2003-2005, Jannis Hermanns (on behalf the Serendipity Developer Team)
+# All rights reserved. See LICENSE file for licensing details
+
+include_once(S9Y_INCLUDE_PATH . "include/functions_trackbacks.inc.php");
+
+/**
+ * Prints the form for editing/creating new blog entries
+ *
+ * This is the core file where your edit form appears. The Heart Of Gold, so to say.
+ *
+ * @access public
+ * @param string The URL where the entry form is submitted to
+ * @param array An array of hidden input fields that should be passed on to the HTML FORM
+ * @param array The entry superarray with your entry's contents
+ * @param string Any error messages that might have occured on the last run
+ * @return null
+ */
+function serendipity_printEntryForm($targetURL, $hiddens = array(), $entry = array(), $errMsg = "") {
+ global $serendipity;
+
+ $serendipity['EditorBrowsers'] = '(IE|Mozilla|Opera)';
+
+ $draftD = '';
+ $draftP = '';
+ $categoryselector_expanded = false;
+
+ serendipity_plugin_api::hook_event('backend_entryform', $entry);
+
+ if ( (isset($entry['isdraft']) && serendipity_db_bool($entry['isdraft'])) ||
+ (!isset($entry['isdraft']) && $serendipity['publishDefault'] == 'draft') ) {
+ $draftD = ' selected="selected"';
+ } else {
+ $draftP = ' selected="selected"';
+ }
+
+ if (isset($entry['moderate_comments']) && (serendipity_db_bool($entry['moderate_comments']))) {
+ $moderate_comments = ' checked="checked"';
+ } elseif (!isset($entry['moderate_comments']) && ($serendipity['moderateCommentsDefault'] == 'true' || $serendipity['moderateCommentsDefault'] === true)) {
+ // This is the default on creation of a new entry and depends on the "moderateCommentsDefault" variable of the configuration.
+ $moderate_comments = ' checked="checked"';
+ } else {
+ $moderate_comments = '';
+ }
+
+
+ if (isset($entry['allow_comments']) && (serendipity_db_bool($entry['allow_comments']))) {
+ $allow_comments = ' checked="checked"';
+ } elseif ((!isset($entry['allow_comments']) || $entry['allow_comments'] !== 'false') && (!isset($serendipity['allowCommentsDefault']) || $serendipity['allowCommentsDefault'] == 'true' || $serendipity['allowCommentsDefault'] === true)) {
+ // This is the default on creation of a new entry and depends on the "allowCommentsDefault" variable of the configuration.
+ $allow_comments = ' checked="checked"';
+ } else {
+ $allow_comments = '';
+ }
+
+ // Fix category list. If the entryForm is displayed after a POST request, the additional category information is lost.
+ if (is_array($entry['categories']) && !is_array($entry['categories'][0])) {
+ $categories = (array)$entry['categories'];
+ $entry['categories'] = array();
+ foreach ($categories as $catid) {
+ $entry['categories'][] = serendipity_fetchCategoryInfo($catid);
+ }
+ }
+
+ $n = "\n";
+ $cat_list = '<select id="categoryselector" name="serendipity[categories][]" style="vertical-align: middle;" multiple="multiple">' . $n;
+ $cat_list .= ' <option value="0">[' . NO_CATEGORY . ']</option>' . $n;
+ $selected = array();
+ if (is_array($entry['categories'])) {
+ if (count($entry['categories']) > 1) {
+ $categoryselector_expanded = true;
+ }
+
+ foreach ($entry['categories'] as $cat) {
+ $selected[] = $cat['categoryid'];
+ }
+ }
+
+ if (count($selected) > 1 ||
+ (isset($serendipity['POST']['categories']) && is_array($serendipity['POST']['categories']) && sizeof($serendipity['POST']['categories']) > 1)) {
+ $categoryselector_expanded = true;
+ }
+
+ if (is_array($cats = serendipity_fetchCategories())) {
+ $cats = serendipity_walkRecursive($cats, 'categoryid', 'parentid', VIEWMODE_THREADED);
+ foreach ( $cats as $cat ) {
+ $cat_list .= '<option value="'. $cat['categoryid'] .'"'. (in_array($cat['categoryid'], $selected) ? ' selected="selected"' : '') .'>'. str_repeat(' ', $cat['depth']) . $cat['category_name'] .'</option>' . "\n";
+ }
+ }
+ $cat_list .= '</select>' . $n;
+
+ if (!empty($serendipity['GET']['title'])) {
+ $entry['title'] = utf8_decode(urldecode($serendipity['GET']['title']));
+ }
+
+ if (!empty($serendipity['GET']['body'])) {
+ $entry['body'] = utf8_decode(urldecode($serendipity['GET']['body']));
+ }
+
+ if (!empty($serendipity['GET']['url'])) {
+ $entry['body'] .= "\n" . '<br /><a href="' . htmlspecialchars(utf8_decode(urldecode($serendipity['GET']['url']))) . '">' . $entry['title'] . '</a>';
+ }
+
+ $hidden = '';
+ foreach($hiddens as $key => $value) {
+ $hidden .= ' <input type="hidden" name="' . $key . '" value="' . $value . '" />' . $n;
+ }
+ $hidden .= ' <input type="hidden" id="entryid" name="serendipity[id]" value="' . (isset($entry['id']) ? $entry['id'] : '') . '" />' . $n;
+ $hidden .= ' <input type="hidden" name="serendipity[timestamp]" value="' . (isset($entry['timestamp']) ? serendipity_serverOffsetHour($entry['timestamp']) : serendipity_serverOffsetHour(time())) . '" />' . $n;
+ $hidden .= ' <input type="hidden" name="serendipity[preview]" value="false" />';
+ $hidden .= ' ' . serendipity_setFormToken();
+ if (!empty($errMsg)) {
+?>
+ <div class="serendipityAdminMsgError"><?php echo $errMsg; ?></div>
+<?php } ?>
+ <form <?php echo $entry['entry_form']; ?> action="<?php echo $targetURL; ?>" method="post" <?php echo ($serendipity['XHTML11'] ? 'id' : 'name'); ?>="serendipityEntry" style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px">
+ <?php echo $hidden; ?>
+
+ <table class="serendipityEntryEdit" border="0" width="100%">
+ <tr>
+ <td>
+ <b><?php echo TITLE; ?>:</b>
+ </td>
+ <td colspan="2">
+ <table width="100%" cellspacing="0" cellpadding="0" border="0">
+ <tr>
+ <td><input type="text" id="entryTitle" name="serendipity[title]" value="<?php echo isset($entry['title']) ? htmlspecialchars($entry['title']) : ''; ?>" size="60" /></td>
+ <td align="right">
+ <select name="serendipity[isdraft]">
+ <?php if ($_SESSION['serendipityRightPublish']) { ?><option value="false" <?php echo $draftP; ?>><?php echo PUBLISH; ?></option><?php } ?>
+ <option value="true" <?php echo $draftD; ?>><?php echo DRAFT; ?></option>
+ </select>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+<?php
+ if (isset($serendipity['allowDateManipulation']) && $serendipity['allowDateManipulation']) {
+?>
+ <td>
+ <b><?php echo DATE; ?>:</b>
+ </td>
+ <td>
+ <input type="hidden" name="serendipity[chk_timestamp]" value="<?php echo serendipity_serverOffsetHour(isset($entry['timestamp']) && $entry['timestamp'] > 0 ? $entry['timestamp'] : time()); ?>" />
+ <input type="text" name="serendipity[new_timestamp]" id="serendipityNewTimestamp" value="<?php echo date(DATE_FORMAT_2, serendipity_serverOffsetHour(isset($entry['timestamp']) && $entry['timestamp'] > 0 ? $entry['timestamp'] : time())); ?>" />
+ <a href="#" onclick="document.getElementById('serendipityNewTimestamp').value = '<?php echo date(DATE_FORMAT_2, serendipity_serverOffsetHour(time())) ?>'; return false;" title="<?php echo RESET_DATE_DESC ?>"><img src="<?php echo serendipity_getTemplateFile('admin/img/clock.png') ?>" border="0" style="vertical-align: text-top;" alt="<?php echo RESET_DATE ?>" /></a>
+ </td>
+ <td align="right">
+<?php
+ } else {
+?>
+ <td align="right" colspan="3">
+<?php
+ }
+?>
+ <a style="border:0; text-decoration: none" href="#" onclick="showItem('categoryselector'); return false" title="<?php echo TOGGLE_OPTION; ?>"><img src="<?php echo serendipity_getTemplateFile('img/plus.png') ?>" id="option_categoryselector" style="border: 20px" alt="" border="0" /></a> <b><?php echo CATEGORY; ?>:</b> <?php echo $cat_list ; ?>
+ <script type="text/javascript" language="JavaScript">
+
+ function toggle_extended(setCookie) {
+ var textarea = document.getElementById('serendipity[extended]');
+ var button = document.getElementById('option_extended');
+ var tools = document.getElementById('tools_extended');
+ if ( textarea.style.display == 'none' ) {
+ textarea.style.display = '';
+ tools.style.display = '';
+ button.src = '<?php echo serendipity_getTemplateFile('img/minus.png') ?>';
+ if (setCookie == true) {
+ document.cookie = 'serendipity[toggle_extended]=true;';
+ }
+ } else {
+ textarea.style.display = 'none';
+ tools.style.display = 'none';
+ button.src = '<?php echo serendipity_getTemplateFile('img/plus.png') ?>';
+ if (setCookie == true) {
+ document.cookie = 'serendipity[toggle_extended]=;';
+ }
+ }
+ }
+
+ var selector_toggle = new Array();
+ var selector_store = new Array();
+ var selector_restore = new Array();
+
+ function showItem(id) {
+ var selected = 0;
+ if (typeof(id) == 'undefined' || typeof(id) == 'object') {
+ id = 'categoryselector';
+ }
+
+ if (document.getElementById) {
+ el = document.getElementById(id);
+ if (selector_toggle[id] && selector_toggle[id] == 'off') {
+ selector_restore[id] = new Array();
+ selector_toggle[id] = 'on';
+
+ /* Hack to make sure that when the single dropdown is shown, don't have multiple selections */
+ last = 0;
+
+ for (i=0; i < el.options.length; i++) {
+ if (el.options[i].selected == true) {
+ selected++;
+ last = i;
+ selector_restore[id][last] = 'on';
+ }
+
+ if (selected > 1) {
+ /* If there is more than one selected, we reset all those to false
+ This is because otherwise the label will say 'No Category', but the categories will still be selected */
+ for (j=0; j < el.options.length; j++) {
+ /* Save selection in array to later restore them */
+ if (el.options[j].selected == true) {
+ el.options[j].selected = false;
+ selector_restore[id][j] = 'on';
+ last = j;
+ } else {
+ selector_restore[id][j] = false;
+ }
+ }
+ break;
+ }
+ }
+
+ el.selectedIndex = null;
+ if (last > 0) {
+ el.selectedIndex = last;
+ }
+
+ el.size = 1;
+
+ /* Show a normal dropdown */
+ if (el.multiple) {
+ el.multiple = false;
+ }
+
+ document.getElementById('option_' + id).src = '<?php echo serendipity_getTemplateFile('img/plus.png') ?>';
+ } else {
+ selector_store[id] = el.size;
+ if (selector_store[id] == 0) {
+ selector_store[id] = 5;
+ }
+
+ last = 0;
+ if (el.selectedIndex > 0) {
+ if (!selector_restore[id]) {
+ selector_restore[id] = new Array();
+ }
+
+ for (j=0; j < el.options.length; j++) {
+ /* Save selection in array to later restore them */
+ if (el.options[j].selected == true) {
+ selector_restore[id][j] = 'on';
+ last = j;
+ }
+ }
+ }
+ el.selectedIndex = -1;
+ el.size = <?php echo count($cats)+1; ?>;
+ selector_toggle[id] = 'off';
+
+ /* Show multiple items */
+ el.multiple = true;
+
+ /* Restore previously selected items? */
+ last = 0;
+ for (i = 0; i < el.options.length; i++) {
+ if (selector_restore && selector_restore[id] && selector_restore[id][i] && selector_restore[id][i] == 'on') {
+ val = el.options[i].value;
+ if (el.options[i].selected != true) {
+ el.options[i].selected = true;
+ last = i;
+ // [TODO] IE Bug: Don't ask me why, but this restoring only works in Internet Explorer if you put this:
+ // alert('it doesnt matter what, just the alert is important');
+ }
+ }
+ }
+
+ document.getElementById('option_' + id).src = '<?php echo serendipity_getTemplateFile('img/minus.png') ?>';
+ }
+ }
+ }
+
+ function checkSave() {
+<?php
+ $void = null;
+ serendipity_plugin_api::hook_event('backend_entry_checkSave', $void);
+?>
+ return true;
+ }
+
+ selector_toggle['categoryselector'] = '<?php echo ($categoryselector_expanded ? 'on' : 'off'); ?>';
+ addLoadEvent(showItem);
+ </script>
+ </td>
+ </tr>
+ <tr>
+<?php
+ if (!$serendipity['wysiwyg']) {
+?>
+ <td colspan="2"><b><?php echo ENTRY_BODY; ?></b></td>
+ <td align="right">
+<?php
+ /* Since the user has WYSIWYG editor disabled, we want to check if we should use the "better" non-WYSIWYG editor */
+ if (!$serendipity['wysiwyg'] && eregi($serendipity['EditorBrowsers'], $_SERVER['HTTP_USER_AGENT']) ) {
+?>
+ <script type="text/javascript" language="JavaScript">
+ document.write('<input type="button" class="serendipityPrettyButton" name="insI" value="I" accesskey="i" style="font-style: italic" onclick="wrapSelection(document.forms[\'serendipityEntry\'][\'serendipity[body]\'],\'<em>\',\'</em>\')" />');
+ document.write('<input type="button" class="serendipityPrettyButton" name="insB" value="B" accesskey="b" style="font-weight: bold" onclick="wrapSelection(document.forms[\'serendipityEntry\'][\'serendipity[body]\'],\'<strong>\',\'</strong>\')" />');
+ document.write('<input type="button" class="serendipityPrettyButton" name="insU" value="U" accesskey="u" style="text-decoration: underline;" onclick="wrapSelection(document.forms[\'serendipityEntry\'][\'serendipity[body]\'],\'<u>\',\'</u>\')" />');
+ document.write('<input type="button" class="serendipityPrettyButton" name="insQ" value="<?php echo QUOTE ?>" accesskey="q" style="font-style: italic" onclick="wrapSelection(document.forms[\'serendipityEntry\'][\'serendipity[body]\'],\'<blockquote>\',\'</blockquote>\')" />');
+ document.write('<input type="button" class="serendipityPrettyButton" name="insJ" value="img" accesskey="j" onclick="wrapInsImage(document.forms[\'serendipityEntry\'][\'serendipity[body]\'])" />');
+ document.write('<input type="button" class="serendipityPrettyButton" name="insImage" value="<?php echo MEDIA; ?>" style="" onclick="window.open(\'serendipity_admin_image_selector.php?serendipity[textarea]=body\', \'ImageSel\', \'width=800,height=600,toolbar=no,scrollbars=1,scrollbars,resize=1,resizable=1\');" />');
+ document.write('<input type="button" class="serendipityPrettyButton" name="insURL" value="URL" accesskey="l" onclick="wrapSelectionWithLink(document.forms[\'serendipityEntry\'][\'serendipity[body]\'])" />');
+ </script>
+<?php
+ /* Do the "old" non-WYSIWYG editor */
+ } elseif (!$serendipity['wysiwyg']) { ?>
+ <script type="text/javascript" language="JavaScript">
+ document.write('<input type="button" class="serendipityPrettyButton" value=" B " onclick="serendipity_insBasic(document.forms[\'serendipityEntry\'][\'serendipity[body]\'], \'b\')">');
+ document.write('<input type="button" class="serendipityPrettyButton" value=" U " onclick="serendipity_insBasic(document.forms[\'serendipityEntry\'][\'serendipity[body]\'], \'u\')">');
+ document.write('<input type="button" class="serendipityPrettyButton" value=" I " onclick="serendipity_insBasic(document.forms[\'serendipityEntry\'][\'serendipity[body]\'], \'i\')">');
+ document.write('<input type="button" class="serendipityPrettyButton" value="<img>" onclick="serendipity_insImage(document.forms[\'serendipityEntry\'][\'serendipity[body]\'])">');
+ document.write('<input type="button" class="serendipityPrettyButton" value="<?php echo MEDIA; ?>" onclick="window.open(\'serendipity_admin_image_selector.php?serendipity[textarea]=body\', \'ImageSel\', \'width=800,height=600,toolbar=no\');">');
+ document.write('<input type="button" class="serendipityPrettyButton" value="Link" onclick="serendipity_insLink(document.forms[\'serendipityEntry\'][\'serendipity[body]\'])">');
+ </script>
+<?php }
+
+ serendipity_plugin_api::hook_event('backend_entry_toolbar_body', $entry);
+ } else {
+?>
+ <td colspan="2"><b><?php echo ENTRY_BODY; ?></b></td>
+ <td><?php serendipity_plugin_api::hook_event('backend_entry_toolbar_body', $entry); ?>
+
+<?php } ?>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="3">
+ <textarea style="width: 100%" name="serendipity[body]" id="serendipity[body]" cols="80" rows="20"><?php echo isset($entry['body']) ? htmlspecialchars($entry['body']) : ''; ?></textarea>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="3">
+ <table width="100%" cellpadding="0" cellspacing="0">
+ <tr>
+ <td align="left" width="70%">
+ <input id="checkbox_allow_comments" type="checkbox" name="serendipity[allow_comments]" value="true" <?php echo $allow_comments; ?> /><label for="checkbox_allow_comments"><?php echo COMMENTS_ENABLE; ?></label><br />
+ <input id="checkbox_moderate_comments" type="checkbox" name="serendipity[moderate_comments]" value="true" <?php echo $moderate_comments; ?> /><label for="checkbox_moderate_comments"><?php echo COMMENTS_MODERATE; ?></label>
+ </td>
+ <td align="right" rowspan="2" valign="middle" width="30%">
+ <input accesskey="p" type="submit" value="- <?php echo PREVIEW; ?> -" class="serendipityPrettyButton" style="width: 150px" onclick="document.forms['serendipityEntry'].elements['serendipity[preview]'].value='true';" /><br />
+ <input accesskey="s" type="submit" onclick="return checkSave();" value="- <?php echo SAVE; ?> -" class="serendipityPrettyButton" style="width: 150px" />
+ </td>
+ </tr>
+ </table>
+ <br />
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2">
+<?php if (!$serendipity['wysiwyg']) { ?>
+ <a style="border:0; text-decoration: none" href="#" onclick="toggle_extended(true); return false;" title="<?php echo TOGGLE_OPTION; ?>"><img src="<?php echo serendipity_getTemplateFile('img/plus.png') ?>" id="option_extended" alt="+/-" border="0" /></a>
+<?php } ?> <b><?php echo EXTENDED_BODY; ?></b></td>
+ <td align="right">
+ <?php
+if (!$serendipity['wysiwyg']) {
+?>
+ <div id="tools_extended" style="display: none">
+<?php
+ /* Since the user has WYSIWYG editor disabled, we want to check if we should use the "better" non-WYSIWYG editor */
+ if (eregi($serendipity['EditorBrowsers'], $_SERVER['HTTP_USER_AGENT']) ) {
+?>
+ <input type="button" class="serendipityPrettyButton" name="insI" value="I" accesskey="i" style="font-style: italic" onclick="wrapSelection(document.forms['serendipityEntry']['serendipity[extended]'],'<em>','</em>')" />
+ <input type="button" class="serendipityPrettyButton" name="insB" value="B" accesskey="b" style="font-weight: bold" onclick="wrapSelection(document.forms['serendipityEntry']['serendipity[extended]'],'<strong>','</strong>')" />
+ <input type="button" class="serendipityPrettyButton" name="insU" value="U" accesskey="u" style="text-decoration: underline;" onclick="wrapSelection(document.forms['serendipityEntry']['serendipity[extended]'],'<u>','</u>')" />
+ <input type="button" class="serendipityPrettyButton" name="insQ" value="<?php echo QUOTE ?>" accesskey="q" style="font-style: italic" onclick="wrapSelection(document.forms['serendipityEntry']['serendipity[extended]'],'<blockquote>','</blockquote>')" />
+ <input type="button" class="serendipityPrettyButton" name="insJ" value="img" accesskey="j" onclick="wrapInsImage(document.forms['serendipityEntry']['serendipity[extended]'])" />
+ <input type="button" class="serendipityPrettyButton" name="insImage" value="<?php echo MEDIA; ?>" onclick="window.open('serendipity_admin_image_selector.php?serendipity[textarea]=extended', 'ImageSel', 'width=800,height=600,toolbar=no,scrollbars=1,scrollbars,resize=1,resizable=1');" />
+ <input type="button" class="serendipityPrettyButton" name="insURL" value="URL" accesskey="l" onclick="wrapSelectionWithLink(document.forms['serendipityEntry']['serendipity[extended]'])" />
+<?php
+ /* Do the "old" non-WYSIWYG editor */
+ } else { ?>
+ <input type="button" class="serendipityPrettyButton" value=" B " onclick="serendipity_insBasic(document.forms['serendipityEntry']['serendipity[extended]'], 'b')">
+ <input type="button" class="serendipityPrettyButton" value=" U " onclick="serendipity_insBasic(document.forms['serendipityEntry']['serendipity[extended]'], 'u')">
+ <input type="button" class="serendipityPrettyButton" value=" I " onclick="serendipity_insBasic(document.forms['serendipityEntry']['serendipity[extended]'], 'i')">
+ <input type="button" class="serendipityPrettyButton" value="<img>" onclick="serendipity_insImage(document.forms['serendipityEntry']['serendipity[extended]'])">
+ <input type="button" class="serendipityPrettyButton" value="<?php echo MEDIA; ?>" onclick="window.open('serendipity_admin_image_selector.php?serendipity[textarea]=extended', 'ImageSel', 'width=800,height=600,toolbar=no');">
+ <input type="button" class="serendipityPrettyButton" value="Link" onclick="serendipity_insLink(document.forms['serendipityEntry']['serendipity[extended]'])">
+<?php
+ }
+
+ serendipity_plugin_api::hook_event('backend_entry_toolbar_extended', $entry);
+?>
+ </div>
+<?php } else {
+ serendipity_plugin_api::hook_event('backend_entry_toolbar_extended', $entry);
+} ?>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="3">
+ <textarea style="width: 100%;" name="serendipity[extended]" id="serendipity[extended]" cols="80" rows="20"><?php echo isset($entry['extended']) ? htmlspecialchars($entry['extended']) : ''; ?></textarea>
+<?php if (!$serendipity['wysiwyg']) { ?>
+ <script type="text/javascript" language="JavaScript">
+ toggle_extended();
+ </script>
+<?php } ?>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="3">
+ <br />
+ <fieldset>
+ <legend><b><?php echo ADVANCED_OPTIONS; ?></b></legend>
+<?php
+ serendipity_plugin_api::hook_event('backend_display', $entry);
+?>
+ </fieldset>
+ </td>
+ </tr>
+ </table>
+ </form>
+<?php
+ if ((!empty($entry['extended']) || !empty($serendipity['COOKIE']['toggle_extended'])) && !$serendipity['wysiwyg']) {
+?>
+ <script type="text/javascript" language="JavaScript">
+ toggle_extended();
+ </script>
+<?php } ?>
+<?php
+ if ($serendipity['wysiwyg']) {
+ $fields = array(
+ 'body' => 'serendipity[body]',
+ 'extended' => 'serendipity[extended]'
+ );
+
+ foreach($fields AS $f_jsname => $f_item) {
+ serendipity_emit_htmlarea_code($f_item, $f_jsname);
+ }
+ serendipity_plugin_api::hook_event('backend_wysiwyg_finish', $fields);
+ }
+
+ echo ' <script type="text/javascript" language="JavaScript" src="serendipity_define.js.php"></script>';
+ echo ' <script type="text/javascript" language="JavaScript" src="serendipity_editor.js"></script>';
+}
+
+function serendipity_emit_htmlarea_code($item, $jsname, $spawnMulti = false) {
+ static $init = false;
+ global $serendipity;
+
+ if ($init && $spawnMulti) {
+ return true;
+ }
+
+ if (isset($serendipity['wysiwyg']) && $serendipity['wysiwyg']) {
+
+ $eventData = array(
+ 'init' => &$init,
+ 'item' => &$item,
+ 'jsname' => &$jsname,
+ 'skip' => false
+ );
+ serendipity_plugin_api::hook_event('backend_wysiwyg', $eventData);
+
+ if ($eventData['skip']) {
+ return true;
+ }
+
+ if (!$init) {
+?>
+ <script type="text/javascript">
+ _editor_url = "<?php echo $serendipity['serendipityHTTPPath'] . 'htmlarea/'; ?>";
+ _editor_lang = "<?php echo WYSIWYG_LANG; ?>";
+ var editorref = '';
+ </script>
+ <script type="text/javascript" src="htmlarea/htmlarea.js"></script>
+ <script type="text/javascript" src="htmlarea/lang/<?php echo WYSIWYG_LANG; ?>.js"></script>
+ <script type="text/javascript" src="htmlarea/dialog.js"></script>
+ <style type="text/css">@import url(htmlarea/htmlarea.css);</style>
+<?php
+ }
+
+ $csscode = str_replace(
+ array(
+ "\n",
+ "'",
+ "\r",
+ "{LANG_DIRECTION}"
+ ),
+
+ array(
+ '\n',
+ "\'",
+ "",
+ (defined('LANG_DIRECTION') ? LANG_DIRECTION : 'ltr')
+ ),
+
+ file_get_contents(serendipity_getTemplateFile('htmlarea.css', 'serendipityPath'))
+ );
+?>
+ <script type="text/javascript">
+ // IF you want to enable HTMLArea's spellchecker, download the SpellChecker plugin from the HTMLArea homepage
+ // (http://www.sourceforge.net/projects/itools-htmlarea) and uncomment the lines suffixed with ' // [SPELLCHECK]'
+ // Note that the SpellChecker is a CGI-based application which needs setup in your Apache host ("Options +CGIExec")
+ // Thanks to Randall for pointing this out!
+
+ // HTMLArea.loadPlugin("SpellChecker"); // [SPELLCHECK]
+ <?php if($spawnMulti) { ?>
+ // when spawning multiple editors at once, keep track of instances in this array
+ var htmlarea_editors = new Array();
+ <?php } else { ?>
+ var editor<?php echo $jsname; ?> = null; var config<?php echo $jsname; ?> = null;
+ <?php } // end if ?>
+ function Spawn<?php echo $jsname; ?>(<?php echo $spawnMulti ? 'id' : ''; ?>) {
+ editor<?php echo $jsname; ?> = new HTMLArea("<?php echo $item; ?>"<?php echo $spawnMulti ? ' + id' : ''; ?>);
+ <?php if($spawnMulti) { ?>
+ htmlarea_editors["<?php echo $item; ?>"<?php echo $spawnMulti ? ' + id' : ''; ?>] = editor<?php echo $jsname; ?>;
+ <?php } // end if ?>
+ config<?php echo $jsname; ?> = editor<?php echo $jsname; ?>.config;
+ config<?php echo $jsname; ?>.registerButton('image_selector', '<?PHP echo MANAGE_IMAGES; ?>', '<?php echo $serendipity['serendipityHTTPPath']; ?>htmlarea/images/ed_s9yimage.gif', false,
+ function(editor, id) {
+ window.open('<?php echo $serendipity['serendipityHTTPPath']; ?>serendipity_admin_image_selector.php?serendipity[textarea]=<?php echo $jsname . ($spawnMulti ? "' + editor._textArea.id + '" : ''); ?>', 'ImageSel', 'width=800,height=600,toolbar=no,scrollbars=1,scrollbars,resize=1,resizable=1');
+ editorref = editor<?php echo $jsname; ?>;
+ }
+ );
+ config<?php echo $jsname; ?>.toolbar.push([ "image_selector"]);
+ config<?php echo $jsname; ?>.cssFile = '<?php echo $csscode; ?>';
+ config<?php echo $jsname; ?>.toolbar = [
+ [ "fontname", "space",
+ "fontsize", "space",
+ "formatblock", "space",
+ "bold", "italic", "underline", "strikethrough", "separator",
+ "subscript", "superscript", "separator",
+ "copy", "cut", "paste", "space", "undo", "redo" ],
+
+ [ "justifyleft", "justifycenter", "justifyright", "justifyfull", "separator",
+ "lefttoright", "righttoleft", "separator",
+ "orderedlist", "unorderedlist", "outdent", "indent", "separator",
+ "forecolor", "hilitecolor", "separator",
+ "inserthorizontalrule", "createlink", "insertimage", "image_selector", "inserttable", "htmlmode", "separator",
+ "popupeditor", "separator", "showhelp", "about" ]
+ ];
+ // editor<?php echo $jsname; ?>.registerPlugin(SpellChecker); // [SPELLCHECK]
+ editor<?php echo $jsname; ?>.generate();
+ editor<?php echo $jsname; ?>._textArea.className = 'serendipity_entry';
+ }
+ </script>
+<?php
+ }
+
+ $init = true;
+}
--- /dev/null
+<!-- // Hide from older browsers
+/* $Id$ */
+/*
+# Copyright (c) 2003-2005, Jannis Hermanns (on behalf the Serendipity Developer Team)
+# All rights reserved. See LICENSE file for licensing details
+*/
+
+/*
+ Written by chris wetherell
+ http://www.massless.org
+ chris [THE AT SIGN] massless.org
+
+ warning: it only works for IE4+/Win and Moz1.1+
+ feel free to take it for your site
+ if there are any problems, let chris know.
+*/
+
+var thisForm;
+
+function getMozSelection(txtarea) {
+ var selLength = txtarea.textLength;
+ var selStart = txtarea.selectionStart;
+ var selEnd = txtarea.selectionEnd;
+
+ if (selEnd==1 || selEnd==2) {
+ selEnd=selLength;
+ }
+ return (txtarea.value).substring(selStart, selEnd);
+}
+
+function getIESelection(txtarea) {
+ return document.selection.createRange().text;
+}
+
+function mozWrap(txtarea, lft, rgt) {
+ var selLength = txtarea.textLength;
+ var selStart = txtarea.selectionStart;
+ var selEnd = txtarea.selectionEnd;
+
+ if (txtarea.setSelectionRange) {
+ if (selEnd==1 || selEnd==2) selEnd=selLength;
+ var s1 = (txtarea.value).substring(0,selStart);
+ var s2 = (txtarea.value).substring(selStart, selEnd)
+ var s3 = (txtarea.value).substring(selEnd, selLength);
+ txtarea.value = s1 + lft + s2 + rgt + s3;
+ } else {
+ txtarea.value = txtarea.value + ' ' + lft + rgt + ' ';
+ }
+}
+
+function IEWrap(txtarea, lft, rgt) {
+ strSelection = document.selection.createRange().text;
+ if (strSelection != "") {
+ document.selection.createRange().text = lft + strSelection + rgt;
+ } else {
+ txtarea.value = txtarea.value + lft + rgt;
+ }
+}
+
+function wrapSelection(txtarea, lft, rgt) {
+ if (document.all) {
+ IEWrap(txtarea, lft, rgt);
+ } else if (document.getElementById) {
+ mozWrap(txtarea, lft, rgt);
+ }
+}
+
+function wrapSelectionWithLink(txtarea) {
+ var my_link = prompt("Enter URL:","http://");
+
+ if (document.all && getIESelection(txtarea) == "" ||
+ document.getElementById && getMozSelection(txtarea) == "") {
+ var my_desc = prompt("Enter Description", '');
+ }
+
+ var my_title = prompt("Enter title/tooltip:", "");
+
+ html_title = "";
+ if (my_title != "") {
+ html_title = ' title="' + my_title + '"';
+ }
+
+ if (my_link != null) {
+ lft = "<a href=\"" + my_link + "\" " + html_title + ">";
+ if (my_desc != null && my_desc != "") {
+ rgt = my_desc + "</a>";
+ } else {
+ rgt = "</a>";
+ }
+ wrapSelection(txtarea, lft, rgt);
+ }
+
+ return;
+}
+/* end chris w. script */
+
+function mozInsert(txtarea, str) {
+ var selLength = txtarea.textLength;
+ var selStart = txtarea.selectionStart;
+ var selEnd = txtarea.selectionEnd;
+ if (selEnd==1 || selEnd==2) {
+ selEnd=selLength;
+ }
+ var s1 = (txtarea.value).substring(0,selStart);
+ var s2 = (txtarea.value).substring(selStart, selEnd)
+ var s3 = (txtarea.value).substring(selEnd, selLength);
+ txtarea.value = s1 + str + s2 + s3;
+}
+
+function wrapInsImage(area) {
+ var loc = prompt('Enter the Image Location: ');
+ if (!loc) {
+ return;
+ }
+ mozInsert(area,'<img src="'+ loc + '" alt="" />');
+}
+
+/* end Better-Editor functions */
+
+function serendipity_insImage (area) {
+ var loc = prompt('Enter the Image Location: ');
+ if (!loc) {
+ area.focus();
+ return;
+ }
+
+ area.value = area.value + '<img src="' + loc + '" alt="" />';
+ area.focus();
+}
+
+function serendipity_insBasic (area, tag) {
+ area.value = area.value + "<" + tag + "></" + tag + ">";
+ area.focus();
+}
+
+function serendipity_insLink (area) {
+ var loc = prompt('Enter URL Location: ');
+ var text = prompt('Enter Description: ');
+ var my_title = prompt("Enter title/tooltip:", "");
+
+ if (!loc) {
+ area.focus();
+ return;
+ }
+
+ html_title = "";
+ if (my_title != "") {
+ html_title = ' title="' + my_title + '"';
+ }
+
+ area.value = area.value + '<a href="' + loc + '" ' + html_title + '>' + (text ? text : loc) + '</a>';
+ area.focus();
+}
+
+function serendipity_imageSelector_addToElement (str, el)
+{
+ document.getElementById(el).value = str;
+ document.getElementById(el).focus();
+ if (document.getElementById(el).onchange) {
+ document.getElementById(el).onchange();
+ }
+}
+
+function serendipity_imageSelector_addToBody (str, textarea)
+{
+ eltarget = '';
+ if (document.forms['serendipityEntry'] && document.forms['serendipityEntry']['serendipity['+ textarea +']']) {
+ eltarget = document.forms['serendipityEntry']['serendipity['+ textarea +']']
+ } else if (document.forms['serendipityEntry'] && document.forms['serendipityEntry'][textarea]) {
+ eltarget = document.forms['serendipityEntry'][textarea];
+ } else {
+ eltarget = document.forms[0].elements[0];
+ }
+
+ wrapSelection(eltarget, str, '');
+ eltarget.focus();
+}
+
+function serendipity_imageSelector_done(textarea)
+{
+ var insert = '';
+ var img = '';
+ var src = '';
+ var f = document.forms['serendipity[selForm]'].elements;
+
+ if (f['serendipity[linkThumbnail]'][0].checked == true) {
+ img = f['thumbName'].value;
+ imgWidth = f['imgThumbWidth'].value;
+ imgHeight = f['imgThumbHeight'].value;
+ } else {
+ img = f['imgName'].value;
+ imgWidth = f['imgWidth'].value;
+ imgHeight = f['imgHeight'].value;
+ }
+
+ if (f['serendipity[filename_only]'] && f['serendipity[filename_only]'].value == 'true') {
+ self.opener.serendipity_imageSelector_addToElement(img, f['serendipity[htmltarget]'].value);
+ self.close();
+ return true;
+ }
+
+ if (document.getElementById('serendipity_imagecomment').value != '') {
+ styled = false;
+ } else {
+ styled = true;
+ }
+
+ floating = 'center';
+ if (XHTML11) {
+ if (f['serendipity[align]'][0].checked == true) {
+ img = "<img width='" + imgWidth + "' height='" + imgHeight + "' " + (styled ? 'style="border: 0px; padding-left: 5px; padding-right: 5px;"' : '') + ' src="' + img + "\" alt=\"\" />";
+ } else if (f['serendipity[align]'][1].checked == true) {
+ img = "<img width='" + imgWidth + "' height='" + imgHeight + "' " + (styled ? 'style="float: left; border: 0px; padding-left: 5px; padding-right: 5px;"' : '') + ' src="' + img + "\" alt=\"\" />";
+ floating = 'left';
+ } else if (f['serendipity[align]'][2].checked == true) {
+ img = "<img width='" + imgWidth + "' height='" + imgHeight + "' " + (styled ? 'style="float: right; border: 0px; padding-left: 5px; padding-right: 5px;"' : '') + ' src="' + img + "\" alt=\"\" />";
+ floating = 'right';
+ }
+ } else {
+ if (f['serendipity[align]'][0].checked == true) {
+ img = "<img width='" + imgWidth + "' height='" + imgHeight + "' border='0' hspace='5' src='" + img + "' alt='' />";
+ } else if (f['serendipity[align]'][1].checked == true) {
+ img = "<img width='" + imgWidth + "' height='" + imgHeight + "' border='0' hspace='5' align='left' src='" + img + "' alt='' />";
+ floating = 'left';
+ } else if (f['serendipity[align]'][2].checked == true) {
+ img = "<img width='" + imgWidth + "' height='" + imgHeight + "' border='0' hspace='5' align='right' src='" + img + "' alt='' />";
+ floating = 'right';
+ }
+ }
+
+ if (f['serendipity[isLink]'][1].checked == true) {
+ insert = "<a class='serendipity_image_link' href='" + f['serendipity[url]'].value + "'>" + img + "</a>";
+ } else {
+ insert = img;
+ }
+
+ if (document.getElementById('serendipity_imagecomment').value != '') {
+ comment = f['serendipity[imagecomment]'].value;
+ block = '<div class="serendipity_imageComment_' + floating + '" style="width: ' + imgWidth + 'px">'
+ + '<div class="serendipity_imageComment_img">' + insert + '</div>'
+ + '<div class="serendipity_imageComment_txt">' + comment + '</div>'
+ + '</div>';
+ } else {
+ block = insert;
+ }
+
+ if (block == 'x' && typeof(self.opener.htmlarea_editors) != 'undefined' && typeof(self.opener.htmlarea_editors[textarea] != 'undefined')) {
+ self.opener.htmlarea_editors[textarea].surroundHTML(block, '');
+ } else if (parent.self.opener.editorref) {
+ parent.self.opener.editorref.surroundHTML(block, '');
+ } else {
+ parent.self.opener.serendipity_imageSelector_addToBody(block, textarea);
+ }
+
+ parent.self.close();
+}
+
+// -->