add('name', PLUGIN_EVENT_MULTILINGUAL_TITLE); $propbag->add('description', PLUGIN_EVENT_MULTILINGUAL_DESC); $propbag->add('stackable', false); $propbag->add('author', 'Garvin Hicking, Wesley Hwang-Chung'); $propbag->add('requirements', array( 'serendipity' => '0.8', 'smarty' => '2.6.7', 'php' => '4.1.0' )); $propbag->add('groups', array('FRONTEND_ENTRY_RELATED', 'BACKEND_EDITOR')); $propbag->add('version', '2.15'); $propbag->add('configuration', array('copytext', 'placement', 'tagged_title', 'tagged_entries', 'tagged_sidebar', 'langswitch')); $propbag->add('event_hooks', array( 'frontend_fetchentries' => true, 'frontend_fetchentry' => true, 'entry_display' => true, 'backend_publish' => true, 'backend_save' => true, 'backend_display' => true, 'frontend_entryproperties' => true, 'backend_sidebar_entries' => true, 'external_plugin' => true, 'css' => true, 'backend_entryform' => true, 'backend_entry_presave' => true, 'backend_entry_updertEntry' => true, 'frontend_entries_rss' => true, 'frontend_comment' => true, 'frontend_sidebar_plugins' => true, 'genpage' => true, )); $this->supported_properties = array('lang_selected','lang_display'); $this->dependencies = array('serendipity_plugin_multilingual' => 'remove'); // Okay, Garv. I explain this to you ONCE and ONLY. // $this->lang_display is the variable that FORCES translations of entries. If a translation does not exist, // an entry is NOT SHOWN. // $this->showlang is the variable that indicates which language of an entry to prefer. if (isset($serendipity['GET']['lang_display'])) { $this->lang_display = serendipity_db_escape_string($serendipity['GET']['lang_display']); header('X-Serendipity-ML-LD-1: ' . $this->cleanheader($this->lang_display)); } if (empty($this->showlang) && isset($serendipity['POST']['properties']['lang_selected'])) { $this->showlang = serendipity_db_escape_string($serendipity['POST']['properties']['lang_selected']); $_SESSION['last_lang'] = $this->showlang; serendipity_header('X-Serendipity-ML-SL-1: ' . $this->cleanheader($this->showlang)); } elseif (empty($this->showlang) && isset($serendipity['GET']['lang_selected'])) { $this->showlang = serendipity_db_escape_string($serendipity['GET']['lang_selected']); $_SESSION['last_lang'] = $this->showlang; serendipity_header('X-Serendipity-ML-SL-2: ' . $this->cleanheader($this->showlang)); } elseif (empty($this->showlang) && isset($_REQUEST['user_language'])) { $this->showlang = $_REQUEST['user_language']; serendipity_header('X-Serendipity-ML-SL-3: ' . $this->cleanheader($this->showlang)); } elseif (empty($this->showlang) && isset($_REQUEST['serendipity']['serendipityLanguage'])) { $this->showlang = $_REQUEST['serendipity']['serendipityLanguage']; serendipity_header('X-Serendipity-ML-SL-4: ' . $this->cleanheader($this->showlang)); } elseif (empty($this->showlang) && isset($serendipity['lang']) && !isset($_SESSION['last_lang'])) { $this->showlang = $serendipity['lang']; serendipity_header('X-Serendipity-ML-SL-5: ' . $this->cleanheader($this->showlang)); } if (!isset($serendipity['languages'][$this->showlang])) { $this->showlang = ''; serendipity_header('X-Serendipity-ML-SL-RESET: ' . $this->cleanheader($serendipity['default_lang'])); } if (!headers_sent()) { serendipity_header('X-Serendipity-ContentLang: ' . $this->cleanheader($this->showlang)); } $this->setupDB(); } function setupDB() { global $serendipity; $built = $this->get_config('db_built', null); if (empty($built)) { $q = "@CREATE {FULLTEXT_MYSQL} INDEX fulltext_idx on {$serendipity['dbPrefix']}entryproperties (value);"; serendipity_db_schema_import($q); $this->set_config('db_built', 2); } } function cleanheader($string) { $string = preg_replace('@[^0-9a-z_-]@imsU', '', $string); } function introspect_config_item($name, &$propbag) { switch($name) { case 'tagged_title': $propbag->add('type', 'boolean'); $propbag->add('name', PLUGIN_EVENT_MULTILINGUAL_TAGTITLE); $propbag->add('description', PLUGIN_EVENT_MULTILINGUAL_TAGTITLE_DESC); $propbag->add('default', 'true'); break; case 'tagged_entries': $propbag->add('type', 'boolean'); $propbag->add('name', PLUGIN_EVENT_MULTILINGUAL_TAGENTRIES); $propbag->add('description', PLUGIN_EVENT_MULTILINGUAL_TAGENTRIES_DESC); $propbag->add('default', 'false'); break; case 'langswitch': $propbag->add('type', 'boolean'); $propbag->add('name', PLUGIN_EVENT_MULTILINGUAL_LANGSWITCH); $propbag->add('description', PLUGIN_EVENT_MULTILINGUAL_LANGSWITCH_DESC); $propbag->add('default', 'true'); break; case 'tagged_sidebar': $propbag->add('type', 'boolean'); $propbag->add('name', PLUGIN_EVENT_MULTILINGUAL_TAGSIDEBAR); $propbag->add('description', PLUGIN_EVENT_MULTILINGUAL_TAGSIDEBAR_DESC); $propbag->add('default', 'true'); break; case 'placement': $propbag->add('type', 'radio'); $propbag->add('name', PLUGIN_EVENT_MULTILINGUAL_PLACE); $propbag->add('description', ''); $propbag->add('radio', array( 'value' => array('add_footer', 'multilingual_footer'), 'desc' => array(PLUGIN_EVENT_MULTILINGUAL_PLACE_ADDFOOTER, PLUGIN_EVENT_MULTILINGUAL_PLACE_ADDSPECIAL) )); $propbag->add('radio_per_row', '1'); $propbag->add('default', 'add_footer'); break; case 'copytext': $propbag->add('type', 'boolean'); $propbag->add('name', PLUGIN_EVENT_MULTILINGUAL_COPY); $propbag->add('description', PLUGIN_EVENT_MULTILINGUAL_COPYDESC); $propbag->add('default', 'true'); break; default: return false; } return true; } function generate_content(&$title) { $title = $this->title; } function urlparam($key) { static $langswitch = null; if ($langswitch === null) { $langswitch = serendipity_db_bool($this->get_config('langswitch')); } if ($langswitch) { // user_language return 'serendipity[lang_selected]=' . $key . '&serendipity[user_language]=' . $key; } else { return 'serendipity[lang_selected]=' . $key; } } function &getLang($id, &$properties) { global $serendipity; static $default_lang = null; static $false = false; static $true = true; $langs = array(); // list/each can use references if (!is_array($properties)) { return $false; } while(list($key,) = each($properties)) { preg_match('@^multilingual_body_(.+)$@', $key, $match); if (isset($match[1])) { $langs[$match[1]] = '' . $serendipity['languages'][$match[1]] . ''; } } if (count($langs) < 1) { return $false; } // retrieve the default language of the blog... if ($default_lang === null) { if (isset($serendipity['default_lang'])) { $default_lang = $serendipity['languages'][$serendipity['default_lang']]; } else { $default_lang_sql = serendipity_db_query("SELECT value FROM {$serendipity['dbPrefix']}config WHERE name = 'lang'", true, 'assoc'); if (is_array($default_lang_sql)) { $default_lang = $serendipity['languages'][$default_lang_sql['value']]; } else { $default_lang = USE_DEFAULT; } } } if (!isset($langs[$default_lang])) { $langs[$default_lang] = '' . $default_lang . ''; } $lang = implode(', ', $langs); return $lang; } //function neglang($lang) { function neglang($lang, $assert = '?!') { /* Creates the negation pattern from a two letter language identifier. */ // Negative look ahead assertion. ".*" is used because any letter except of the language string shall be allowed, without it, nothing woud ever match */ return '(' . $assert . $lang . ').*'; //return '(?!' . $lang . ').*'; //return '[^'.$lang[0].'][^'.$lang[1].']'; } function strip_langs($msg) { global $serendipity; if (!preg_match('@{{@', $msg)) return $msg; $language = $serendipity['lang']; /* Handle escaping of {} chars. If someone is up for it, they're welcome to try and find a better way. As it is, this appears to work. */ $msg = str_replace('\{', chr(1), $msg); $msg = str_replace('\}', chr(2), $msg); // The explode actually makes sure that each latter array part will end on either the full string end or {{--}}. {{--}} will also never be contained inside the string, so we don't need to rule it out any longer. $parts = explode('{{--}}', $msg); $out = ''; // Iterate each subblock and inspect if its language matches. foreach($parts AS $idx => $match) { if (empty($match)) continue; // Last block part, skip it. if (stristr($match, '{{!' . $serendipity['lang'] . '}}')) { // Current language found. Keep the string, minus the {{!xx}} part. $out .= preg_replace('@\{\{!' . $serendipity['lang'] . '\}\}@', '', $match); } else { // Current language not found. Remove everything after {{!xx}}. $out .= preg_replace('@\{\{![^\}]+\}\}.+$@', '', $match); } } $msg = $out; /* Put back escaped {} chars */ $msg = str_replace(chr(1), '{', $msg); $msg = str_replace(chr(2), '}', $msg); return $msg; } function tag_title() { global $serendipity; if (serendipity_db_bool($this->get_config('tagged_title', 'true'))) { if ($serendipity['smarty']) { $serendipity['smarty']->assign('blogTitle',$this->strip_langs($serendipity['blogTitle'])); $serendipity['smarty']->assign('blogDescription',$this->strip_langs($serendipity['blogDescription'])); $head_title = $serendipity['smarty']->get_template_vars('head_title'); if (!empty($head_title)) { $serendipity['smarty']->assign('head_title',$this->strip_langs($head_title)); } $head_subtitle = $serendipity['smarty']->get_template_vars('head_subtitle'); if (!empty($head_subtitle)) { $serendipity['smarty']->assign('head_subtitle',$this->strip_langs($head_subtitle)); } } else { $serendipity['blogTitle'] = $this->strip_langs($serendipity['blogTitle']); $serendipity['blogDescription'] = $this->strip_langs($serendipity['blogDescription']); } } } 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': $this->tag_title(); if ($serendipity['smarty']) { $serendipity['smarty']->register_modifier('multilingual_lang', array($this, 'strip_lang')); } 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; } get_config('placement', 'add_footer'); $msg = '