static function addEntryProperties($entryId, $supported_properties, &$properties, $deleteMissing = true)
 {
     global $serendipity;
     // Get existing data
     $property = serendipity_fetchEntryProperties($entryId);
     foreach ($supported_properties as $prop_key) {
         $prop_val = isset($properties[$prop_key]) ? $properties[$prop_key] : null;
         if (!$deleteMissing && empty($prop_val)) {
             continue;
         }
         // Don't clear data if not allowed.
         $q = '';
         if (!isset($property[$prop_key]) && !empty($prop_val)) {
             if ($prop_val != '#') {
                 $q = "INSERT INTO {$serendipity['dbPrefix']}entryproperties (entryid, property, value) VALUES (" . (int) $entryId . ", '" . serendipity_db_escape_string($prop_key) . "', '" . serendipity_db_escape_string($prop_val) . "')";
             }
         } elseif ($property[$prop_key] != $prop_val && !empty($prop_val)) {
             if ($prop_val == '#') {
                 $q = "DELETE FROM {$serendipity['dbPrefix']}entryproperties WHERE entryid = " . (int) $entryId . " AND property = '" . serendipity_db_escape_string($prop_key) . "'";
             } else {
                 $q = "UPDATE {$serendipity['dbPrefix']}entryproperties SET value = '" . serendipity_db_escape_string($prop_val) . "' WHERE entryid = " . (int) $entryId . " AND property = '" . serendipity_db_escape_string($prop_key) . "'";
             }
         } elseif (empty($property[$prop_key])) {
             $q = "DELETE FROM {$serendipity['dbPrefix']}entryproperties WHERE entryid = " . (int) $entryId . " AND property = '" . serendipity_db_escape_string($prop_key) . "'";
         }
         if (!empty($q)) {
             serendipity_db_query($q);
         }
     }
 }
 function addProperties(&$properties, &$eventData)
 {
     global $serendipity;
     // Get existing data
     $property = serendipity_fetchEntryProperties($eventData['id']);
     $supported_properties =& $this->getSupportedProperties();
     foreach ($supported_properties as $prop_key => $_pkey) {
         if (!preg_match('@_(id|rating)$@', $prop_key)) {
             continue;
         }
         $prop_val = isset($properties[$prop_key]) ? $properties[$prop_key] : null;
         $prop_key = 'cr_' . $prop_key;
         if (is_array($prop_val)) {
             $prop_val = ";" . implode(';', $prop_val) . ";";
         }
         $q = "DELETE FROM {$serendipity['dbPrefix']}entryproperties WHERE entryid = " . (int) $eventData['id'] . " AND property = '" . serendipity_db_escape_string($prop_key) . "'";
         serendipity_db_query($q);
         if (!empty($prop_val)) {
             $q = "INSERT INTO {$serendipity['dbPrefix']}entryproperties (entryid, property, value) VALUES (" . (int) $eventData['id'] . ", '" . serendipity_db_escape_string($prop_key) . "', '" . serendipity_db_escape_string($prop_val) . "')";
             serendipity_db_query($q);
         }
     }
 }
 function addProperties(&$properties, &$eventData)
 {
     global $serendipity;
     // Get existing data
     $property = serendipity_fetchEntryProperties($eventData['id']);
     $supported_properties = serendipity_event_entryproperties::getSupportedProperties();
     // Cleanup properties first, if none disable_markups plugins were set, or a previous selected one was re-set
     if (is_array($serendipity['POST']['properties']) && !is_array($serendipity['POST']['properties']['disable_markups'])) {
         $q = "DELETE FROM {$serendipity['dbPrefix']}entryproperties WHERE entryid = " . (int) $eventData['id'] . " AND property LIKE 'ep_disable_markup_%'";
         serendipity_db_query($q);
     }
     // Special case for input type checkbox entryproperties
     $reset_properties = array('is_sticky', 'no_frontpage', 'hiderss');
     foreach ($reset_properties as $property) {
         if (!isset($serendipity['POST']['propertyform']) && is_array($serendipity['POST']['properties']) && !in_array($property, $serendipity['POST']['properties'])) {
             $q = "DELETE FROM {$serendipity['dbPrefix']}entryproperties WHERE entryid = " . (int) $eventData['id'] . " AND property = 'ep_{$property}'";
             serendipity_db_query($q);
         }
     }
     // Special case for disable markups.
     if (is_array($properties['disable_markups'])) {
         $q = "DELETE FROM {$serendipity['dbPrefix']}entryproperties WHERE entryid = " . (int) $eventData['id'] . " AND property LIKE 'ep_disable_markup_%'";
         serendipity_db_query($q);
         foreach ($properties['disable_markups'] as $idx => $instance) {
             $supported_properties[] = 'disable_markup_' . $instance;
         }
     }
     serendipity_plugin_api::hook_event('backend_entryproperties', $supported_properties);
     foreach ($supported_properties as $prop_key) {
         // Do not delete a property if it is not subbmitted to this function, because serendipity_updertEntry
         // possibly only wants to update entry metadata and left out any specific properties, which need to be kept.
         // An empty string like "" will properly remove an entryproperty, and POST values will always set an array index to an empty string.
         // $serendipipty['POST']['propertyform'] will be set whenever the entryeditor was properly displayed and unticked checkboxes shall remain.
         // (Not for checkboxes, but checkboxes are not used for entryproperties) - (Edit: Well, actually we do have some, see reset special case checkboxed properties above!)
         if (!isset($properties[$prop_key]) && !isset($serendipity['POST']['propertyform'])) {
             continue;
         }
         $prop_val = isset($properties[$prop_key]) ? $properties[$prop_key] : null;
         $prop_key = 'ep_' . $prop_key;
         if (is_array($prop_val)) {
             $prop_val = ";" . implode(';', $prop_val) . ";";
         }
         $q = "DELETE FROM {$serendipity['dbPrefix']}entryproperties WHERE entryid = " . (int) $eventData['id'] . " AND property = '" . serendipity_db_escape_string($prop_key) . "'";
         serendipity_db_query($q);
         if (!empty($prop_val)) {
             $q = "INSERT INTO {$serendipity['dbPrefix']}entryproperties (entryid, property, value) VALUES (" . (int) $eventData['id'] . ", '" . serendipity_db_escape_string($prop_key) . "', '" . serendipity_db_escape_string($prop_val) . "')";
             serendipity_db_query($q);
         }
     }
 }
/**
 * 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 <= " . serendipity_db_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,\n                            e.title,\n                            e.timestamp,\n                            e.body,\n                            e.comments,\n                            e.trackbacks,\n                            e.extended,\n                            e.exflag,\n                            e.authorid,\n                            e.isdraft,\n                            e.allow_comments,\n                            e.last_modified,\n                            e.moderate_comments,\n\n                            a.realname AS author,\n                            a.username AS loginname,\n                            a.email\n                      FROM\n                            {$serendipity['dbPrefix']}entries e\n                 LEFT JOIN  {$serendipity['dbPrefix']}authors a\n                        ON  e.authorid = a.authorid\n                            {$cond['joins']}\n                     WHERE\n                            e.{$key} " . ($key == 'id' ? '=' : 'LIKE') . " '" . serendipity_db_escape_string($val) . "'\n                            {$cond['and']}\n\n                            {$cond['single_group']}\n                            {$cond['single_having']}\n                            {$cond['single_orderby']}\n                     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']);
        $stack = array();
        $stack[0] =& $ret;
        $assoc_ids = array($ret['id'] => 0);
        serendipity_plugin_api::hook_event('frontend_entryproperties', $stack, $assoc_ids);
    }
    return $ret;
}
/**
 * This is not a XML RPC function. metaWeblog_getPost and metaWeblog_getRecentPosts produce exactly the same structure.
 * @param unknown_type $entry
 */
function metaWeblog_createPostRpcValue($entry)
{
    global $serendipity;
    $values = array('dateCreated' => new XML_RPC_Value(XML_RPC_iso8601_encode($entry['timestamp'], $serendipity['XMLRPC_GMT']) . ($serendipity['XMLRPC_GMT'] ? 'Z' : ''), 'dateTime.iso8601'), 'postid' => new XML_RPC_Value($entry['id'], 'string'), 'userid' => new XML_RPC_Value($entry['authorid'], 'string'), 'description' => new XML_RPC_Value($entry['body'], 'string'), 'mt_excerpt' => new XML_RPC_Value('', 'string'), 'mt_allow_comments' => new XML_RPC_Value($entry['allow_comments'] ? 1 : 0, 'int'), 'mt_text_more' => new XML_RPC_Value($entry['extended'], 'string'), 'mt_allow_pings' => new XML_RPC_Value($entry['allow_comments'] ? 1 : 0, 'int'), 'mt_convert_breaks' => new XML_RPC_Value('', 'string'), 'mt_keywords' => new XML_RPC_Value(isset($entry['mt_keywords']) ? $entry['mt_keywords'] : '', 'string'), 'title' => new XML_RPC_Value($entry['title'], 'string'), 'permaLink' => new XML_RPC_Value(serendipity_archiveURL($entry['id'], $entry['title'], 'baseURL', true, array('timestamp' => $entry['timestamp'])), 'string'), 'link' => new XML_RPC_Value(serendipity_archiveURL($entry['id'], $entry['title'], 'baseURL', true, array('timestamp' => $entry['timestamp'])), 'string'));
    // Add geo coords if given:
    if (isset($entry['properties'])) {
        if (isset($entry['properties']['geo_lat']) && isset($entry['properties']['geo_long'])) {
            $geo_lat = new XML_RPC_Value(array('key' => new XML_RPC_Value('geo_latitude', 'string'), 'value' => new XML_RPC_Value($entry['properties']['geo_lat'], 'double')), 'struct');
            $geo_long = new XML_RPC_Value(array('key' => new XML_RPC_Value('geo_longitude', 'string'), 'value' => new XML_RPC_Value($entry['properties']['geo_long'], 'double')), 'struct');
            $values['custom_fields'] = new XML_RPC_Value(array($geo_lat, $geo_long), 'array');
        }
    }
    // Add Categories. metaWeblog supports names only.
    if (isset($entry['categories'])) {
        $rpc_categories = array();
        foreach ($entry['categories'] as $category) {
            $rpc_categories[] = new XML_RPC_Value($category['category_name'], 'string');
        }
        $values['categories'] = new XML_RPC_Value($rpc_categories, 'array');
    }
    if (XMLRPC_WP_COMPATIBLE) {
        $values['wp_slug'] = new XML_RPC_Value(serendipity_archiveURL($entry['id'], $entry['title'], 'baseURL', true, array('timestamp' => $entry['timestamp'])), 'string');
        $values['wp_author_id'] = new XML_RPC_Value($entry['authorid'], 'string');
        $values['wp_author_display_name'] = new XML_RPC_Value($entry['author'], 'string');
        $values['wp_post_format'] = new XML_RPC_Value('standard', 'string');
        $draft = isset($entry['isdraft']) && serendipity_db_bool($entry['isdraft']);
        $values['post_status'] = new XML_RPC_Value($draft ? 'draft' : 'publish', 'string');
        $values['date_created_gmt'] = new XML_RPC_Value(XML_RPC_iso8601_encode($entry['timestamp'], 1) . 'Z', 'dateTime.iso8601');
        $modified = empty($entry['last_modified']) ? $entry['timestamp'] : $entry['last_modified'];
        $values['date_modified'] = new XML_RPC_Value(XML_RPC_iso8601_encode($modified, 1) . 'Z', 'dateTime.iso8601');
        $values['date_modified_gmt'] = new XML_RPC_Value(XML_RPC_iso8601_encode($modified, 1) . 'Z', 'dateTime.iso8601');
        // Extended Article Properties supports passwords.
        $entry_properties = serendipity_fetchEntryProperties($entry['id']);
        if (is_array($entry_properties)) {
            $values['wp_password'] = new XML_RPC_Value($entry_properties['ep_entrypassword'], 'string');
        } else {
            $values['wp_password'] = new XML_RPC_Value('', 'string');
        }
    }
    return new XML_RPC_Value($values, 'struct');
}
    /**
     * @param string $event
     * @param serendipity_property_bag $bag
     * @param mixed $eventData
     * @param mixed $addData
     * @return bool
     */
    function event_hook($event, &$bag, &$eventData, $addData = null)
    {
        global $serendipity;
        switch ($event) {
            case 'backend_publish':
            case 'backend_save':
                serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}entryproperties WHERE entryid = '" . $eventData['id'] . "' AND property LIKE 'ep_flattr%'");
                foreach ($this->flattr_attrs as $attr => $attr_desc) {
                    serendipity_db_query("INSERT INTO {$serendipity['dbPrefix']}entryproperties (entryid, value, property) VALUES ('" . $eventData['id'] . "', '" . serendipity_db_escape_string($serendipity['POST']['properties']['ep_' . $attr]) . "', 'ep_" . $attr . "')");
                }
                return true;
                break;
            case 'backend_display':
                ?>
                    <fieldset style="margin: 5px">
                        <legend><?php 
                echo PLUGIN_FLATTR_NAME;
                ?>
</legend>
<?php 
                foreach ($this->flattr_attrs as $attr => $attr_desc) {
                    if (isset($serendipity['POST']['properties']['ep_' . $attr])) {
                        $val = $serendipity['POST']['properties']['ep_' . $attr];
                    } elseif (isset($eventData['id'])) {
                        $val = $eventData['properties']['ep_' . $attr];
                    } else {
                        $val = '';
                    }
                    echo '<label for="serendipity[properties][ep_' . $attr . ']" title="' . PLUGIN_FLATTR_NAME . '">
                            ' . $attr_desc . ':</label><br/>';
                    if ($attr == 'flattr_active' || $attr == 'flattr_lng' || $attr == 'flattr_cat') {
                        echo '<select name="serendipity[properties][ep_' . $attr . ']" id="properties_' . $attr . '" class="input_select">';
                        if ($attr == 'flattr_lng') {
                            $opt = $this->flattr_langs;
                            echo '<option value=""></option>' . "\n";
                        } elseif ($attr == 'flattr_cat') {
                            $opt = $this->flattr_cats;
                            echo '<option value=""></option>' . "\n";
                        } elseif ($attr == 'flattr_active') {
                            $opt = array('1' => YES, '-1' => NO);
                        }
                        foreach ($opt as $key => $kval) {
                            echo '<option value="' . $key . '" ' . ((string) $val == (string) $key ? 'selected="selected"' : '') . '>' . (function_exists('serendipity_specialchars') ? serendipity_specialchars($kval) : htmlspecialchars($kval, ENT_COMPAT, LANG_CHARSET)) . '</option>' . "\n";
                        }
                        echo '</select>';
                    } else {
                        echo '<input type="text" name="serendipity[properties][ep_' . $attr . ']" id="properties_' . $attr . '" class="input_textbox" value="' . (function_exists('serendipity_specialchars') ? serendipity_specialchars($val) : htmlspecialchars($val, ENT_COMPAT, LANG_CHARSET)) . '" style="width: 100%" />' . "\n";
                    }
                    echo '<br />';
                }
                ?>
                    </fieldset>
<?php 
                return true;
                break;
            case 'frontend_header':
                $flattr_uid = substr($this->_addslashes($this->get_config('userid')), 0, 500);
                $flattr_btn = $this->_addslashes($this->get_config('flattr_btn'));
                $flattr_lng = substr($this->get_config('flattr_lng'), 0, 255);
                $flattr_cat = substr($this->get_config('flattr_cat'), 0, 255);
                ?>
<script type="text/javascript">
/* <![CDATA[ */
(function() {
    var s = document.createElement('script');
    var t = document.getElementsByTagName('script')[0];
    s.type = 'text/javascript';
    s.async = true;
    s.src = 'https://api.flattr.com/js/0.6/load.js?mode=auto';
    s.src += '&popout=<?php 
                echo (int) $this->get_config('flattr_pop');
                ?>
';
    s.src += '&uid=<?php 
                echo $flattr_uid;
                ?>
';
    s.src += '&language=<?php 
                echo $flattr_lng;
                ?>
';
    s.src += '&category=<?php 
                echo $flattr_cat;
                ?>
';
    <?php 
                if (in_array($flattr_btn, array('default', 'compact'))) {
                    ?>
    s.src += '&button=<?php 
                    echo $flattr_btn;
                    ?>
';
    <?php 
                }
                ?>
    t.parentNode.insertBefore(s, t);
 })();
/* ]]> */
</script>
<?php 
                break;
            case 'frontend_display':
                if (empty($eventData['properties'])) {
                    $eventData['properties'] =& serendipity_fetchEntryProperties($eventData['id']);
                }
                if ($eventData['properties']['ep_flattr_active'] == '-1') {
                    return true;
                }
                if (empty($eventData['body']) && empty($eventData['extended'])) {
                    return true;
                }
                $flattr_uid = $this->_addslashes($this->get_config('userid'));
                $flattr_tle = $this->_addslashes($eventData['title']);
                $flattr_pop = (int) $this->get_config('flattr_pop');
                $flattr_dsc = $this->_addslashes($eventData['properties']['ep_flattr_dsc']);
                $flattr_cat = $this->_addslashes($eventData['properties']['ep_flattr_cat']);
                $flattr_lng = $this->_addslashes($eventData['properties']['ep_flattr_lng']);
                $flattr_tag = $this->_addslashes($eventData['properties']['ep_flattr_tag']);
                if (empty($flattr_dsc)) {
                    $flattr_dsc = $this->_addslashes($eventData['body']);
                }
                $flattr_dsc = strip_tags($flattr_dsc);
                if (empty($flattr_cat)) {
                    $flattr_cat = $this->get_config('flattr_cat');
                }
                if (empty($flattr_lng)) {
                    $flattr_lng = $this->get_config('flattr_lng');
                }
                if (empty($flattr_tag) && class_exists('serendipity_event_freetag')) {
                    $_tags = serendipity_event_freetag::getTagsForEntries(array($eventData['id']));
                    $tags = is_array($_tags) ? array_pop($_tags) : array();
                    $flattr_tag = implode(',', $tags);
                }
                $flattr_url = $this->_addslashes(serendipity_archiveURL($eventData['id'], $eventData['title'], 'baseURL', true, array('timestamp' => $eventData['timestamp'])));
                $flattr_btn = $this->_addslashes($this->get_config('flattr_btn'));
                $flattr_uid = substr($flattr_uid, 0, 500);
                $flattr_tle = substr($flattr_tle, 0, 500);
                $flattr_dsc = substr($flattr_dsc, 0, 1000);
                $flattr_cat = substr($flattr_cat, 0, 255);
                $flattr_lng = substr($flattr_lng, 0, 255);
                $flattr_tag = substr($flattr_tag, 0, 255);
                $flattr_url = substr($flattr_url, 0, 2048);
                $flattr_btn = substr($flattr_btn, 0, 255);
                if ($flattr_btn != 'default' && $flattr_btn != 'compact') {
                    $flattr = "<a href=\"https://flattr.com/submit/auto?" . "user_id=" . urlencode($flattr_uid) . "&" . "url=" . urlencode($flattr_url) . "&" . "title=" . urlencode($flattr_tle) . "&" . "description=" . urlencode($flattr_dsc) . "&" . "category=" . urlencode($flattr_cat) . "&" . "popout=" . urlencode($flattr_pop) . "&" . "language=" . urlencode($flattr_lng) . "\">" . $flattr_btn . "</a>";
                } else {
                    $flattr_tle2 = stripslashes($flattr_tle2);
                    $flattr_tle2 = function_exists('serendipity_specialchars') ? serendipity_specialchars($flattr_tle2) : htmlspecialchars($flattr_tle2, ENT_COMPAT, LANG_CHARSET);
                    $flattr = "\r\n<a class='FlattrButton' style='display:none;'\r\n    title=\"" . $flattr_tle2 . "\"\r\n    data-flattr-uid='" . $flattr_uid . "'\r\n    data-flattr-tags='" . $flattr_tag . "'\r\n    data-flattr-category='" . $flattr_cat . "'\r\n    data-flattr-language='" . $flattr_lng . "'\r\n    href='" . $flattr_url . "'>\r\n\r\n    " . stripslashes($flattr_dsc) . "\r\n</a>\r\n    ";
                }
                $field = $this->get_config('placement');
                if ($addData['from'] == 'functions_entries:updertEntry') {
                } elseif ($addData['from'] == 'functions_entries:printEntries_rss') {
                    $entryText =& $this->getFieldReference($field, $eventData);
                    $entryText .= function_exists('serendipity_specialchars') ? serendipity_specialchars($flattr) : htmlspecialchars($flattr, ENT_COMPAT, LANG_CHARSET);
                } else {
                    $entryText =& $this->getFieldReference($field, $eventData);
                    $entryText .= $flattr;
                }
                if ($field == 'extended') {
                    $eventData['is_extended'] = true;
                }
                break;
            case 'frontend_display:rss-1.0:namespace':
            case 'frontend_display:rss-2.0:namespace':
                if ($this->get_config('add_to_feed')) {
                    $eventData['display_dat'] .= '
   xmlns:atom="http://www.w3.org/2005/Atom"';
                }
                return true;
                break;
            case 'frontend_display:rss-1.0:per_entry':
            case 'frontend_display:rss-2.0:per_entry':
                if ($this->get_config('add_to_feed')) {
                    $flattr_uid = $this->_addslashes($this->get_config('userid'));
                    $flattr_uid = substr($flattr_uid, 0, 500);
                    $flattr_url = $this->_addslashes(serendipity_archiveURL($eventData['id'], $eventData['title'], 'baseURL', true, array('timestamp' => $eventData['timestamp'])));
                    $flattr_url = substr($flattr_url, 0, 2048);
                    $eventData['display_dat'] .= '
    <atom:link rel="payment" href="https://flattr.com/submit/auto?url=' . urlencode($flattr_url) . '&amp;user_id=' . $flattr_uid . '" type="text/html" />';
                }
                return true;
                break;
            case 'frontend_display:atom-1.0:per_entry':
                if ($this->get_config('add_to_feed')) {
                    $flattr_uid = $this->_addslashes($this->get_config('userid'));
                    $flattr_uid = substr($flattr_uid, 0, 500);
                    $flattr_url = $this->_addslashes(serendipity_archiveURL($eventData['id'], $eventData['title'], 'baseURL', true, array('timestamp' => $eventData['timestamp'])));
                    $flattr_url = substr($flattr_url, 0, 2048);
                    $eventData['display_dat'] .= '
    <link rel="payment" href="https://flattr.com/submit/auto?url=' . $flattr_url . '&amp;user_id=' . $flattr_uid . '" type="text/html" />';
                }
                return true;
                break;
        }
    }
 function addProperties(&$properties, &$eventData)
 {
     global $serendipity;
     // Get existing data
     $property = serendipity_fetchEntryProperties($eventData['id']);
     $supported_properties = serendipity_event_entryproperties::getSupportedProperties();
     // Special case for disable markups.
     if (is_array($properties['disable_markups'])) {
         $q = "DELETE FROM {$serendipity['dbPrefix']}entryproperties WHERE entryid = " . (int) $eventData['id'] . " AND property LIKE 'ep_disable_markup_%'";
         serendipity_db_query($q);
         foreach ($properties['disable_markups'] as $idx => $instance) {
             $supported_properties[] = 'disable_markup_' . $instance;
         }
     }
     serendipity_plugin_api::hook_event('backend_entryproperties', $supported_properties);
     foreach ($supported_properties as $prop_key) {
         $prop_val = isset($properties[$prop_key]) ? $properties[$prop_key] : null;
         $prop_key = 'ep_' . $prop_key;
         if (is_array($prop_val)) {
             $prop_val = ";" . implode(';', $prop_val) . ";";
         }
         $q = "DELETE FROM {$serendipity['dbPrefix']}entryproperties WHERE entryid = " . (int) $eventData['id'] . " AND property = '" . serendipity_db_escape_string($prop_key) . "'";
         serendipity_db_query($q);
         if (!empty($prop_val)) {
             $q = "INSERT INTO {$serendipity['dbPrefix']}entryproperties (entryid, property, value) VALUES (" . (int) $eventData['id'] . ", '" . serendipity_db_escape_string($prop_key) . "', '" . serendipity_db_escape_string($prop_val) . "')";
             serendipity_db_query($q);
         }
     }
 }
 function add_entries(&$sitemap_xml, $limit = 0)
 {
     global $serendipity;
     $sqlnullfunction = $this->get_sqlnullfunction();
     // fetch all entries from the db (tested with: mysql, sqlite, postgres)
     $entries = serendipity_db_query('SELECT
                 entries.id AS id,
                 entries.title AS title,
                 ' . $sqlnullfunction . '(entries.last_modified,0) AS timestamp_1,
                 ' . $sqlnullfunction . '(MAX(comments.timestamp),0) AS timestamp_2
             FROM ' . $serendipity['dbPrefix'] . 'entries entries
             LEFT JOIN ' . $serendipity['dbPrefix'] . 'comments comments
             ON entries.id = comments.entry_id
             WHERE entries.isdraft = \'false\'
             AND entries.timestamp < ' . time() . '
             GROUP BY entries.id, entries.title, entries.last_modified
             ORDER BY entries.id DESC
             ' . ($limit > 0 ? ' LIMIT ' . $limit : ''), false, 'assoc');
     if (is_array($entries)) {
         if ($this->should_add('sm_archives')) {
             // get the P*.html sites if there are more than fetchLimit entries
             for ($i = 2; $i <= ceil(sizeof($entries) / $serendipity['fetchLimit']); ++$i) {
                 $url = $url = serendipity_rewriteURL(PATH_ARCHIVES . "/P{$i}.html");
                 $this->addtoxml($sitemap_xml, $url, null, 0.5);
             }
         }
         // add entries
         foreach ($entries as $entry) {
             $max = max($entry['timestamp_1'] + 0, $entry['timestamp_2'] + 0);
             $url = serendipity_archiveURL($entry['id'], $entry['title']);
             $props = serendipity_fetchEntryProperties($entry['id']);
             $props['title'] = $entry['title'];
             $this->addtoxml($sitemap_xml, $url, $max, 0.7, null, $props);
         }
     }
 }
    function event_hook($event, &$bag, &$eventData, $addData = null)
    {
        global $serendipity;
        $hooks =& $bag->get('event_hooks');
        if (isset($hooks[$event])) {
            switch ($event) {
                case 'backend_entry_updertEntry':
                    if (isset($serendipity['POST']['no_save'])) {
                        $eventData['error'] = true;
                    }
                    return true;
                    break;
                case 'backend_entry_presave':
                    if (!isset($serendipity['POST']['properties']) || !is_array($serendipity['POST']['properties']) || !isset($eventData['id']) || empty($serendipity['POST']['properties']['lang_selected'])) {
                        return true;
                    }
                    // Restore native language version, ONLY if a different language is being submitted.
                    $restore = serendipity_db_query("SELECT title, body, extended FROM {$serendipity['dbPrefix']}entries WHERE id = " . (int) $eventData['id']);
                    if (is_array($restore)) {
                        foreach ($restore as $row) {
                            foreach ($this->switch_keys as $restorekey) {
                                $eventData[$restorekey] = $row[$restorekey];
                            }
                        }
                    }
                    break;
                case 'backend_publish':
                case 'backend_save':
                    if (!isset($serendipity['POST']['properties']) || !is_array($serendipity['POST']['properties']) || !isset($eventData['id']) || empty($serendipity['POST']['properties']['lang_selected'])) {
                        return true;
                    }
                    $ls =& $serendipity['POST']['properties']['lang_selected'];
                    $this->supported_properties[] = 'multilingual_title_' . $ls;
                    $serendipity['POST']['properties']['multilingual_title_' . $ls] = $serendipity['POST']['title'];
                    $this->supported_properties[] = 'multilingual_body_' . $ls;
                    $serendipity['POST']['properties']['multilingual_body_' . $ls] = $serendipity['POST']['body'];
                    $this->supported_properties[] = 'multilingual_extended_' . $ls;
                    $serendipity['POST']['properties']['multilingual_extended_' . $ls] = $serendipity['POST']['extended'];
                    // Get existing data
                    $property = serendipity_fetchEntryProperties($eventData['id']);
                    foreach ($this->supported_properties as $prop_key) {
                        $prop_val = isset($serendipity['POST']['properties'][$prop_key]) ? $serendipity['POST']['properties'][$prop_key] : null;
                        if (!isset($property[$prop_key]) && !empty($prop_val)) {
                            $q = "INSERT INTO {$serendipity['dbPrefix']}entryproperties (entryid, property, value) VALUES (" . (int) $eventData['id'] . ", '" . serendipity_db_escape_string($prop_key) . "', '" . serendipity_db_escape_string($prop_val) . "')";
                        } elseif ($property[$propkey] != $prop_val && !empty($prop_val)) {
                            $q = "UPDATE {$serendipity['dbPrefix']}entryproperties SET value = '" . serendipity_db_escape_string($prop_val) . "' WHERE entryid = " . (int) $eventData['id'] . " AND property = '" . serendipity_db_escape_string($prop_key) . "'";
                        } else {
                            $q = "DELETE FROM {$serendipity['dbPrefix']}entryproperties WHERE entryid = " . (int) $eventData['id'] . " AND property = '" . serendipity_db_escape_string($prop_key) . "'";
                        }
                        serendipity_db_query($q);
                    }
                    return true;
                    break;
                case 'genpage':
                    if (!is_object($serendipity['smarty'])) {
                        // never init in genpage without adding previously set $vars, which is $view etc!
                        serendipity_smarty_init($serendipity['plugindata']['smartyvars']);
                    }
                    if (!defined('Smarty::SMARTY_VERSION')) {
                        $this->tag_title();
                        // in Smarty 2 only
                        // check this deeply! - since at least for the non-tag banner entry_title this seems to not work here with Smarty 3 - see workaround in frontent_display
                    }
                    if ($serendipity['version'][0] < 2) {
                        $serendipity['smarty']->register_modifier('multilingual_lang', array($this, 'strip_langs'));
                    } else {
                        $serendipity['smarty']->registerPlugin('modifier', 'multilingual_lang', array($this, 'strip_langs'));
                    }
                    return true;
                    break;
                case 'backend_entryform':
                    if (!empty($this->showlang)) {
                        // language is given (he wants a translation)
                        $props = serendipity_fetchEntryProperties($eventData['id']);
                        // this is a language change, not a save -- we want the DB values
                        // unless the user chooses to retain previous language content
                        if (isset($serendipity['POST']['no_save'])) {
                            foreach ($this->switch_keys as $key) {
                                if (!serendipity_db_bool($this->get_config('copytext', 'true')) || !empty($props['multilingual_' . $key . '_' . $this->showlang])) {
                                    $eventData[$key] = $props['multilingual_' . $key . '_' . $this->showlang];
                                }
                            }
                        }
                    } elseif (!empty($eventData['id'])) {
                        // language is NOT given (he wants the default language)
                        $props = serendipity_fetchEntry('id', $eventData['id'], 1, 1);
                        if (!is_array($props)) {
                            return true;
                        }
                        // this is a language change, not a save -- we want the DB values
                        if (isset($serendipity['POST']['no_save'])) {
                            foreach ($this->switch_keys as $key) {
                                $eventData[$key] = $props[$key];
                            }
                        }
                    }
                    return true;
                    break;
                case 'css':
                    if (strpos($eventData, '.serendipity_multilingualInfo')) {
                        // class exists in CSS, so a user has customized it and we don't need default
                        return true;
                    }
                    ?>

.serendipity_multilingualInfo {
    margin-left: auto;
    margin-right: 0px;
    text-align: right;
    font-size: 7pt;
    display: block;
    margin-top: 5px;
    margin-bottom: 0px;
}

.serendipity_multilingualInfo a {
    font-size: 7pt;
    text-decoration: none;
}

.serendipity_multilingualInfo a:hover {
    color: green;
}

<?php 
                    return true;
                    break;
                case 'frontend_display':
                    // cannot use tag_title() method here and use with Smarty3+ only
                    // check for single entry multilingual context only, to set the correct themes banner title
                    if (defined('Smarty::SMARTY_VERSION') && isset($eventData['properties']['multilingual_title_' . $this->showlang]) && serendipity_db_bool($this->get_config('tagged_title', 'true')) && $serendipity['view'] == 'entry') {
                        $serendipity['smarty']->assign('head_title', $eventData['title']);
                    }
                    break;
                case 'entry_display':
                    if (!is_array($eventData)) {
                        return false;
                    }
                    $place = $this->get_config('placement', 'add_footer');
                    $msg = '<div class="serendipity_multilingualInfo">' . PLUGIN_EVENT_MULTILINGUAL_SWITCH . ': %s</div>';
                    if ($addData['extended'] || $addData['preview']) {
                        if ($langs = $this->getLang($eventData[0]['id'], $eventData[0]['properties'])) {
                            if (!empty($this->showlang)) {
                                $props =& $eventData[0]['properties'];
                                foreach ($this->switch_keys as $key) {
                                    if (!empty($props['multilingual_' . $key . '_' . $this->showlang])) {
                                        $eventData[0][$key] = $props['multilingual_' . $key . '_' . $this->showlang];
                                    }
                                }
                                unset($eventData[0]['properties']['ep_cache_body']);
                                unset($eventData[0]['properties']['ep_cache_extended']);
                            }
                            $eventData[0][$place] .= sprintf($msg, $langs);
                        }
                    } else {
                        $elements = count($eventData);
                        // Walk entry array and insert karma voting line.
                        for ($i = 0; $i < $elements; $i++) {
                            if (!isset($eventData[$i][$place])) {
                                $eventData[$i][$place] = '';
                            }
                            if (!empty($this->lang_display)) {
                                $this->showlang = $this->lang_display;
                            }
                            if (!empty($this->showlang)) {
                                // Not sure if it's the best way to get translations shown instead of the
                                // original entries
                                $props =& $eventData[$i]['properties'];
                                foreach ($this->switch_keys as $key) {
                                    if (!empty($props['multilingual_' . $key . '_' . $this->showlang])) {
                                        $eventData[$i][$key] = $props['multilingual_' . $key . '_' . $this->showlang];
                                    }
                                }
                                unset($eventData[$i]['properties']['ep_cache_body']);
                                unset($eventData[$i]['properties']['ep_cache_extended']);
                            }
                            if ($langs = $this->getLang($eventData[$i]['id'], $eventData[$i]['properties'])) {
                                $eventData[$i][$place] .= sprintf($msg, $langs);
                                // this may throw two for the same, eg. when already linked as <en>, them set to POST cookie <en> too in the sidebar selection.
                                // In this case the lang link and the default lang link are both named 'english'
                            }
                        }
                    }
                    // Tagged translation of Blog title and description
                    $this->tag_title();
                    if (serendipity_db_bool($this->get_config('tagged_entries', 'true'))) {
                        foreach ($eventData as $key => $entry) {
                            if (isset($eventData[$key]['title'])) {
                                $eventData[$key]['title'] = $this->strip_langs($eventData[$key]['title']);
                                $eventData[$key]['body'] = $this->strip_langs($eventData[$key]['body']);
                                if (is_array($eventData[$key]['categories'])) {
                                    foreach ($eventData[$key]['categories'] as $ec_key => $ec_val) {
                                        $eventData[$key]['categories'][$ec_key]['category_name'] = $this->strip_langs($ec_val['category_name']);
                                    }
                                }
                            }
                        }
                    }
                    return true;
                    break;
                case 'backend_display':
                    if (isset($serendipity['POST']['properties']['lang_selected'])) {
                        $lang_selected = $serendipity['POST']['properties']['lang_selected'];
                    } else {
                        $lang_selected = '';
                    }
                    $use_lang = $serendipity['languages'];
                    unset($use_lang[$serendipity['lang']]);
                    // Unset 'default' language. Easier handling.
                    $langs = '';
                    //asort($use_lang); //sorts by value ASC, but if so we should do it everywhere though
                    foreach ($use_lang as $code => $desc) {
                        $langs .= '<option value="' . $code . '" ' . ($lang_selected == $code ? 'selected="selected"' : '') . '>' . (function_exists('serendipity_specialchars') ? serendipity_specialchars($desc) : htmlspecialchars($desc, ENT_COMPAT, LANG_CHARSET)) . '</option>' . "\n";
                    }
                    if ($serendipity['version'][0] < 2) {
                        ?>
                    <fieldset style="margin: 5px">
                        <legend><?php 
                        echo PLUGIN_EVENT_MULTILINGUAL_TITLE;
                        ?>
</legend>
                        <div class="form_field">
<?php 
                    } else {
                        ?>
                    <fieldset id="edit_entry_multilingual" class="entryproperties_multilingual" style="margin: 1em 0">
                        <span class="wrap_legend"><legend><?php 
                        echo PLUGIN_EVENT_MULTILINGUAL_TITLE;
                        ?>
</legend></span>
                        <div class="form_field">
<?php 
                    }
                    if (isset($eventData['id'])) {
                        ?>
                        <label for="serendipity[properties][lang_selected]"><?php 
                        echo PLUGIN_EVENT_MULTILINGUAL_CURRENT;
                        ?>
</label><br />
                        <select name="serendipity[properties][lang_selected]" id="properties_lang_selected" />
                            <option value=""><?php 
                        echo USE_DEFAULT;
                        ?>
</option>
                            <?php 
                        echo $langs;
                        ?>
                        </select>
                        <input class="serendipityPrettyButton input_button" type="submit" name="serendipity[no_save]" value="<?php 
                        echo PLUGIN_EVENT_MULTILINGUAL_SWITCH;
                        ?>
" />
<?php 
                    } else {
                        echo '<span class="msg_notice"><span class="icon-info-circled"></span> ' . PLUGIN_EVENT_MULTILINGUAL_NEEDTOSAVE . "</span>\n";
                    }
                    ?>
                        </div>
                    </fieldset>
<?php 
                    return true;
                    break;
                case 'frontend_entryproperties':
                    if (class_exists('serendipity_event_entryproperties')) {
                        // Fetching of properties is already done there, so this is just for poor users who don't have the entryproperties plugin enabled
                        return true;
                    }
                    $q = "SELECT entryid, property, value FROM {$serendipity['dbPrefix']}entryproperties WHERE entryid IN (" . implode(', ', array_keys($addData)) . ") AND property LIKE '%multilingual_%'";
                    $properties = serendipity_db_query($q);
                    if (!is_array($properties)) {
                        return true;
                    }
                    foreach ($properties as $idx => $row) {
                        $eventData[$addData[$row['entryid']]]['properties'][$row['property']] = $row['value'];
                    }
                    return true;
                    break;
                case 'frontend_entries_rss':
                    if (is_array($eventData)) {
                        foreach ($eventData as $i => $entry) {
                            if (!empty($this->lang_display)) {
                                $this->showlang = $this->lang_display;
                            }
                            if (!empty($this->showlang)) {
                                // Not sure if it's the best way to get translations shown instead of the
                                // original entries
                                $props =& $eventData[$i]['properties'];
                                foreach ($this->switch_keys as $key) {
                                    if (!empty($props['multilingual_' . $key . '_' . $this->showlang])) {
                                        $eventData[$i][$key] = $props['multilingual_' . $key . '_' . $this->showlang];
                                    }
                                }
                                unset($eventData[$i]['properties']['ep_cache_body']);
                                unset($eventData[$i]['properties']['ep_cache_extended']);
                            }
                        }
                    }
                    if (serendipity_db_bool($this->get_config('tagged_entries', 'true'))) {
                        foreach ($eventData as $key => $entry) {
                            $eventData[$key]['title'] = $this->strip_langs($eventData[$key]['title']);
                            $eventData[$key]['body'] = $this->strip_langs($eventData[$key]['body']);
                        }
                    }
                    break;
                case 'frontend_fetchentries':
                case 'frontend_fetchentry':
                    if (!empty($this->lang_display)) {
                        $this->showlang = $this->lang_display;
                    }
                    if ($addData['source'] == 'search' && empty($this->showlang) && !empty($_SESSION['last_lang'])) {
                        header('X-SearchLangOverride: ' . $_SESSION['last_lang']);
                        $this->showlang = $_SESSION['last_lang'];
                    }
                    if (empty($this->showlang)) {
                        return;
                    }
                    $cond = "multilingual_body.value AS multilingual_body,\n";
                    $cond .= "multilingual_extended.value AS multilingual_extended,\n";
                    $cond .= "multilingual_title.value AS multilingual_title,\n";
                    if (empty($eventData['addkey'])) {
                        $eventData['addkey'] = $cond;
                    } else {
                        $eventData['addkey'] .= $cond;
                    }
                    $cond = "\nLEFT OUTER JOIN {$serendipity['dbPrefix']}entryproperties multilingual_body\n                                                 ON (e.id = multilingual_body.entryid AND multilingual_body.property = 'multilingual_body_" . $this->showlang . "')";
                    $cond .= "\nLEFT OUTER JOIN {$serendipity['dbPrefix']}entryproperties multilingual_extended\n                                                 ON (e.id = multilingual_extended.entryid AND multilingual_extended.property = 'multilingual_extended_" . $this->showlang . "')";
                    $cond .= "\nLEFT OUTER JOIN {$serendipity['dbPrefix']}entryproperties multilingual_title\n                                                 ON (e.id = multilingual_title.entryid AND multilingual_title.property = 'multilingual_title_" . $this->showlang . "')";
                    if (!empty($this->lang_display)) {
                        // If lang_display is set - we want ONLY the entries which have translation
                        $eventData['and'] .= " AND multilingual_body.value IS NOT NULL";
                    }
                    if (empty($eventData['joins'])) {
                        $eventData['joins'] = $cond;
                    } else {
                        $eventData['joins'] .= $cond;
                    }
                    if ($addData['source'] == 'search' && isset($eventData['find_part'])) {
                        $term =& $addData['term'];
                        $cond =& $eventData;
                        if ($serendipity['dbType'] == 'postgres') {
                            $cond['find_part'] .= " OR (multilingual_body.value ILIKE '%{$term}%' OR multilingual_extended.value ILIKE '%{$term}%' OR multilingual_title.value ILIKE '%{$term}%')";
                        } elseif ($serendipity['dbType'] == 'sqlite') {
                            $term = serendipity_mb('strtolower', $term);
                            $cond['find_part'] .= " OR (lower(multilingual_body.value) LIKE '%{$term}%' OR lower(multilingual_extended.value) LIKE '%{$term}%' OR lower(multilingual_title.value) LIKE '%{$term}%')";
                        } else {
                            if (preg_match('@["\\+\\-\\*~<>\\(\\)]+@', $term)) {
                                $bool = ' IN BOOLEAN MODE';
                            } else {
                                $bool = '';
                            }
                            $cond['find_part'] .= " OR (\n                                                         MATCH(multilingual_body.value)        AGAINST('{$term}' {$bool})\n                                                         OR MATCH(multilingual_extended.value) AGAINST('{$term}' {$bool})\n                                                         OR MATCH(multilingual_title.value)    AGAINST('{$term}' {$bool})\n                                                       )";
                        }
                    }
                    return true;
                    break;
                case 'frontend_comment':
                    if (serendipity_db_bool($this->get_config('tagged_entries', 'true'))) {
                        $serendipity['smarty']->assign('head_title', $this->strip_langs($serendipity['head_title']));
                    }
                    if (serendipity_db_bool($this->get_config('tagged_title', 'true'))) {
                        $serendipity['smarty']->assign('head_subtitle', $this->strip_langs($serendipity['head_subtitle']));
                    }
                    return true;
                    break;
                case 'frontend_sidebar_plugins':
                    if (serendipity_db_bool($this->get_config('tagged_sidebar', 'true'))) {
                        foreach ($eventData as $key => $entry) {
                            $eventData[$key]['title'] = $this->strip_langs($eventData[$key]['title']);
                            $eventData[$key]['content'] = $this->strip_langs($eventData[$key]['content']);
                        }
                    }
                    return true;
                    break;
                default:
                    return false;
                    break;
            }
        } else {
            return false;
        }
    }
    function event_hook($event, &$bag, &$eventData, $addData = null)
    {
        global $serendipity;
        $hooks =& $bag->get('event_hooks');
        if (isset($hooks[$event])) {
            switch ($event) {
                case 'genpage':
                    // The 'genpage' hook is our last chance to modify Smarty
                    // variables before the template is called for the HTML head.
                    // Only modify the title on single-entry pages
                    if ($addData['view'] == 'entry') {
                        // Get the properties for this entry
                        $myid = $serendipity['GET']['id'];
                        // Requires a database fetch, but the only other way
                        // get the entry properties is to wait until we
                        // generate the entry; by the time that hook is
                        // called, the <title> tag has already been emitted.
                        // We need those properties now!
                        $property = serendipity_fetchEntryProperties($myid);
                        // Set a title, if one was defined for this entry
                        if (!empty($property['meta_head_title'])) {
                            // Make the variable name a little less unwieldy
                            $this->meta_title = $property['meta_head_title'];
                            $this->save_title = $serendipity['head_title'];
                            $this->save_subtitle = $serendipity['head_subtitle'];
                            $serendipity['head_title'] = function_exists('serendipity_specialchars') ? serendipity_specialchars($this->meta_title) : htmlspecialchars($this->meta_title, ENT_COMPAT, LANG_CHARSET);
                            // Clear the subtitle (many templates use it along with the title)
                            $serendipity['head_subtitle'] = '';
                        }
                    }
                    return true;
                    break;
                case 'frontend_header':
                    $default_description = $this->get_config('default_description');
                    $default_keywords = $this->get_config('default_keywords');
                    // Only emit in Single Entry Mode
                    if ($serendipity['GET']['id'] && isset($GLOBALS['entry'][0]['body'])) {
                        // If we modified the <title>...
                        if (!empty($this->meta_title)) {
                            // We've messed up the banner.  Put it back the way it was.
                            // Set smarty variables for banner back to normal "entry title - blog description"
                            $serendipity['smarty']->assign(array('head_title' => $this->save_title, 'head_subtitle' => $this->save_subtitle));
                        }
                        $meta_description = $GLOBALS['entry'][0]['properties']['meta_description'];
                        if (empty($meta_description)) {
                            $description_body = $GLOBALS['entry'][0]['body'];
                            if (isset($GLOBALS['entry'][0]['plaintext_body'])) {
                                $description_body = trim($GLOBALS['entry'][0]['plaintext_body']);
                            }
                            $meta_description = $this->extract_description($description_body);
                        }
                        $meta_keywords = $GLOBALS['entry'][0]['properties']['meta_keywords'];
                        if (empty($meta_keywords)) {
                            $meta_keywords = (array) $this->extract_keywords($GLOBALS['entry'][0]['body']);
                            if (!empty($meta_keywords)) {
                                $meta_keywords = implode(',', $meta_keywords);
                            } else {
                                // no entry specific keywords for this entry and extract_keywords was returned empty
                                $meta_keywords = $default_keywords;
                            }
                        }
                        if (serendipity_db_bool($this->get_config('escape'))) {
                            $md = function_exists('serendipity_specialchars') ? serendipity_specialchars($meta_description) : htmlspecialchars($meta_description, ENT_COMPAT, LANG_CHARSET);
                            $mk = function_exists('serendipity_specialchars') ? serendipity_specialchars($meta_keywords) : htmlspecialchars($meta_keywords, ENT_COMPAT, LANG_CHARSET);
                        } else {
                            $md = $meta_description;
                            $mk = $meta_keywords;
                        }
                        echo '<meta name="description" content="' . $md . '" />' . "\n";
                        if (!empty($meta_keywords)) {
                            echo '        <meta name="keywords" content="' . $mk . '" />' . "\n";
                        }
                    } else {
                        // emit default meta description and meta keyword, if not blank, for pages other than single entry
                        if (serendipity_db_bool($this->get_config('escape'))) {
                            $md = function_exists('serendipity_specialchars') ? serendipity_specialchars($default_description) : htmlspecialchars($default_description, ENT_COMPAT, LANG_CHARSET);
                            $mk = function_exists('serendipity_specialchars') ? serendipity_specialchars($default_keywords) : htmlspecialchars($default_keywords, ENT_COMPAT, LANG_CHARSET);
                        } else {
                            $md = $default_description;
                            $mk = $default_keywords;
                        }
                        if (!empty($default_description)) {
                            echo '<meta name="description" content="' . $md . '" />' . "\n";
                        }
                        if (!empty($default_keywords)) {
                            echo '        <meta name="keywords" content="' . $mk . '" />' . "\n";
                        }
                    }
                    break;
                case 'backend_publish':
                case 'backend_save':
                    if (!isset($serendipity['POST']['properties']) || !is_array($serendipity['POST']['properties']) || !isset($eventData['id'])) {
                        return true;
                    }
                    // Get existing data
                    $property = serendipity_fetchEntryProperties($eventData['id']);
                    foreach ($this->supported_properties as $prop_key) {
                        $prop_val = isset($serendipity['POST']['properties'][$prop_key]) ? $serendipity['POST']['properties'][$prop_key] : null;
                        if (!isset($property[$prop_key]) && !empty($prop_val)) {
                            $q = "INSERT INTO {$serendipity['dbPrefix']}entryproperties (entryid, property, value) VALUES (" . (int) $eventData['id'] . ", '" . serendipity_db_escape_string($prop_key) . "', '" . serendipity_db_escape_string($prop_val) . "')";
                        } elseif ($property[$propkey] != $prop_val && !empty($prop_val)) {
                            $q = "UPDATE {$serendipity['dbPrefix']}entryproperties SET value = '" . serendipity_db_escape_string($prop_val) . "' WHERE entryid = " . (int) $eventData['id'] . " AND property = '" . serendipity_db_escape_string($prop_key) . "'";
                        } else {
                            $q = "DELETE FROM {$serendipity['dbPrefix']}entryproperties WHERE entryid = " . (int) $eventData['id'] . " AND property = '" . serendipity_db_escape_string($prop_key) . "'";
                        }
                        serendipity_db_query($q);
                    }
                    return true;
                    break;
                case 'backend_display':
                    if (isset($serendipity['POST']['properties']['meta_description'])) {
                        $meta_description = $serendipity['POST']['properties']['meta_description'];
                    } elseif (isset($eventData['properties']['meta_description'])) {
                        $meta_description = $eventData['properties']['meta_description'];
                    } else {
                        $meta_description = '';
                    }
                    if (isset($serendipity['POST']['properties']['meta_keywords'])) {
                        $meta_keywords = $serendipity['POST']['properties']['meta_keywords'];
                    } elseif (isset($eventData['properties']['meta_keywords'])) {
                        $meta_keywords = $eventData['properties']['meta_keywords'];
                    } else {
                        $meta_keywords = '';
                    }
                    if (isset($serendipity['POST']['properties']['meta_head_title'])) {
                        $meta_head_title = $serendipity['POST']['properties']['meta_head_title'];
                    } elseif (isset($eventData['properties']['meta_head_title'])) {
                        $meta_head_title = $eventData['properties']['meta_head_title'];
                    } else {
                        $meta_head_title = '';
                    }
                    ?>
                    <fieldset style="margin: 5px">
                        <legend><?php 
                    echo PLUGIN_METADESC_NAME;
                    ?>
</legend>
                        <p class="meta_description"><em><?php 
                    echo PLUGIN_METADESC_FORM;
                    ?>
</em></p>

                        <label for="serendipity[properties][meta_description]"><?php 
                    echo PLUGIN_METADESC_DESCRIPTION;
                    ?>
</label><br />
                        <input class="input_textbox" type="text" style="width: 100%" value="<?php 
                    echo function_exists('serendipity_specialchars') ? serendipity_specialchars($meta_description) : htmlspecialchars($meta_description, ENT_COMPAT, LANG_CHARSET);
                    ?>
" name="serendipity[properties][meta_description]" id="properties_meta_description" />
                        <span class="meta_string_length"><?php 
                    echo PLUGIN_METADESC_LENGTH . ': ' . str_word_count($meta_description) . ' ' . PLUGIN_METADESC_WORDS . ', ' . strlen($meta_description) . ' ' . PLUGIN_METADESC_CHARACTERS;
                    ?>
</span>
                        <br /><br />
                        <label for="serendipity[properties][meta_keywords]"><?php 
                    echo PLUGIN_METADESC_KEYWORDS;
                    ?>
</label><br />
                        <input class="input_textbox" type="text" style="width: 100%" value="<?php 
                    echo function_exists('serendipity_specialchars') ? serendipity_specialchars($meta_keywords) : htmlspecialchars($meta_keywords, ENT_COMPAT, LANG_CHARSET);
                    ?>
" name="serendipity[properties][meta_keywords]" id="properties_meta_keywords" />
                        <span class="meta_string_length"><?php 
                    echo PLUGIN_METADESC_LENGTH . ': ' . str_word_count($meta_keywords) . ' ' . PLUGIN_METADESC_WORDS . ', ' . strlen($meta_keywords) . ' ' . PLUGIN_METADESC_CHARACTERS;
                    ?>
</span>
                        <br /><br />

                        <p class="meta_description"><em><?php 
                    echo PLUGIN_METADESC_HEADTITLE_DESC;
                    ?>
</em></p>

                        <label for="serendipity[properties][meta_head_title]"><?php 
                    echo PLUGIN_METADESC_HEADTITLE;
                    ?>
</label><br />
                        <input class="input_textbox" type="text" style="width: 100%" value="<?php 
                    echo function_exists('serendipity_specialchars') ? serendipity_specialchars($meta_head_title) : htmlspecialchars($meta_head_title, ENT_COMPAT, LANG_CHARSET);
                    ?>
" name="serendipity[properties][meta_head_title]" id="properties_headtitle" />
                        <span class="meta_string_length"><?php 
                    echo PLUGIN_METADESC_LENGTH . ': ' . str_word_count($meta_head_title) . ' ' . PLUGIN_METADESC_WORDS . ', ' . strlen($meta_head_title) . ' ' . PLUGIN_METADESC_CHARACTERS;
                    ?>
</span>

                        <p class="meta_stringlength_disclaimer"><em><?php 
                    echo '<sup>*</sup> ' . PLUGIN_METADESC_STRINGLENGTH_DISCLAIMER;
                    ?>
</em></p>
                    </fieldset>
<?php 
                    return true;
                    break;
                case 'xmlrpc_deleteEntry':
                case 'backend_delete_entry':
                    $q = "DELETE FROM {$serendipity['dbPrefix']}entryproperties WHERE entryid = " . (int) $eventData['id'] . " AND property LIKE '%meta_%'";
                    serendipity_db_query($q);
                    return true;
                    break;
                case 'frontend_entryproperties':
                    if (class_exists('serendipity_event_entryproperties') || !is_array($addData)) {
                        // Fetching of properties is already done there, so this is just for poor users who don't have the entryproperties plugin enabled
                        return true;
                    }
                    $q = "SELECT entryid, property, value FROM {$serendipity['dbPrefix']}entryproperties WHERE entryid IN (" . implode(', ', array_keys($addData)) . ") AND property LIKE '%meta_%'";
                    $properties = serendipity_db_query($q);
                    if (!is_array($properties)) {
                        return true;
                    }
                    foreach ($properties as $idx => $row) {
                        $eventData[$addData[$row['entryid']]]['properties'][$row['property']] = $row['value'];
                    }
                    return true;
                    break;
                    /* MAYBE FOR FUTURE
                                    case 'frontend_fetchentry':
                                        $cond  = "meta_d.value AS meta_description,\n";
                                        $cond .= "meta_k.value AS meta_keywords,\n";
                                        if (empty($eventData['addkey'])) {
                                            $eventData['addkey'] = $cond;
                                        } else {
                                            $eventData['addkey'] .= $cond;
                                        }
                                        $cond  = "\nLEFT OUTER JOIN {$serendipity['dbPrefix']}entryproperties meta_d
                                                                     ON (e.id = meta_d.entryid AND meta_d.property = 'meta_description')";
                                        $cond .= "\nLEFT OUTER JOIN {$serendipity['dbPrefix']}entryproperties meta_k
                                                                     ON (e.id = meta_k.entryid AND meta_d.property = 'meta_keywords')";
                    
                                        if (empty($eventData['joins'])) {
                                            $eventData['joins'] = $cond;
                                        } else {
                                            $eventData['joins'] .= $cond;
                                        }
                    
                                        return true;
                                        break;
                    */
                /* MAYBE FOR FUTURE
                                case 'frontend_fetchentry':
                                    $cond  = "meta_d.value AS meta_description,\n";
                                    $cond .= "meta_k.value AS meta_keywords,\n";
                                    if (empty($eventData['addkey'])) {
                                        $eventData['addkey'] = $cond;
                                    } else {
                                        $eventData['addkey'] .= $cond;
                                    }
                                    $cond  = "\nLEFT OUTER JOIN {$serendipity['dbPrefix']}entryproperties meta_d
                                                                 ON (e.id = meta_d.entryid AND meta_d.property = 'meta_description')";
                                    $cond .= "\nLEFT OUTER JOIN {$serendipity['dbPrefix']}entryproperties meta_k
                                                                 ON (e.id = meta_k.entryid AND meta_d.property = 'meta_keywords')";
                
                                    if (empty($eventData['joins'])) {
                                        $eventData['joins'] = $cond;
                                    } else {
                                        $eventData['joins'] .= $cond;
                                    }
                
                                    return true;
                                    break;
                */
                case 'xmlrpc_updertEntry':
                    if (isset($eventData['id'])) {
                        //XMLRPC call
                        if (!empty($eventData['mt_keywords'])) {
                            $property = serendipity_fetchEntryProperties($eventData['id']);
                            if (!isset($property['meta_keywords'])) {
                                $q = "INSERT INTO {$serendipity['dbPrefix']}entryproperties (entryid, property, value) VALUES (" . (int) $eventData['id'] . ", 'meta_keywords', '" . serendipity_db_escape_string($eventData['mt_keywords']) . "')";
                            } elseif ($property['mt_keywords'] != $eventData['mt_keywords']) {
                                $q = "UPDATE {$serendipity['dbPrefix']}entryproperties SET value = '" . serendipity_db_escape_string($eventData['mt_keywords']) . "' WHERE entryid = " . (int) $eventData['id'] . " AND property = 'meta_keywords'";
                            } else {
                                $q = "DELETE FROM {$serendipity['dbPrefix']}entryproperties WHERE entryid = " . (int) $eventData['id'] . " AND property = 'meta_keywords'";
                            }
                            serendipity_db_query($q);
                        }
                    }
                    return true;
                    break;
                case 'xmlrpc_fetchEntry':
                    if (isset($eventData['id'])) {
                        //XMLRPC call
                        $q = "SELECT entryid, property, value FROM {$serendipity['dbPrefix']}entryproperties WHERE entryid IN (" . $eventData['id'] . ") AND property LIKE '%meta_keywords%'";
                        $properties = serendipity_db_query($q);
                        if (!is_array($properties)) {
                            return true;
                        }
                        //wow, this is hack... is there a better way?
                        $properties = $properties[0];
                        $eventData['mt_keywords'] = $properties['value'];
                    }
                    return true;
                    break;
                default:
                    return false;
                    break;
            }
            return true;
        } else {
            return false;
        }
    }
 function getRelatedEntriesHtml(&$entries, $extended_smarty = false)
 {
     global $serendipity;
     if (!is_array($entries)) {
         return false;
     }
     $entrylink = $serendipity['baseURL'] . ($serendipity['rewrite'] == 'none' ? $serendipity['indexFile'] . '?/' : '/');
     if ($extended_smarty) {
         $return = array();
         $return['description'] = PLUGIN_EVENT_FREETAG_RELATED_ENTRIES;
         foreach ($entries as $entryid => $title) {
             $properties = serendipity_fetchEntryProperties($entryid);
             $return['entries'][] = array('url' => serendipity_archiveURL($entryid, $title), 'title' => htmlspecialchars($title), 'rlbild' => htmlspecialchars($properties[ep_smallIMG]), 'sub_title' => htmlspecialchars($properties[entry_subtitle]));
         }
     } else {
         $return = '<div class="serendipity_freeTag_related">' . PLUGIN_EVENT_FREETAG_RELATED_ENTRIES . '<ul class="plainList">';
         foreach ($entries as $entryid => $title) {
             $properties = serendipity_fetchEntryProperties($entryid);
             $return .= '<li> <a href="' . serendipity_archiveURL($entryid, $title) . '" title="' . (function_exists('serendipity_specialchars') ? serendipity_specialchars($title) : htmlspecialchars($title, ENT_COMPAT, LANG_CHARSET)) . '">' . (function_exists('serendipity_specialchars') ? serendipity_specialchars($title) : htmlspecialchars($title, ENT_COMPAT, LANG_CHARSET)) . '</a></li>';
         }
         $return .= '</ul>hallo</div>';
     }
     return $return;
 }