(.+)|ms', $res, $m)) {
+ $p_content = $m[1];
+ }
+
+ call_user_func($open_f, $p_title, $p_head);
+ echo $p_content;
+ if (!$popup) {
+ // Add direct links to plugin settings if any
+ $settings = adminModulesList::getSettingsUrls($core, $p, true, false);
+ if (!empty($settings)) {
+ echo '' . implode(' - ', $settings) . '
';
+ }
+ }
+ call_user_func($close_f);
+} else {
+ call_user_func($open_f, __('Plugin not found'), '',
+ dcPage::breadcrumb(
+ [
+ __('System') => '',
+ __('Plugin not found') => ''
+ ])
+ );
+
+ echo '' . __('The plugin you reached does not exist or does not have an admin page.') . '
';
+
+ call_user_func($close_f);
+}
diff --git a/dotclear._no/admin/plugins.php b/dotclear._no/admin/plugins.php
new file mode 100644
index 0000000..b35ba55
--- /dev/null
+++ b/dotclear._no/admin/plugins.php
@@ -0,0 +1,260 @@
+plugins,
+ DC_PLUGINS_ROOT,
+ $core->blog->settings->system->store_plugin_url,
+ !empty($_GET['nocache'])
+);
+
+adminModulesList::$allow_multi_install = (boolean) DC_ALLOW_MULTI_MODULES;
+adminModulesList::$distributed_modules = explode(',', DC_DISTRIB_PLUGINS);
+
+if ($core->plugins->disableDepModules($core->adminurl->get('admin.plugins', []))) {
+ exit;
+}
+
+# -- Display module configuration page --
+if ($list->setConfiguration()) {
+
+ # Get content before page headers
+ include $list->includeConfiguration();
+
+ # Gather content
+ $list->getConfiguration();
+
+ # Display page
+ dcPage::open(__('Plugins management'),
+
+ # --BEHAVIOR-- pluginsToolsHeaders
+ $core->callBehavior('pluginsToolsHeaders', $core, true),
+
+ dcPage::breadcrumb(
+ [
+ html::escapeHTML($core->blog->name) => '',
+ __('Plugins management') => $list->getURL('', false),
+ '' . __('Plugin configuration') . ' ' => ''
+ ])
+ );
+
+ # Display previously gathered content
+ $list->displayConfiguration();
+
+ dcPage::helpBlock('core_plugins_conf');
+ dcPage::close();
+
+ # Stop reading code here
+ return;
+}
+
+# -- Execute actions --
+try {
+ $list->doActions();
+} catch (Exception $e) {
+ $core->error->add($e->getMessage());
+}
+
+# -- Plugin install --
+$plugins_install = null;
+if (!$core->error->flag()) {
+ $plugins_install = $core->plugins->installModules();
+}
+
+# -- Page header --
+dcPage::open(__('Plugins management'),
+ dcPage::jsLoad('js/_plugins.js') .
+ dcPage::jsPageTabs() .
+
+ # --BEHAVIOR-- pluginsToolsHeaders
+ $core->callBehavior('pluginsToolsHeaders', $core, false),
+
+ dcPage::breadcrumb(
+ [
+ __('System') => '',
+ __('Plugins management') => ''
+ ])
+);
+
+# -- Plugins install messages --
+if (!empty($plugins_install['success'])) {
+ echo
+ '' . __('Following plugins have been installed:') . '
';
+
+ foreach ($plugins_install['success'] as $k => $v) {
+ echo
+ '' . $k . ' ';
+ }
+
+ echo
+ ' ';
+}
+if (!empty($plugins_install['failure'])) {
+ echo
+ '' . __('Following plugins have not been installed:') . '
';
+
+ foreach ($plugins_install['failure'] as $k => $v) {
+ echo
+ '' . $k . ' (' . $v . ') ';
+ }
+
+ echo
+ ' ';
+}
+
+# -- Display modules lists --
+if ($core->auth->isSuperAdmin()) {
+
+ if (!$core->error->flag()) {
+ if (!empty($_GET['nocache'])) {
+ dcPage::success(__('Manual checking of plugins update done successfully.'));
+ }
+ }
+
+ # Updated modules from repo
+ $modules = $list->store->get(true);
+ if (!empty($modules)) {
+ echo
+ '' .
+ '
' . html::escapeHTML(__('Update plugins')) . ' ' .
+ '
' . sprintf(
+ __('There is one plugin to update available from repository.', 'There are %s plugins to update available from repository.', count($modules)),
+ count($modules)
+ ) . '
';
+
+ $list
+ ->setList('plugin-update')
+ ->setTab('update')
+ ->setModules($modules)
+ ->displayModules(
+ /*cols */['checkbox', 'icon', 'name', 'version', 'current_version', 'desc'],
+ /* actions */['update']
+ );
+
+ echo
+ '
' . sprintf(
+ __("Visit %s repository, the resources center for Dotclear."),
+ 'Dotaddict '
+ ) .
+ '
' .
+
+ '
';
+ } else {
+ echo
+ '';
+ }
+}
+
+echo
+'';
+
+# Activated modules
+$modules = $list->modules->getModules();
+if (!empty($modules)) {
+
+ echo
+ '
' . ($core->auth->isSuperAdmin() ? __('Activated plugins') : __('Installed plugins')) . ' ' .
+ '
' . __('You can configure and manage installed plugins from this list.') . '
';
+
+ $list
+ ->setList('plugin-activate')
+ ->setTab('plugins')
+ ->setModules($modules)
+ ->displayModules(
+ /* cols */['expander', 'icon', 'name', 'version', 'desc', 'distrib', 'deps'],
+ /* actions */['deactivate', 'delete', 'behavior']
+ );
+}
+
+# Deactivated modules
+if ($core->auth->isSuperAdmin()) {
+ $modules = $list->modules->getDisabledModules();
+ if (!empty($modules)) {
+ echo
+ '
' . __('Deactivated plugins') . ' ' .
+ '
' . __('Deactivated plugins are installed but not usable. You can activate them from here.') . '
';
+
+ $list
+ ->setList('plugin-deactivate')
+ ->setTab('plugins')
+ ->setModules($modules)
+ ->displayModules(
+ /* cols */['expander', 'icon', 'name', 'version', 'desc', 'distrib'],
+ /* actions */['activate', 'delete']
+ );
+ }
+}
+
+echo
+ '
';
+
+if ($core->auth->isSuperAdmin() && $list->isWritablePath()) {
+
+ # New modules from repo
+ $search = $list->getSearch();
+ $modules = $search ? $list->store->search($search) : $list->store->get();
+
+ if (!empty($search) || !empty($modules)) {
+ echo
+ '' .
+ '
' . __('Add plugins from repository') . ' ';
+
+ $list
+ ->setList('plugin-new')
+ ->setTab('new')
+ ->setModules($modules)
+ ->displaySearch()
+ ->displayIndex()
+ ->displayModules(
+ /* cols */['expander', 'name', 'score', 'version', 'desc', 'deps'],
+ /* actions */['install'],
+ /* nav limit */true
+ );
+
+ echo
+ '
' . sprintf(
+ __("Visit %s repository, the resources center for Dotclear."),
+ 'Dotaddict '
+ ) .
+ '
' .
+
+ '
';
+ }
+
+ # Add a new plugin
+ echo
+ '' .
+ '
' . __('Add plugins from a package') . ' ' .
+ '
' . __('You can install plugins by uploading or downloading zip files.') . '
';
+
+ $list->displayManualForm();
+
+ echo
+ '
';
+}
+
+# --BEHAVIOR-- pluginsToolsTabs
+$core->callBehavior('pluginsToolsTabs', $core);
+
+# -- Notice for super admin --
+if ($core->auth->isSuperAdmin() && !$list->isWritablePath()) {
+ echo
+ '' . __('Some functions are disabled, please give write access to your plugins directory to enable them.') . '
';
+}
+
+dcPage::helpBlock('core_plugins');
+dcPage::close();
diff --git a/dotclear._no/admin/popup_link.php b/dotclear._no/admin/popup_link.php
new file mode 100644
index 0000000..58efc28
--- /dev/null
+++ b/dotclear._no/admin/popup_link.php
@@ -0,0 +1,46 @@
+callBehavior('adminPopupLink', $plugin_id));
+
+echo '' . __('Add a link') . ' ';
+
+# Languages combo
+$rs = $core->blog->getLangs(['order' => 'asc']);
+$lang_combo = dcAdminCombos::getLangsCombo($rs, true);
+
+echo
+'' .
+
+'' . __('Cancel') . ' - ' .
+'' . __('Insert') . '
' . "\n";
+
+dcPage::closePopup();
diff --git a/dotclear._no/admin/popup_posts.php b/dotclear._no/admin/popup_posts.php
new file mode 100644
index 0000000..3b1dc1c
--- /dev/null
+++ b/dotclear._no/admin/popup_posts.php
@@ -0,0 +1,77 @@
+getPostTypes();
+foreach ($post_types as $k => $v) {
+ $type_combo[__($k)] = (string) $k;
+}
+if (!in_array($type, $type_combo)) {
+ $type = null;
+}
+
+$params = [];
+$params['limit'] = [(($page - 1) * $nb_per_page), $nb_per_page];
+$params['no_content'] = true;
+$params['order'] = 'post_dt DESC';
+
+if ($q) {
+ $params['search'] = $q;
+}
+
+if ($type) {
+ $params['post_type'] = $type;
+}
+
+dcPage::openPopup(__('Add a link to an entry'),
+ dcPage::jsLoad('js/_posts_list.js') .
+ dcPage::jsLoad('js/_popup_posts.js') .
+ $core->callBehavior('adminPopupPosts', $plugin_id));
+
+echo '' . __('Add a link to an entry') . ' ';
+
+echo '';
+
+echo '';
+
+try {
+ $posts = $core->blog->getPosts($params);
+ $counter = $core->blog->getPosts($params, true);
+ $post_list = new adminPostMiniList($core, $posts, $counter->f(0));
+} catch (Exception $e) {
+ $core->error->add($e->getMessage());
+}
+
+echo ''; # I know it's not a form but we just need the ID
+$post_list->display($page, $nb_per_page);
+echo '
';
+
+echo '' . __('cancel') . '
';
+
+dcPage::closePopup();
diff --git a/dotclear._no/admin/post.php b/dotclear._no/admin/post.php
new file mode 100644
index 0000000..2b2df7e
--- /dev/null
+++ b/dotclear._no/admin/post.php
@@ -0,0 +1,934 @@
+auth->getOption('post_format');
+$post_editor = $core->auth->getOption('editor');
+$post_password = '';
+$post_url = '';
+$post_lang = $core->auth->getInfo('user_lang');
+$post_title = '';
+$post_excerpt = '';
+$post_excerpt_xhtml = '';
+$post_content = '';
+$post_content_xhtml = '';
+$post_notes = '';
+$post_status = $core->auth->getInfo('user_post_status');
+$post_selected = false;
+$post_open_comment = $core->blog->settings->system->allow_comments;
+$post_open_tb = $core->blog->settings->system->allow_trackbacks;
+
+$page_title = __('New post');
+
+$can_view_page = true;
+$can_edit_post = $core->auth->check('usage,contentadmin', $core->blog->id);
+$can_publish = $core->auth->check('publish,contentadmin', $core->blog->id);
+$can_delete = false;
+
+$post_headlink = ' ';
+$post_link = '%s ';
+$next_link = $prev_link = $next_headlink = $prev_headlink = null;
+
+# If user can't publish
+if (!$can_publish) {
+ $post_status = -2;
+}
+
+# Getting categories
+$categories_combo = dcAdminCombos::getCategoriesCombo(
+ $core->blog->getCategories()
+);
+
+$status_combo = dcAdminCombos::getPostStatusesCombo();
+
+$img_status_pattern = ' ';
+
+# Formats combo
+$core_formaters = $core->getFormaters();
+$available_formats = ['' => ''];
+foreach ($core_formaters as $editor => $formats) {
+ foreach ($formats as $format) {
+ $available_formats[$format] = $format;
+ }
+}
+
+# Languages combo
+$rs = $core->blog->getLangs(['order' => 'asc']);
+$lang_combo = dcAdminCombos::getLangsCombo($rs, true);
+
+# Validation flag
+$bad_dt = false;
+
+# Trackbacks
+$TB = new dcTrackback($core);
+$tb_urls = $tb_excerpt = '';
+
+# Get entry informations
+if (!empty($_REQUEST['id'])) {
+ $page_title = __('Edit post');
+
+ $params['post_id'] = $_REQUEST['id'];
+
+ $post = $core->blog->getPosts($params);
+
+ if ($post->isEmpty()) {
+ $core->error->add(__('This entry does not exist.'));
+ $can_view_page = false;
+ } else {
+ $post_id = $post->post_id;
+ $cat_id = $post->cat_id;
+ $post_dt = date('Y-m-d H:i', strtotime($post->post_dt));
+ $post_format = $post->post_format;
+ $post_password = $post->post_password;
+ $post_url = $post->post_url;
+ $post_lang = $post->post_lang;
+ $post_title = $post->post_title;
+ $post_excerpt = $post->post_excerpt;
+ $post_excerpt_xhtml = $post->post_excerpt_xhtml;
+ $post_content = $post->post_content;
+ $post_content_xhtml = $post->post_content_xhtml;
+ $post_notes = $post->post_notes;
+ $post_status = $post->post_status;
+ $post_selected = (boolean) $post->post_selected;
+ $post_open_comment = (boolean) $post->post_open_comment;
+ $post_open_tb = (boolean) $post->post_open_tb;
+
+ $can_edit_post = $post->isEditable();
+ $can_delete = $post->isDeletable();
+
+ $next_rs = $core->blog->getNextPost($post, 1);
+ $prev_rs = $core->blog->getNextPost($post, -1);
+
+ if ($next_rs !== null) {
+ $next_link = sprintf($post_link, $next_rs->post_id,
+ html::escapeHTML($next_rs->post_title), __('Next entry') . ' »');
+ $next_headlink = sprintf($post_headlink, 'next',
+ html::escapeHTML($next_rs->post_title), $next_rs->post_id);
+ }
+
+ if ($prev_rs !== null) {
+ $prev_link = sprintf($post_link, $prev_rs->post_id,
+ html::escapeHTML($prev_rs->post_title), '« ' . __('Previous entry'));
+ $prev_headlink = sprintf($post_headlink, 'previous',
+ html::escapeHTML($prev_rs->post_title), $prev_rs->post_id);
+ }
+
+ try {
+ $core->media = new dcMedia($core);
+ } catch (Exception $e) {
+ $core->error->add($e->getMessage());
+ }
+
+ # Sanitize trackbacks excerpt
+ $tb_excerpt = empty($_POST['tb_excerpt']) ?
+ $post_excerpt_xhtml . ' ' . $post_content_xhtml :
+ $_POST['tb_excerpt'];
+ $tb_excerpt = html::decodeEntities(html::clean($tb_excerpt));
+ $tb_excerpt = text::cutString(html::escapeHTML($tb_excerpt), 255);
+ $tb_excerpt = preg_replace('/\s+/ms', ' ', $tb_excerpt);
+ }
+}
+if (isset($_REQUEST['section']) && $_REQUEST['section'] == 'trackbacks') {
+ $anchor = 'trackbacks';
+} else {
+ $anchor = 'comments';
+}
+
+$comments_actions_page = new dcCommentsActionsPage($core, $core->adminurl->get('admin.post'), ['id' => $post_id, '_ANCHOR' => $anchor, 'section' => $anchor]);
+
+if ($comments_actions_page->process()) {
+ return;
+}
+
+# Ping blogs
+if (!empty($_POST['ping'])) {
+ if (!empty($_POST['tb_urls']) && $post_id && $post_status == 1 && $can_edit_post) {
+ $tb_urls = $_POST['tb_urls'];
+ $tb_urls = str_replace("\r", '', $tb_urls);
+ $tb_post_title = html::escapeHTML(trim(html::clean($post_title)));
+ $tb_post_url = $post->getURL();
+
+ foreach (explode("\n", $tb_urls) as $tb_url) {
+ try {
+ # --BEHAVIOR-- adminBeforePingTrackback
+ $core->callBehavior('adminBeforePingTrackback', $tb_url, $post_id, $tb_post_title, $tb_excerpt, $tb_post_url);
+
+ $TB->ping($tb_url, $post_id, $tb_post_title, $tb_excerpt, $tb_post_url);
+ } catch (Exception $e) {
+ $core->error->add($e->getMessage());
+ }
+ }
+
+ if (!$core->error->flag()) {
+ dcPage::addSuccessNotice(__('All pings sent.'));
+ $core->adminurl->redirect(
+ 'admin.post',
+ ['id' => $post_id, 'tb' => '1']
+ );
+ }
+ }
+}
+
+# Format excerpt and content
+elseif (!empty($_POST) && $can_edit_post) {
+ $post_format = $_POST['post_format'];
+ $post_excerpt = $_POST['post_excerpt'];
+ $post_content = $_POST['post_content'];
+
+ $post_title = $_POST['post_title'];
+
+ $cat_id = (integer) $_POST['cat_id'];
+
+ if (isset($_POST['post_status'])) {
+ $post_status = (integer) $_POST['post_status'];
+ }
+
+ if (empty($_POST['post_dt'])) {
+ $post_dt = '';
+ } else {
+ try
+ {
+ $post_dt = strtotime($_POST['post_dt']);
+ if ($post_dt == false || $post_dt == -1) {
+ $bad_dt = true;
+ throw new Exception(__('Invalid publication date'));
+ }
+ $post_dt = date('Y-m-d H:i', $post_dt);
+ } catch (Exception $e) {
+ $core->error->add($e->getMessage());
+ }
+ }
+
+ $post_open_comment = !empty($_POST['post_open_comment']);
+ $post_open_tb = !empty($_POST['post_open_tb']);
+ $post_selected = !empty($_POST['post_selected']);
+ $post_lang = $_POST['post_lang'];
+ $post_password = !empty($_POST['post_password']) ? $_POST['post_password'] : null;
+
+ $post_notes = $_POST['post_notes'];
+
+ if (isset($_POST['post_url'])) {
+ $post_url = $_POST['post_url'];
+ }
+
+ $core->blog->setPostContent(
+ $post_id, $post_format, $post_lang,
+ $post_excerpt, $post_excerpt_xhtml, $post_content, $post_content_xhtml
+ );
+}
+
+# Delete post
+if (!empty($_POST['delete']) && $can_delete) {
+ try {
+ # --BEHAVIOR-- adminBeforePostDelete
+ $core->callBehavior('adminBeforePostDelete', $post_id);
+ $core->blog->delPost($post_id);
+ $core->adminurl->redirect("admin.posts");
+ } catch (Exception $e) {
+ $core->error->add($e->getMessage());
+ }
+}
+
+# Create or update post
+if (!empty($_POST) && !empty($_POST['save']) && $can_edit_post && !$bad_dt) {
+ # Create category
+ if (!empty($_POST['new_cat_title']) && $core->auth->check('categories', $core->blog->id)) {
+
+ $cur_cat = $core->con->openCursor($core->prefix . 'category');
+ $cur_cat->cat_title = $_POST['new_cat_title'];
+ $cur_cat->cat_url = '';
+
+ $parent_cat = !empty($_POST['new_cat_parent']) ? $_POST['new_cat_parent'] : '';
+
+ # --BEHAVIOR-- adminBeforeCategoryCreate
+ $core->callBehavior('adminBeforeCategoryCreate', $cur_cat);
+
+ $cat_id = $core->blog->addCategory($cur_cat, (integer) $parent_cat);
+
+ # --BEHAVIOR-- adminAfterCategoryCreate
+ $core->callBehavior('adminAfterCategoryCreate', $cur_cat, $cat_id);
+ }
+
+ $cur = $core->con->openCursor($core->prefix . 'post');
+
+ $cur->post_title = $post_title;
+ $cur->cat_id = ($cat_id ?: null);
+ $cur->post_dt = $post_dt ? date('Y-m-d H:i:00', strtotime($post_dt)) : '';
+ $cur->post_format = $post_format;
+ $cur->post_password = $post_password;
+ $cur->post_lang = $post_lang;
+ $cur->post_title = $post_title;
+ $cur->post_excerpt = $post_excerpt;
+ $cur->post_excerpt_xhtml = $post_excerpt_xhtml;
+ $cur->post_content = $post_content;
+ $cur->post_content_xhtml = $post_content_xhtml;
+ $cur->post_notes = $post_notes;
+ $cur->post_status = $post_status;
+ $cur->post_selected = (integer) $post_selected;
+ $cur->post_open_comment = (integer) $post_open_comment;
+ $cur->post_open_tb = (integer) $post_open_tb;
+
+ if (isset($_POST['post_url'])) {
+ $cur->post_url = $post_url;
+ }
+
+ # Update post
+ if ($post_id) {
+ try {
+ # --BEHAVIOR-- adminBeforePostUpdate
+ $core->callBehavior('adminBeforePostUpdate', $cur, $post_id);
+
+ $core->blog->updPost($post_id, $cur);
+
+ # --BEHAVIOR-- adminAfterPostUpdate
+ $core->callBehavior('adminAfterPostUpdate', $cur, $post_id);
+ dcPage::addSuccessNotice(sprintf(__('The post "%s" has been successfully updated'), html::escapeHTML($cur->post_title)));
+ $core->adminurl->redirect(
+ 'admin.post',
+ ['id' => $post_id]
+ );
+ } catch (Exception $e) {
+ $core->error->add($e->getMessage());
+ }
+ } else {
+ $cur->user_id = $core->auth->userID();
+
+ try {
+ # --BEHAVIOR-- adminBeforePostCreate
+ $core->callBehavior('adminBeforePostCreate', $cur);
+
+ $return_id = $core->blog->addPost($cur);
+
+ # --BEHAVIOR-- adminAfterPostCreate
+ $core->callBehavior('adminAfterPostCreate', $cur, $return_id);
+
+ dcPage::addSuccessNotice(__('Entry has been successfully created.'));
+ $core->adminurl->redirect(
+ 'admin.post',
+ ['id' => $return_id]
+ );
+ } catch (Exception $e) {
+ $core->error->add($e->getMessage());
+ }
+ }
+}
+
+# Getting categories
+$categories_combo = dcAdminCombos::getCategoriesCombo(
+ $core->blog->getCategories()
+);
+/* DISPLAY
+-------------------------------------------------------- */
+$default_tab = 'edit-entry';
+if (!$can_edit_post) {
+ $default_tab = '';
+}
+if (!empty($_GET['co'])) {
+ $default_tab = 'comments';
+} elseif (!empty($_GET['tb'])) {
+ $default_tab = 'trackbacks';
+}
+
+if ($post_id) {
+ switch ($post_status) {
+ case 1:
+ $img_status = sprintf($img_status_pattern, __('Published'), 'check-on.png');
+ break;
+ case 0:
+ $img_status = sprintf($img_status_pattern, __('Unpublished'), 'check-off.png');
+ break;
+ case -1:
+ $img_status = sprintf($img_status_pattern, __('Scheduled'), 'scheduled.png');
+ break;
+ case -2:
+ $img_status = sprintf($img_status_pattern, __('Pending'), 'check-wrn.png');
+ break;
+ default:
+ $img_status = '';
+ }
+ $edit_entry_str = __('“%s”');
+ $page_title_edit = sprintf($edit_entry_str, html::escapeHTML($post_title)) . ' ' . $img_status;
+} else {
+ $img_status = '';
+}
+
+$admin_post_behavior = '';
+if ($post_editor) {
+ $p_edit = $c_edit = '';
+ if (!empty($post_editor[$post_format])) {
+ $p_edit = $post_editor[$post_format];
+ }
+ if (!empty($post_editor['xhtml'])) {
+ $c_edit = $post_editor['xhtml'];
+ }
+ if ($p_edit == $c_edit) {
+ $admin_post_behavior .= $core->callBehavior('adminPostEditor',
+ $p_edit, 'post', ['#post_excerpt', '#post_content', '#comment_content'], $post_format);
+ } else {
+ $admin_post_behavior .= $core->callBehavior('adminPostEditor',
+ $p_edit, 'post', ['#post_excerpt', '#post_content'], $post_format);
+ $admin_post_behavior .= $core->callBehavior('adminPostEditor',
+ $c_edit, 'comment', ['#comment_content'], 'xhtml');
+ }
+}
+
+dcPage::open($page_title . ' - ' . __('Posts'),
+ dcPage::jsDatePicker() .
+ dcPage::jsModal() .
+ dcPage::jsMetaEditor() .
+ $admin_post_behavior .
+ dcPage::jsLoad('js/_post.js') .
+ dcPage::jsConfirmClose('entry-form', 'comment-form') .
+ # --BEHAVIOR-- adminPostHeaders
+ $core->callBehavior('adminPostHeaders') .
+ dcPage::jsPageTabs($default_tab) .
+ $next_headlink . "\n" . $prev_headlink,
+ dcPage::breadcrumb(
+ [
+ html::escapeHTML($core->blog->name) => '',
+ __('Posts') => $core->adminurl->get("admin.posts"),
+ ($post_id ? $page_title_edit : $page_title) => ''
+ ])
+ , [
+ 'x-frame-allow' => $core->blog->url
+ ]
+);
+
+if (!empty($_GET['upd'])) {
+ dcPage::success(__('Entry has been successfully updated.'));
+} elseif (!empty($_GET['crea'])) {
+ dcPage::success(__('Entry has been successfully created.'));
+} elseif (!empty($_GET['attached'])) {
+ dcPage::success(__('File has been successfully attached.'));
+} elseif (!empty($_GET['rmattach'])) {
+ dcPage::success(__('Attachment has been successfully removed.'));
+}
+
+if (!empty($_GET['creaco'])) {
+ dcPage::success(__('Comment has been successfully created.'));
+}
+if (!empty($_GET['tbsent'])) {
+ dcPage::success(__('All pings sent.'));
+}
+
+# XHTML conversion
+if (!empty($_GET['xconv'])) {
+ $post_excerpt = $post_excerpt_xhtml;
+ $post_content = $post_content_xhtml;
+ $post_format = 'xhtml';
+
+ dcPage::message(__('Don\'t forget to validate your XHTML conversion by saving your post.'));
+}
+
+if ($post_id && $post->post_status == 1) {
+ echo '' . __('Go to this entry on the site') . '
';
+}
+if ($post_id) {
+ echo '';
+ if ($prev_link) {echo $prev_link;}
+ if ($next_link && $prev_link) {echo ' | ';}
+ if ($next_link) {echo $next_link;}
+
+ # --BEHAVIOR-- adminPostNavLinks
+ $core->callBehavior('adminPostNavLinks', isset($post) ? $post : null, 'post');
+
+ echo '
';
+}
+
+# Exit if we cannot view page
+if (!$can_view_page) {
+ dcPage::helpBlock('core_post');
+ dcPage::close();
+ exit;
+}
+
+# Controls comments or trakbacks capabilities
+$isContributionAllowed = function ($id, $dt, $com = true) {
+ global $core;
+
+ if (!$id) {
+ return true;
+ }
+ if ($com) {
+ if (($core->blog->settings->system->comments_ttl == 0) ||
+ (time() - $core->blog->settings->system->comments_ttl * 86400 < $dt)) {
+ return true;
+ }
+ } else {
+ if (($core->blog->settings->system->trackbacks_ttl == 0) ||
+ (time() - $core->blog->settings->system->trackbacks_ttl * 86400 < $dt)) {
+ return true;
+ }
+ }
+ return false;
+};
+
+# Show comments or trackbacks
+$showComments = function ($rs, $has_action, $tb = false) {
+ global $core;
+ echo
+ '' .
+ '
';
+};
+
+/* Post form if we can edit post
+-------------------------------------------------------- */
+if ($can_edit_post) {
+ $sidebar_items = new ArrayObject([
+ 'status-box' => [
+ 'title' => __('Status'),
+ 'items' => [
+ 'post_status' =>
+ '' . __('Entry status') . ' ' . $img_status . ' ' .
+ form::combo('post_status', $status_combo,
+ ['default' => $post_status, 'class' => 'maximal', 'disabled' => !$can_publish]) .
+ '
',
+ 'post_dt' =>
+ '' . __('Publication date and hour') . ' ' .
+ form::field('post_dt', 16, 16, $post_dt, ($bad_dt ? 'invalid' : '')) .
+ /*
+ Previous line will be replaced by this one as soon as every browser will support datetime-local input type
+ Dont forget to remove call to datepicker in post.js
+
+ form::datetime('post_dt', [
+ 'default' => html::escapeHTML(dt::str('%Y-%m-%dT%H:%M', strtotime($post_dt))),
+ 'class' => ($bad_dt ? 'invalid' : '')
+ ]) .
+ */
+ '
',
+ 'post_lang' =>
+ '' . __('Entry language') . ' ' .
+ form::combo('post_lang', $lang_combo, $post_lang) .
+ '
',
+ 'post_format' =>
+ '']],
+ 'metas-box' => [
+ 'title' => __('Filing'),
+ 'items' => [
+ 'post_selected' =>
+ '' .
+ form::checkbox('post_selected', 1, $post_selected) . ' ' .
+ __('Selected entry') . '
',
+ 'cat_id' =>
+ '' .
+ '
' . __('Category') . ' ' .
+ '
' . __('Category:') . ' ' .
+ form::combo('cat_id', $categories_combo, $cat_id, 'maximal') .
+ '
' .
+ ($core->auth->check('categories', $core->blog->id) ?
+ '
' .
+ '
' . __('Add a new category') . ' ' .
+ '
' . __('Title:') . ' ' .
+ form::field('new_cat_title', 30, 255, ['class' => 'maximal']) . '
' .
+ '
' . __('Parent:') . ' ' .
+ form::combo('new_cat_parent', $categories_combo, '', 'maximal') .
+ '
' .
+ '
'
+ : '') .
+ '
']],
+ 'options-box' => [
+ 'title' => __('Options'),
+ 'items' => [
+ 'post_open_comment_tb' =>
+ '' .
+ '' .
+ '
' .
+ form::checkbox('post_open_comment', 1, $post_open_comment) . ' ' .
+ __('Accept comments') . '
' .
+ ($core->blog->settings->system->allow_comments ?
+ ($isContributionAllowed($post_id, strtotime($post_dt), true) ?
+ '' :
+ '
' .
+ __('Warning: Comments are not more accepted for this entry.') . '
') :
+ '
' .
+ __('Comments are not accepted on this blog so far.') . '
') .
+ '
' .
+ form::checkbox('post_open_tb', 1, $post_open_tb) . ' ' .
+ __('Accept trackbacks') . '
' .
+ ($core->blog->settings->system->allow_trackbacks ?
+ ($isContributionAllowed($post_id, strtotime($post_dt), false) ?
+ '' :
+ '
' .
+ __('Warning: Trackbacks are not more accepted for this entry.') . '
') :
+ '
' . __('Trackbacks are not accepted on this blog so far.') . '
') .
+ '
',
+ 'post_password' =>
+ '' . __('Password') . ' ' .
+ form::field('post_password', 10, 32, html::escapeHTML($post_password), 'maximal') .
+ '
',
+ 'post_url' =>
+ '' .
+ '
' . __('Edit basename') . ' ' .
+ form::field('post_url', 10, 255, html::escapeHTML($post_url), 'maximal') .
+ '
' .
+ '
' .
+ __('Warning: If you set the URL manually, it may conflict with another entry.') .
+ '
'
+ ]]]);
+
+ $main_items = new ArrayObject([
+ "post_title" =>
+ '' .
+ '* ' . __('Title:') . ' ' .
+ form::field('post_title', 20, 255, [
+ 'default' => html::escapeHTML($post_title),
+ 'class' => 'maximal',
+ 'extra_html' => 'required placeholder="' . __('Title') . '" lang="' . $post_lang . '" spellcheck="true"'
+ ]) .
+ '
',
+
+ "post_excerpt" =>
+ '' . __('Excerpt:') . ' ' .
+ __('Introduction to the post.') . ' ' .
+ form::textarea('post_excerpt', 50, 5,
+ [
+ 'default' => html::escapeHTML($post_excerpt),
+ 'extra_html' => 'lang="' . $post_lang . '" spellcheck="true"'
+ ]) .
+ '
',
+
+ "post_content" =>
+ '* ' . __('Content:') . ' ' .
+ form::textarea('post_content', 50, $core->auth->getOption('edit_size'),
+ [
+ 'default' => html::escapeHTML($post_content),
+ 'extra_html' => 'required placeholder="' . __('Content') . '" lang="' . $post_lang . '" spellcheck="true"'
+ ]) .
+ '
',
+
+ "post_notes" =>
+ '' . __('Personal notes:') . ' ' .
+ __('Unpublished notes.') . ' ' .
+ form::textarea('post_notes', 50, 5,
+ [
+ 'default' => html::escapeHTML($post_notes),
+ 'extra_html' => 'lang="' . $post_lang . '" spellcheck="true"'
+ ]) .
+ '
'
+ ]
+ );
+
+ # --BEHAVIOR-- adminPostFormItems
+ $core->callBehavior('adminPostFormItems', $main_items, $sidebar_items, isset($post) ? $post : null, 'post');
+
+ echo '';
+ echo '
';
+
+ # --BEHAVIOR-- adminPostForm
+ $core->callBehavior('adminPostAfterForm', isset($post) ? $post : null, 'post');
+
+ echo '
';
+}
+
+if ($post_id) {
+ /* Comments
+ -------------------------------------------------------- */
+
+ $params = ['post_id' => $post_id, 'order' => 'comment_dt ASC'];
+
+ $comments = $core->blog->getComments(array_merge($params, ['comment_trackback' => 0]));
+
+ echo
+ ''; #comments
+}
+
+if ($post_id && $post_status == 1) {
+ /* Trackbacks
+ -------------------------------------------------------- */
+
+ $params = ['post_id' => $post_id, 'order' => 'comment_dt ASC'];
+ $trackbacks = $core->blog->getComments(array_merge($params, ['comment_trackback' => 1]));
+
+ # Actions combo box
+ $combo_action = $comments_actions_page->getCombo();
+ $has_action = !empty($combo_action) && !$trackbacks->isEmpty();
+
+ if (!empty($_GET['tb_auto'])) {
+ $tb_urls = implode("\n", $TB->discover($post_excerpt_xhtml . ' ' . $post_content_xhtml));
+ }
+
+ # Display tab
+ echo
+ '';
+
+ # tracbacks actions
+ if ($has_action) {
+ echo '
';
+ }
+
+ /* Add trackbacks
+ -------------------------------------------------------- */
+ if ($can_edit_post && $post->post_status) {
+ echo
+ '
';
+
+ echo
+ '
' . __('Ping blogs') . ' ' .
+ '
';
+
+ $pings = $TB->getPostPings($post_id);
+
+ if (!$pings->isEmpty()) {
+ echo '
' . __('Previously sent pings') . ' ';
+
+ echo '
';
+ while ($pings->fetch()) {
+ echo
+ '' . dt::dt2str(__('%Y-%m-%d %H:%M'), $pings->ping_dt) . ' - ' .
+ $pings->ping_url . ' ';
+ }
+ echo ' ';
+ }
+
+ echo '
';
+ }
+
+ echo '
'; #trackbacks
+}
+
+dcPage::helpBlock('core_post', 'core_trackbacks', 'core_wiki');
+dcPage::close();
diff --git a/dotclear._no/admin/post_media.php b/dotclear._no/admin/post_media.php
new file mode 100644
index 0000000..2b2f0e5
--- /dev/null
+++ b/dotclear._no/admin/post_media.php
@@ -0,0 +1,79 @@
+blog->getPosts(['post_id' => $post_id, 'post_type' => '']);
+if ($rs->isEmpty()) {
+ exit;
+}
+
+try {
+ if ($post_id && $media_id && !empty($_REQUEST['attach'])) {
+ $core->media = new dcMedia($core);
+ $core->media->addPostMedia($post_id, $media_id, $link_type);
+ if (!empty($_SERVER['HTTP_X_REQUESTED_WITH'])) {
+ header('Content-type: application/json');
+ echo json_encode(['url' => $core->getPostAdminURL($rs->post_type, $post_id, false)]);
+ exit();
+ } else {
+ http::redirect($core->getPostAdminURL($rs->post_type, $post_id, false));
+ }
+ }
+
+ $core->media = new dcMedia($core);
+ $f = $core->media->getPostMedia($post_id, $media_id, $link_type);
+ if (empty($f)) {
+ $post_id = $media_id = null;
+ throw new Exception(__('This attachment does not exist'));
+ }
+ $f = $f[0];
+} catch (Exception $e) {
+ $core->error->add($e->getMessage());
+}
+
+# Remove a media from en
+if (($post_id && $media_id) || $core->error->flag()) {
+ if (!empty($_POST['remove'])) {
+ $core->media->removePostMedia($post_id, $media_id, $link_type);
+
+ dcPage::addSuccessNotice(__('Attachment has been successfully removed.'));
+ http::redirect($core->getPostAdminURL($rs->post_type, $post_id, false));
+ } elseif (isset($_POST['post_id'])) {
+ http::redirect($core->getPostAdminURL($rs->post_type, $post_id, false));
+ }
+
+ if (!empty($_GET['remove'])) {
+ dcPage::open(__('Remove attachment'));
+
+ echo '' . __('Attachment') . ' › ' . __('confirm removal') . ' ';
+
+ echo
+ '';
+
+ dcPage::close();
+ exit;
+ }
+}
diff --git a/dotclear._no/admin/posts.php b/dotclear._no/admin/posts.php
new file mode 100644
index 0000000..50087e5
--- /dev/null
+++ b/dotclear._no/admin/posts.php
@@ -0,0 +1,406 @@
+blog->getCategories();
+} catch (Exception $e) {
+ $core->error->add($e->getMessage());
+}
+
+# Getting authors
+try {
+ $users = $core->blog->getPostsUsers();
+} catch (Exception $e) {
+ $core->error->add($e->getMessage());
+}
+
+# Getting dates
+try {
+ $dates = $core->blog->getDates(['type' => 'month']);
+} catch (Exception $e) {
+ $core->error->add($e->getMessage());
+}
+
+# Getting langs
+try {
+ $langs = $core->blog->getLangs();
+} catch (Exception $e) {
+ $core->error->add($e->getMessage());
+}
+
+# Creating filter combo boxes
+$users_combo = dcAdminCombos::getUsersCombo($users);
+dcUtils::lexicalKeySort($users_combo);
+$users_combo = array_merge(
+ ['-' => ''],
+ $users_combo
+);
+
+$categories_combo = array_merge(
+ [
+ new formSelectOption('-', ''),
+ new formSelectOption(__('(No cat)'), 'NULL')],
+ dcAdminCombos::getCategoriesCombo($categories, false)
+);
+$categories_values = [];
+foreach ($categories_combo as $cat) {
+ if (isset($cat->value)) {
+ $categories_values[$cat->value] = true;
+ }
+}
+
+$status_combo = array_merge(
+ ['-' => ''],
+ dcAdminCombos::getPostStatusesCombo()
+);
+
+$selected_combo = [
+ '-' => '',
+ __('Selected') => '1',
+ __('Not selected') => '0'
+];
+
+$comment_combo = [
+ '-' => '',
+ __('Opened') => '1',
+ __('Closed') => '0'
+];
+
+$trackback_combo = [
+ '-' => '',
+ __('Opened') => '1',
+ __('Closed') => '0'
+];
+
+$attachment_combo = [
+ '-' => '',
+ __('With attachments') => '1',
+ __('Without attachments') => '0'
+];
+
+$password_combo = [
+ '-' => '',
+ __('With password') => '1',
+ __('Without password') => '0'
+];
+
+# Months array
+$dt_m_combo = array_merge(
+ ['-' => ''],
+ dcAdminCombos::getDatesCombo($dates)
+);
+
+$lang_combo = array_merge(
+ ['-' => ''],
+ dcAdminCombos::getLangsCombo($langs, false)
+);
+
+# Post formats
+$core_formaters = $core->getFormaters();
+$available_formats = [];
+foreach ($core_formaters as $editor => $formats) {
+ foreach ($formats as $format) {
+ $available_formats[$format] = $format;
+ }
+}
+$format_combo = array_merge(
+ ['-' => ''],
+ $available_formats
+);
+
+$sortby_combo = [
+ __('Date') => 'post_dt',
+ __('Title') => 'post_title',
+ __('Category') => 'cat_title',
+ __('Author') => 'user_id',
+ __('Status') => 'post_status',
+ __('Selected') => 'post_selected',
+ __('Number of comments') => 'nb_comment',
+ __('Number of trackbacks') => 'nb_trackback'
+];
+
+$sortby_lex = [
+ // key in sorty_combo (see above) => field in SQL request
+ 'post_title' => 'post_title',
+ 'cat_title' => 'cat_title',
+ 'user_id' => 'P.user_id'];
+
+$order_combo = [
+ __('Descending') => 'desc',
+ __('Ascending') => 'asc'
+];
+
+# Actions combo box
+
+$posts_actions_page = new dcPostsActionsPage($core, $core->adminurl->get("admin.posts"));
+
+if ($posts_actions_page->process()) {
+ return;
+}
+
+/* Get posts
+-------------------------------------------------------- */
+$user_id = !empty($_GET['user_id']) ? $_GET['user_id'] : '';
+$cat_id = !empty($_GET['cat_id']) ? $_GET['cat_id'] : '';
+$status = isset($_GET['status']) ? $_GET['status'] : '';
+$password = isset($_GET['password']) ? $_GET['password'] : '';
+$selected = isset($_GET['selected']) ? $_GET['selected'] : '';
+$comment = isset($_GET['comment']) ? $_GET['comment'] : '';
+$trackback = isset($_GET['trackback']) ? $_GET['trackback'] : '';
+$attachment = isset($_GET['attachment']) ? $_GET['attachment'] : '';
+$month = !empty($_GET['month']) ? $_GET['month'] : '';
+$lang = !empty($_GET['lang']) ? $_GET['lang'] : '';
+$format = !empty($_GET['format']) ? $_GET['format'] : '';
+$sortby = !empty($_GET['sortby']) ? $_GET['sortby'] : 'post_dt';
+$order = !empty($_GET['order']) ? $_GET['order'] : 'desc';
+
+$show_filters = false;
+
+$page = !empty($_GET['page']) ? max(1, (integer) $_GET['page']) : 1;
+$nb_per_page = 30;
+
+if (!empty($_GET['nb']) && (integer) $_GET['nb'] > 0) {
+ if ($nb_per_page != (integer) $_GET['nb']) {
+ $show_filters = true;
+ }
+ $nb_per_page = (integer) $_GET['nb'];
+}
+
+$params['limit'] = [(($page - 1) * $nb_per_page), $nb_per_page];
+$params['no_content'] = true;
+$params['where'] = '';
+
+# - User filter
+if ($user_id !== '' && in_array($user_id, $users_combo)) {
+ $params['user_id'] = $user_id;
+ $show_filters = true;
+} else {
+ $user_id = '';
+}
+
+# - Categories filter
+if ($cat_id !== '' && isset($categories_values[$cat_id])) {
+ $params['cat_id'] = $cat_id;
+ $show_filters = true;
+} else {
+ $cat_id = '';
+}
+
+# - Status filter
+if ($status !== '' && in_array($status, $status_combo)) {
+ $params['post_status'] = $status;
+ $show_filters = true;
+} else {
+ $status = '';
+}
+
+# - Password filter
+if ($password !== '' && in_array($password, $password_combo)) {
+ $params['where'] .= ' AND post_password IS ' . ($password ? 'NOT ' : '') . 'NULL ';
+ $show_filters = true;
+} else {
+ $password = '';
+}
+
+# - Selected filter
+if ($selected !== '' && in_array($selected, $selected_combo)) {
+ $params['post_selected'] = $selected;
+ $show_filters = true;
+} else {
+ $selected = '';
+}
+
+# - Comment filter
+if ($comment !== '' && in_array($comment, $comment_combo)) {
+ $params['where'] .= " AND post_open_comment = '" . $comment . "' ";
+ $show_filters = true;
+} else {
+ $comment = '';
+}
+
+# - Comment filter
+if ($trackback !== '' && in_array($trackback, $trackback_combo)) {
+ $params['where'] .= " AND post_open_tb = '" . $trackback . "' ";
+ $show_filters = true;
+} else {
+ $trackback = '';
+}
+
+# - Attachment filter
+if ($attachment !== '' && in_array($attachment, $attachment_combo)) {
+ $params['media'] = $attachment;
+ $params['link_type'] = 'attachment';
+ $show_filters = true;
+} else {
+ $attachment = '';
+}
+
+# - Month filter
+if ($month !== '' && in_array($month, $dt_m_combo)) {
+ $params['post_month'] = substr($month, 4, 2);
+ $params['post_year'] = substr($month, 0, 4);
+ $show_filters = true;
+} else {
+ $month = '';
+}
+
+# - Lang filter
+if ($lang !== '' && in_array($lang, $lang_combo)) {
+ $params['post_lang'] = $lang;
+ $show_filters = true;
+} else {
+ $lang = '';
+}
+
+# - Format filter
+if ($format !== '' && in_array($format, $format_combo)) {
+ $params['where'] .= " AND post_format = '" . $format . "' ";
+ $show_filters = true;
+} else {
+ $format = '';
+}
+
+# - Sortby and order filter
+if ($sortby !== '' && in_array($sortby, $sortby_combo)) {
+ if (array_key_exists($sortby, $sortby_lex)) {
+ $params['order'] = $core->con->lexFields($sortby_lex[$sortby]);
+ } else {
+ $params['order'] = $sortby;
+ }
+ if ($order !== '' && in_array($order, $order_combo)) {
+ $params['order'] .= ' ' . $order;
+ } else {
+ $order = 'desc';
+ }
+
+ if ($sortby != 'post_dt' || $order != 'desc') {
+ $show_filters = true;
+ }
+} else {
+ $sortby = 'post_dt';
+ $order = 'desc';
+}
+
+# Get posts
+try {
+ $posts = $core->blog->getPosts($params);
+ $counter = $core->blog->getPosts($params, true);
+ $post_list = new adminPostList($core, $posts, $counter->f(0));
+} catch (Exception $e) {
+ $core->error->add($e->getMessage());
+}
+
+/* DISPLAY
+-------------------------------------------------------- */
+
+dcPage::open(__('Posts'),
+ dcPage::jsLoad('js/_posts_list.js') . dcPage::jsFilterControl($show_filters),
+ dcPage::breadcrumb(
+ [
+ html::escapeHTML($core->blog->name) => '',
+ __('Posts') => ''
+ ])
+);
+if (!empty($_GET['upd'])) {
+ dcPage::success(__('Selected entries have been successfully updated.'));
+} elseif (!empty($_GET['del'])) {
+ dcPage::success(__('Selected entries have been successfully deleted.'));
+}
+if (!$core->error->flag()) {
+ echo
+ '' . __('New post') . '
' .
+ '';
+
+ # Show posts
+ $post_list->display($page, $nb_per_page,
+ '',
+ $show_filters
+ );
+}
+
+dcPage::helpBlock('core_posts');
+dcPage::close();
diff --git a/dotclear._no/admin/posts_actions.php b/dotclear._no/admin/posts_actions.php
new file mode 100644
index 0000000..5131133
--- /dev/null
+++ b/dotclear._no/admin/posts_actions.php
@@ -0,0 +1,33 @@
+adminurl->get("admin.posts");
+ $args = [];
+}
+
+$posts_actions_page = new dcPostsActionsPage($core, $uri, $args);
+$posts_actions_page->setEnableRedirSelection(false);
+$posts_actions_page->process();
diff --git a/dotclear._no/admin/preferences.php b/dotclear._no/admin/preferences.php
new file mode 100644
index 0000000..407919e
--- /dev/null
+++ b/dotclear._no/admin/preferences.php
@@ -0,0 +1,864 @@
+auth->getInfo('user_name');
+$user_firstname = $core->auth->getInfo('user_firstname');
+$user_displayname = $core->auth->getInfo('user_displayname');
+$user_email = $core->auth->getInfo('user_email');
+$user_url = $core->auth->getInfo('user_url');
+$user_lang = $core->auth->getInfo('user_lang');
+$user_tz = $core->auth->getInfo('user_tz');
+$user_post_status = $core->auth->getInfo('user_post_status');
+
+$user_options = $core->auth->getOptions();
+if (empty($user_options['editor']) || !is_array($user_options['editor'])) {
+ $user_options['editor'] = [];
+}
+
+$core->auth->user_prefs->addWorkspace('dashboard');
+$user_dm_doclinks = $core->auth->user_prefs->dashboard->doclinks;
+$user_dm_dcnews = $core->auth->user_prefs->dashboard->dcnews;
+$user_dm_quickentry = $core->auth->user_prefs->dashboard->quickentry;
+$user_dm_nofavicons = $core->auth->user_prefs->dashboard->nofavicons;
+if ($core->auth->isSuperAdmin()) {
+ $user_dm_nodcupdate = $core->auth->user_prefs->dashboard->nodcupdate;
+}
+
+$core->auth->user_prefs->addWorkspace('accessibility');
+$user_acc_nodragdrop = $core->auth->user_prefs->accessibility->nodragdrop;
+
+$core->auth->user_prefs->addWorkspace('interface');
+$user_ui_darkmode = $core->auth->user_prefs->interface->darkmode;
+$user_ui_enhanceduploader = $core->auth->user_prefs->interface->enhanceduploader;
+$user_ui_hidemoreinfo = $core->auth->user_prefs->interface->hidemoreinfo;
+$user_ui_hidehelpbutton = $core->auth->user_prefs->interface->hidehelpbutton;
+$user_ui_showajaxloader = $core->auth->user_prefs->interface->showajaxloader;
+$user_ui_htmlfontsize = $core->auth->user_prefs->interface->htmlfontsize;
+$user_ui_dynfontsize = $core->auth->user_prefs->interface->dynfontsize;
+if ($core->auth->isSuperAdmin()) {
+ $user_ui_hide_std_favicon = $core->auth->user_prefs->interface->hide_std_favicon;
+}
+$user_ui_iconset = @$core->auth->user_prefs->interface->iconset;
+$user_ui_nofavmenu = $core->auth->user_prefs->interface->nofavmenu;
+$user_ui_media_by_page = ($core->auth->user_prefs->interface->media_by_page ?: 30);
+$user_ui_media_nb_last_dirs = $core->auth->user_prefs->interface->media_nb_last_dirs;
+
+$default_tab = !empty($_GET['tab']) ? html::escapeHTML($_GET['tab']) : 'user-profile';
+
+if (!empty($_GET['append']) || !empty($_GET['removed']) || !empty($_GET['neworder']) ||
+ !empty($_GET['replaced']) || !empty($_POST['appendaction']) || !empty($_POST['removeaction']) ||
+ !empty($_GET['db-updated']) || !empty($_POST['resetorder'])) {
+ $default_tab = 'user-favorites';
+} elseif (!empty($_GET['updated'])) {
+ $default_tab = 'user-options';
+}
+if (($default_tab != 'user-profile') && ($default_tab != 'user-options') && ($default_tab != 'user-favorites')) {
+ $default_tab = 'user-profile';
+}
+
+# Editors combo
+$editors_combo = dcAdminCombos::getEditorsCombo();
+$editors = array_keys($editors_combo);
+
+# Format by editors
+$formaters = $core->getFormaters();
+$format_by_editors = [];
+foreach ($formaters as $editor => $formats) {
+ foreach ($formats as $format) {
+ $format_by_editors[$format][$editor] = $editor;
+ }
+}
+$available_formats = ['' => ''];
+foreach (array_keys($format_by_editors) as $format) {
+ $available_formats[$format] = $format;
+ if (!isset($user_options['editor'][$format])) {
+ $user_options['editor'][$format] = '';
+ }
+}
+$status_combo = dcAdminCombos::getPostStatusescombo();
+
+$iconsets_combo = [__('Default') => ''];
+$iconsets_root = dirname(__FILE__) . '/images/iconset/';
+if (is_dir($iconsets_root) && is_readable($iconsets_root)) {
+ if (($d = @dir($iconsets_root)) !== false) {
+ while (($entry = $d->read()) !== false) {
+ if ($entry != '.' && $entry != '..' && substr($entry, 0, 1) != '.' && is_dir($iconsets_root . '/' . $entry)) {
+ $iconsets_combo[$entry] = $entry;
+ }
+ }
+ }
+}
+
+# Body base font size (37.5% = 6px, 50% = 8px, 62.5% = 10px, 75% = 12px, 87.5% = 14px)
+$htmlfontsize_combo = [
+ __('Smallest') => '37.5%',
+ __('Smaller') => '50%',
+ __('Default') => '62.5%',
+ __('Larger') => '75%',
+ __('Largest') => '87,5%'
+];
+# Ensure Font size is set to default is empty
+if ($user_ui_htmlfontsize == '') {
+ $user_ui_htmlfontsize = '62.5%';
+}
+
+# Language codes
+$lang_combo = dcAdminCombos::getAdminLangsCombo();
+
+# Get 3rd parts xhtml editor flags
+$rte = [
+ 'blog_descr' => [true, __('Blog description (in blog parameters)')],
+ 'cat_descr' => [true, __('Category description')]
+];
+$rte = new ArrayObject($rte);
+$core->callBehavior('adminRteFlags', $core, $rte);
+# Load user settings
+$rte_flags = @$core->auth->user_prefs->interface->rte_flags;
+if (is_array($rte_flags)) {
+ foreach ($rte_flags as $fk => $fv) {
+ if (isset($rte[$fk])) {
+ $rte[$fk][0] = $fv;
+ }
+ }
+}
+
+# Get default colums (admin lists)
+$cols = [
+ 'posts' => [__('Posts'), [
+ 'date' => [true, __('Date')],
+ 'category' => [true, __('Category')],
+ 'author' => [true, __('Author')],
+ 'comments' => [true, __('Comments')],
+ 'trackbacks' => [true, __('Trackbacks')]
+ ]]
+];
+$cols = new arrayObject($cols);
+$core->callBehavior('adminColumnsLists', $core, $cols);
+# Load user settings
+$cols_user = @$core->auth->user_prefs->interface->cols;
+if (is_array($cols_user)) {
+ foreach ($cols_user as $ct => $cv) {
+ foreach ($cv as $cn => $cd) {
+ if (isset($cols[$ct][1][$cn])) {
+ $cols[$ct][1][$cn][0] = $cd;
+ }
+ }
+ }
+}
+
+# Add or update user
+if (isset($_POST['user_name'])) {
+ try
+ {
+ $pwd_check = !empty($_POST['cur_pwd']) && $core->auth->checkPassword($_POST['cur_pwd']);
+
+ if ($core->auth->allowPassChange() && !$pwd_check && $user_email != $_POST['user_email']) {
+ throw new Exception(__('If you want to change your email or password you must provide your current password.'));
+ }
+
+ $cur = $core->con->openCursor($core->prefix . 'user');
+
+ $cur->user_name = $user_name = $_POST['user_name'];
+ $cur->user_firstname = $user_firstname = $_POST['user_firstname'];
+ $cur->user_displayname = $user_displayname = $_POST['user_displayname'];
+ $cur->user_email = $user_email = $_POST['user_email'];
+ $cur->user_url = $user_url = $_POST['user_url'];
+ $cur->user_lang = $user_lang = $_POST['user_lang'];
+ $cur->user_tz = $user_tz = $_POST['user_tz'];
+
+ $cur->user_options = new ArrayObject($user_options);
+
+ if ($core->auth->allowPassChange() && !empty($_POST['new_pwd'])) {
+ if (!$pwd_check) {
+ throw new Exception(__('If you want to change your email or password you must provide your current password.'));
+ }
+
+ if ($_POST['new_pwd'] != $_POST['new_pwd_c']) {
+ throw new Exception(__("Passwords don't match"));
+ }
+
+ $cur->user_pwd = $_POST['new_pwd'];
+ }
+
+ # --BEHAVIOR-- adminBeforeUserUpdate
+ $core->callBehavior('adminBeforeUserProfileUpdate', $cur, $core->auth->userID());
+
+ # Udate user
+ $core->updUser($core->auth->userID(), $cur);
+
+ # --BEHAVIOR-- adminAfterUserUpdate
+ $core->callBehavior('adminAfterUserProfileUpdate', $cur, $core->auth->userID());
+
+ dcPage::addSuccessNotice(__('Personal information has been successfully updated.'));
+
+ $core->adminurl->redirect("admin.user.preferences");
+ } catch (Exception $e) {
+ $core->error->add($e->getMessage());
+ }
+}
+
+# Update user options
+if (isset($_POST['user_editor'])) {
+ try
+ {
+ $cur = $core->con->openCursor($core->prefix . 'user');
+
+ $cur->user_name = $user_name;
+ $cur->user_firstname = $user_firstname;
+ $cur->user_displayname = $user_displayname;
+ $cur->user_email = $user_email;
+ $cur->user_url = $user_url;
+ $cur->user_lang = $user_lang;
+ $cur->user_tz = $user_tz;
+
+ $cur->user_post_status = $user_post_status = $_POST['user_post_status'];
+
+ $user_options['edit_size'] = (integer) $_POST['user_edit_size'];
+ if ($user_options['edit_size'] < 1) {
+ $user_options['edit_size'] = 10;
+ }
+ $user_options['post_format'] = $_POST['user_post_format'];
+ $user_options['editor'] = $_POST['user_editor'];
+ $user_options['enable_wysiwyg'] = !empty($_POST['user_wysiwyg']);
+ $user_options['toolbar_bottom'] = !empty($_POST['user_toolbar_bottom']);
+
+ $cur->user_options = new ArrayObject($user_options);
+
+ # --BEHAVIOR-- adminBeforeUserOptionsUpdate
+ $core->callBehavior('adminBeforeUserOptionsUpdate', $cur, $core->auth->userID());
+
+ # Update user prefs
+ $core->auth->user_prefs->accessibility->put('nodragdrop', !empty($_POST['user_acc_nodragdrop']), 'boolean');
+ $core->auth->user_prefs->interface->put('darkmode', !empty($_POST['user_ui_darkmode']), 'boolean');
+ $core->auth->user_prefs->interface->put('enhanceduploader', !empty($_POST['user_ui_enhanceduploader']), 'boolean');
+ $core->auth->user_prefs->interface->put('hidemoreinfo', !empty($_POST['user_ui_hidemoreinfo']), 'boolean');
+ $core->auth->user_prefs->interface->put('hidehelpbutton', !empty($_POST['user_ui_hidehelpbutton']), 'boolean');
+ $core->auth->user_prefs->interface->put('showajaxloader', !empty($_POST['user_ui_showajaxloader']), 'boolean');
+ $core->auth->user_prefs->interface->put('htmlfontsize', $_POST['user_ui_htmlfontsize'], 'string');
+ $core->auth->user_prefs->interface->put('dynfontsize', !empty($_POST['user_ui_dynfontsize']), 'boolean');
+ if ($core->auth->isSuperAdmin()) {
+ # Applied to all users
+ $core->auth->user_prefs->interface->put('hide_std_favicon', !empty($_POST['user_ui_hide_std_favicon']), 'boolean', null, true, true);
+ }
+ $core->auth->user_prefs->interface->put('media_by_page', (integer) $_POST['user_ui_media_by_page'], 'integer');
+ $core->auth->user_prefs->interface->put('media_nb_last_dirs', (integer) $_POST['user_ui_media_nb_last_dirs'], 'integer');
+ $core->auth->user_prefs->interface->put('media_last_dirs', [], 'array', null, false);
+ $core->auth->user_prefs->interface->put('media_fav_dirs', [], 'array', null, false);
+
+ # Update user columns (lists)
+ $cu = [];
+ foreach ($cols as $col_type => $cols_list) {
+ $ct = [];
+ foreach ($cols_list[1] as $col_name => $col_data) {
+ $ct[$col_name] = isset($_POST['cols_' . $col_type]) && in_array($col_name, $_POST['cols_' . $col_type], true) ? true : false;
+ }
+ if (count($ct)) {
+ $cu[$col_type] = $ct;
+ }
+ }
+ $core->auth->user_prefs->interface->put('cols', $cu, 'array');
+
+ # Update user xhtml editor flags
+ $rf = [];
+ foreach ($rte as $rk => $rv) {
+ $rf[$rk] = isset($_POST['rte_flags']) && in_array($rk, $_POST['rte_flags'], true) ? true : false;
+ }
+ $core->auth->user_prefs->interface->put('rte_flags', $rf, 'array');
+
+ # Update user
+ $core->updUser($core->auth->userID(), $cur);
+
+ # --BEHAVIOR-- adminAfterUserOptionsUpdate
+ $core->callBehavior('adminAfterUserOptionsUpdate', $cur, $core->auth->userID());
+
+ dcPage::addSuccessNotice(__('Personal options has been successfully updated.'));
+ $core->adminurl->redirect("admin.user.preferences", [], '#user-options');
+ } catch (Exception $e) {
+ $core->error->add($e->getMessage());
+ }
+}
+
+# Dashboard options
+if (isset($_POST['db-options'])) {
+ try
+ {
+ # --BEHAVIOR-- adminBeforeUserOptionsUpdate
+ $core->callBehavior('adminBeforeDashboardOptionsUpdate', $core->auth->userID());
+
+ # Update user prefs
+ $core->auth->user_prefs->dashboard->put('doclinks', !empty($_POST['user_dm_doclinks']), 'boolean');
+ $core->auth->user_prefs->dashboard->put('dcnews', !empty($_POST['user_dm_dcnews']), 'boolean');
+ $core->auth->user_prefs->dashboard->put('quickentry', !empty($_POST['user_dm_quickentry']), 'boolean');
+ $core->auth->user_prefs->dashboard->put('nofavicons', empty($_POST['user_dm_nofavicons']), 'boolean');
+ if ($core->auth->isSuperAdmin()) {
+ $core->auth->user_prefs->dashboard->put('nodcupdate', !empty($_POST['user_dm_nodcupdate']), 'boolean');
+ }
+ $core->auth->user_prefs->interface->put('iconset', (!empty($_POST['user_ui_iconset']) ? $_POST['user_ui_iconset'] : ''));
+ $core->auth->user_prefs->interface->put('nofavmenu', empty($_POST['user_ui_nofavmenu']), 'boolean');
+
+ # --BEHAVIOR-- adminAfterUserOptionsUpdate
+ $core->callBehavior('adminAfterDashboardOptionsUpdate', $core->auth->userID());
+
+ dcPage::addSuccessNotice(__('Dashboard options has been successfully updated.'));
+ $core->adminurl->redirect("admin.user.preferences", [], '#user-favorites');
+ } catch (Exception $e) {
+ $core->error->add($e->getMessage());
+ }
+}
+
+# Add selected favorites
+if (!empty($_POST['appendaction'])) {
+ try {
+ if (empty($_POST['append'])) {
+ throw new Exception(__('No favorite selected'));
+ }
+ $user_favs = $core->favs->getFavoriteIDs(false);
+ foreach ($_POST['append'] as $k => $v) {
+ if ($core->favs->exists($v)) {
+ $user_favs[] = $v;
+ }
+ }
+ $core->favs->setFavoriteIDs($user_favs, false);
+
+ if (!$core->error->flag()) {
+ dcPage::addSuccessNotice(__('Favorites have been successfully added.'));
+ $core->adminurl->redirect("admin.user.preferences", [], '#user-favorites');
+ }
+ } catch (Exception $e) {
+ $core->error->add($e->getMessage());
+ }
+}
+
+# Delete selected favorites
+if (!empty($_POST['removeaction'])) {
+ try {
+ if (empty($_POST['remove'])) {
+ throw new Exception(__('No favorite selected'));
+ }
+ $user_fav_ids = [];
+ foreach ($core->favs->getFavoriteIDs(false) as $v) {
+ $user_fav_ids[$v] = true;
+ }
+ foreach ($_POST['remove'] as $v) {
+ if (isset($user_fav_ids[$v])) {
+ unset($user_fav_ids[$v]);
+ }
+ }
+ $core->favs->setFavoriteIDs(array_keys($user_fav_ids), false);
+ if (!$core->error->flag()) {
+ dcPage::addSuccessNotice(__('Favorites have been successfully removed.'));
+ $core->adminurl->redirect("admin.user.preferences", [], '#user-favorites');
+ }
+ } catch (Exception $e) {
+ $core->error->add($e->getMessage());
+ }
+}
+
+# Order favs
+$order = [];
+if (empty($_POST['favs_order']) && !empty($_POST['order'])) {
+ $order = $_POST['order'];
+ asort($order);
+ $order = array_keys($order);
+} elseif (!empty($_POST['favs_order'])) {
+ $order = explode(',', $_POST['favs_order']);
+}
+
+if (!empty($_POST['saveorder']) && !empty($order)) {
+ foreach ($order as $k => $v) {
+ if (!$core->favs->exists($v)) {
+ unset($order[$k]);
+ }
+ }
+ $core->favs->setFavoriteIDs($order, false);
+ if (!$core->error->flag()) {
+ dcPage::addSuccessNotice(__('Favorites have been successfully updated.'));
+ $core->adminurl->redirect("admin.user.preferences", [], '#user-favorites');
+ }
+}
+
+# Replace default favorites by current set (super admin only)
+if (!empty($_POST['replace']) && $core->auth->isSuperAdmin()) {
+ $user_favs = $core->favs->getFavoriteIDs(false);
+ $core->favs->setFavoriteIDs($user_favs, true);
+
+ if (!$core->error->flag()) {
+ dcPage::addSuccessNotice(__('Default favorites have been successfully updated.'));
+ $core->adminurl->redirect("admin.user.preferences", [], '#user-favorites');
+ }
+}
+
+# Reset dashboard items order
+if (!empty($_POST['resetorder'])) {
+ $core->auth->user_prefs->dashboard->drop('main_order');
+ $core->auth->user_prefs->dashboard->drop('boxes_order');
+ $core->auth->user_prefs->dashboard->drop('boxes_items_order');
+ $core->auth->user_prefs->dashboard->drop('boxes_contents_order');
+
+ if (!$core->error->flag()) {
+ dcPage::addSuccessNotice(__('Dashboard items order have been successfully reset.'));
+ $core->adminurl->redirect("admin.user.preferences", [], '#user-favorites');
+ }
+}
+
+/* DISPLAY
+-------------------------------------------------------- */
+dcPage::open($page_title,
+ ($user_acc_nodragdrop ? '' : dcPage::jsLoad('js/_preferences-dragdrop.js')) .
+ dcPage::jsLoad('js/jquery/jquery-ui.custom.js') .
+ dcPage::jsLoad('js/jquery/jquery.ui.touch-punch.js') .
+ dcPage::jsLoad('js/jquery/jquery.pwstrength.js') .
+ dcPage::jsJson('preferences', [
+ sprintf(__('Password strength: %s'), __('very weak')),
+ sprintf(__('Password strength: %s'), __('weak')),
+ sprintf(__('Password strength: %s'), __('mediocre')),
+ sprintf(__('Password strength: %s'), __('strong')),
+ sprintf(__('Password strength: %s'), __('very strong'))
+ ]) .
+ dcPage::jsLoad('js/_preferences.js') .
+ dcPage::jsPageTabs($default_tab) .
+ dcPage::jsConfirmClose('user-form', 'opts-forms', 'favs-form') .
+
+ # --BEHAVIOR-- adminPreferencesHeaders
+ $core->callBehavior('adminPreferencesHeaders'),
+
+ dcPage::breadcrumb(
+ [
+ html::escapeHTML($core->auth->userID()) => '',
+ $page_title => ''
+ ])
+);
+
+# User profile
+echo '';
+
+echo
+'
' . __('My profile') . ' ' .
+'
' .
+
+ '
';
+
+# User options : some from actual user profile, dashboard modules, ...
+echo '';
+
+echo
+'
';
+
+echo '
';
+
+# My dashboard
+echo '';
+$ws = $core->auth->user_prefs->addWorkspace('favorites');
+echo '
' . __('My dashboard') . ' ';
+
+# Favorites
+echo '
';
+
+# Dashboard items
+echo
+'
';
+
+# Dashboard items order (reset)
+echo '
';
+
+echo '
'; # /multipart-user-favorites
+
+dcPage::helpBlock('core_user_pref');
+dcPage::close();
diff --git a/dotclear._no/admin/search.php b/dotclear._no/admin/search.php
new file mode 100644
index 0000000..0b6bd0c
--- /dev/null
+++ b/dotclear._no/admin/search.php
@@ -0,0 +1,157 @@
+blog->getPosts($params);
+ $counter = $core->blog->getPosts($params, true);
+ $post_list = new adminPostList($core, $posts, $counter->f(0));
+ } catch (Exception $e) {
+ $core->error->add($e->getMessage());
+ }
+ }
+ # Get comments
+ elseif ($qtype == 'c') {
+ $starting_scripts .= dcPage::jsLoad('js/_comments.js');
+
+ $params['search'] = $q;
+ $params['limit'] = [(($page - 1) * $nb_per_page), $nb_per_page];
+ $params['no_content'] = true;
+ $params['order'] = 'comment_dt DESC';
+
+ try {
+ $comments = $core->blog->getComments($params);
+ $counter = $core->blog->getComments($params, true);
+ $comment_list = new adminCommentList($core, $comments, $counter->f(0));
+ } catch (Exception $e) {
+ $core->error->add($e->getMessage());
+ }
+ }
+}
+
+if ($qtype == 'p') {
+ $posts_actions_page = new dcPostsActionsPage($core, $core->adminurl->get("admin.search"), ['q' => $q, 'qtype' => $qtype]);
+
+ if ($posts_actions_page->process()) {
+ return;
+ }
+} else {
+ $comments_actions_page = new dcCommentsActionsPage($core, $core->adminurl->get("admin.search"), ['q' => $q, 'qtype' => $qtype]);
+
+ if ($comments_actions_page->process()) {
+ return;
+ }
+}
+
+dcPage::open(__('Search'), $starting_scripts,
+ dcPage::breadcrumb(
+ [
+ html::escapeHTML($core->blog->name) => '',
+ __('Search') => ''
+ ])
+);
+
+echo
+'';
+
+if ($q && !$core->error->flag()) {
+ $redir = html::escapeHTML($_SERVER['REQUEST_URI']);
+
+ # Show posts
+ if ($qtype == 'p') {
+
+ if ($counter->f(0) > 0) {
+ printf('' .
+ ($counter->f(0) == 1 ? __('%d entry found') : __('%d entries found')) .
+ ' ', $counter->f(0));
+ }
+
+ $post_list->display($page, $nb_per_page,
+ ''
+ );
+ }
+ # Show posts
+ elseif ($qtype == 'c') {
+ # Actions combo box
+
+ if ($counter->f(0) > 0) {
+ printf('' .
+ ($counter->f(0) == 1 ? __('%d comment found') : __('%d comments found')) .
+ ' ', $counter->f(0));
+ }
+
+ $comment_list->display($page, $nb_per_page,
+ ''
+ );
+ }
+}
+
+dcPage::helpBlock('core_search');
+dcPage::close();
diff --git a/dotclear._no/admin/services.php b/dotclear._no/admin/services.php
new file mode 100644
index 0000000..dce5fb3
--- /dev/null
+++ b/dotclear._no/admin/services.php
@@ -0,0 +1,639 @@
+rest->addFunction('getPostsCount', ['dcRestMethods', 'getPostsCount']);
+$core->rest->addFunction('getCommentsCount', ['dcRestMethods', 'getCommentsCount']);
+$core->rest->addFunction('checkNewsUpdate', ['dcRestMethods', 'checkNewsUpdate']);
+$core->rest->addFunction('checkCoreUpdate', ['dcRestMethods', 'checkCoreUpdate']);
+$core->rest->addFunction('getPostById', ['dcRestMethods', 'getPostById']);
+$core->rest->addFunction('getCommentById', ['dcRestMethods', 'getCommentById']);
+$core->rest->addFunction('quickPost', ['dcRestMethods', 'quickPost']);
+$core->rest->addFunction('validatePostMarkup', ['dcRestMethods', 'validatePostMarkup']);
+$core->rest->addFunction('getZipMediaContent', ['dcRestMethods', 'getZipMediaContent']);
+$core->rest->addFunction('getMeta', ['dcRestMethods', 'getMeta']);
+$core->rest->addFunction('delMeta', ['dcRestMethods', 'delMeta']);
+$core->rest->addFunction('setPostMeta', ['dcRestMethods', 'setPostMeta']);
+$core->rest->addFunction('searchMeta', ['dcRestMethods', 'searchMeta']);
+$core->rest->addFunction('setSectionFold', ['dcRestMethods', 'setSectionFold']);
+$core->rest->addFunction('getModuleById', ['dcRestMethods', 'getModuleById']);
+$core->rest->addFunction('setDashboardPositions', ['dcRestMethods', 'setDashboardPositions']);
+
+$core->rest->serve();
+
+/* Common REST methods */
+class dcRestMethods
+{
+ /**
+ * Serve method to get number of posts (whatever are their status) for current blog.
+ *
+ * @param core dcCore dcCore instance
+ * @param get array cleaned $_GET
+ */
+ public static function getPostsCount($core, $get)
+ {
+ $count = $core->blog->getPosts([], true)->f(0);
+ $str = sprintf(__('%d post', '%d posts', $count), $count);
+
+ $rsp = new xmlTag('count');
+ $rsp->ret = $str;
+
+ return $rsp;
+ }
+
+ /**
+ * Serve method to get number of comments (whatever are their status) for current blog.
+ *
+ * @param core dcCore dcCore instance
+ * @param get array cleaned $_GET
+ */
+ public static function getCommentsCount($core, $get)
+ {
+ $count = $core->blog->getComments([], true)->f(0);
+ $str = sprintf(__('%d comment', '%d comments', $count), $count);
+
+ $rsp = new xmlTag('count');
+ $rsp->ret = $str;
+
+ return $rsp;
+ }
+
+ public static function checkNewsUpdate($core, $get)
+ {
+ # Dotclear news
+
+ $rsp = new xmlTag('news');
+ $rsp->check = false;
+ $ret = __('Dotclear news not available');
+
+ if ($core->auth->user_prefs->dashboard->dcnews) {
+ try
+ {
+
+ if (empty($GLOBALS['__resources']['rss_news'])) {
+ throw new Exception();
+ }
+ $feed_reader = new feedReader;
+ $feed_reader->setCacheDir(DC_TPL_CACHE);
+ $feed_reader->setTimeout(2);
+ $feed_reader->setUserAgent('Dotclear - https://dotclear.org/');
+ $feed = $feed_reader->parse($GLOBALS['__resources']['rss_news']);
+ if ($feed) {
+ $ret = '' . __('Dotclear news') . ' ';
+ $i = 1;
+ foreach ($feed->items as $item) {
+ $dt = isset($item->link) ? '' .
+ $item->title . ' ' : $item->title;
+
+ if ($i < 3) {
+ $ret .=
+ '' . $dt . ' ' .
+ '' . dt::dt2str(__('%d %B %Y:'), $item->pubdate, 'Europe/Paris') . ' ' .
+ '' . text::cutString(html::clean($item->content), 120) . '...
';
+ } else {
+ $ret .=
+ '' . $dt . ' ' .
+ '' . dt::dt2str(__('%d %B %Y:'), $item->pubdate, 'Europe/Paris') . ' ';
+ }
+ $i++;
+ if ($i > 2) {break;}
+ }
+ $ret .= ' ';
+ $rsp->check = true;
+ }
+ } catch (Exception $e) {}
+ }
+ $rsp->ret = $ret;
+ return $rsp;
+ }
+
+ public static function checkCoreUpdate($core, $get)
+ {
+ # Dotclear updates notifications
+
+ $rsp = new xmlTag('update');
+ $rsp->check = false;
+ $ret = __('Dotclear update not available');
+
+ if ($core->auth->isSuperAdmin() && !DC_NOT_UPDATE && is_readable(DC_DIGESTS) &&
+ !$core->auth->user_prefs->dashboard->nodcupdate) {
+ $updater = new dcUpdate(DC_UPDATE_URL, 'dotclear', DC_UPDATE_VERSION, DC_TPL_CACHE . '/versions');
+ $new_v = $updater->check(DC_VERSION);
+ $version_info = $new_v ? $updater->getInfoURL() : '';
+
+ if ($updater->getNotify() && $new_v) {
+ // Check PHP version required
+ if (version_compare(phpversion(), $updater->getPHPVersion()) >= 0) {
+ $ret =
+ '';
+ } else {
+ $ret = '' .
+ sprintf(__('A new version of Dotclear is available but needs PHP version ≥ %s, your\'s is currently %s'),
+ $updater->getPHPVersion(), phpversion()) .
+ '
';
+ }
+ $rsp->check = true;
+ } else {
+ if (version_compare(phpversion(), DC_NEXT_REQUIRED_PHP, '<')) {
+ $ret = '' .
+ sprintf(__('The next versions of Dotclear will not support PHP version < %s, your\'s is currently %s'),
+ DC_NEXT_REQUIRED_PHP, phpversion()) .
+ '
';
+ $rsp->check = true;
+ }
+ }
+ }
+ $rsp->ret = $ret;
+ return $rsp;
+ }
+
+ public static function getPostById($core, $get)
+ {
+ if (empty($get['id'])) {
+ throw new Exception('No post ID');
+ }
+
+ $params = ['post_id' => (integer) $get['id']];
+
+ if (isset($get['post_type'])) {
+ $params['post_type'] = $get['post_type'];
+ }
+
+ $rs = $core->blog->getPosts($params);
+
+ if ($rs->isEmpty()) {
+ throw new Exception('No post for this ID');
+ }
+
+ $rsp = new xmlTag('post');
+ $rsp->id = $rs->post_id;
+
+ $rsp->blog_id($rs->blog_id);
+ $rsp->user_id($rs->user_id);
+ $rsp->cat_id($rs->cat_id);
+ $rsp->post_dt($rs->post_dt);
+ $rsp->post_creadt($rs->post_creadt);
+ $rsp->post_upddt($rs->post_upddt);
+ $rsp->post_format($rs->post_format);
+ $rsp->post_url($rs->post_url);
+ $rsp->post_lang($rs->post_lang);
+ $rsp->post_title($rs->post_title);
+ $rsp->post_excerpt($rs->post_excerpt);
+ $rsp->post_excerpt_xhtml($rs->post_excerpt_xhtml);
+ $rsp->post_content($rs->post_content);
+ $rsp->post_content_xhtml($rs->post_content_xhtml);
+ $rsp->post_notes($rs->post_notes);
+ $rsp->post_status($rs->post_status);
+ $rsp->post_selected($rs->post_selected);
+ $rsp->post_open_comment($rs->post_open_comment);
+ $rsp->post_open_tb($rs->post_open_tb);
+ $rsp->nb_comment($rs->nb_comment);
+ $rsp->nb_trackback($rs->nb_trackback);
+ $rsp->user_name($rs->user_name);
+ $rsp->user_firstname($rs->user_firstname);
+ $rsp->user_displayname($rs->user_displayname);
+ $rsp->user_email($rs->user_email);
+ $rsp->user_url($rs->user_url);
+ $rsp->cat_title($rs->cat_title);
+ $rsp->cat_url($rs->cat_url);
+
+ $rsp->post_display_content($rs->getContent(true));
+ $rsp->post_display_excerpt($rs->getExcerpt(true));
+
+ $metaTag = new xmlTag('meta');
+ if (($meta = @unserialize($rs->post_meta)) !== false) {
+ foreach ($meta as $K => $V) {
+ foreach ($V as $v) {
+ $metaTag->$K($v);
+ }
+ }
+ }
+ $rsp->post_meta($metaTag);
+
+ return $rsp;
+ }
+
+ public static function getCommentById($core, $get)
+ {
+ if (empty($get['id'])) {
+ throw new Exception('No comment ID');
+ }
+
+ $rs = $core->blog->getComments(['comment_id' => (integer) $get['id']]);
+
+ if ($rs->isEmpty()) {
+ throw new Exception('No comment for this ID');
+ }
+
+ $rsp = new xmlTag('post');
+ $rsp->id = $rs->comment_id;
+
+ $rsp->comment_dt($rs->comment_dt);
+ $rsp->comment_upddt($rs->comment_upddt);
+ $rsp->comment_author($rs->comment_author);
+ $rsp->comment_site($rs->comment_site);
+ $rsp->comment_content($rs->comment_content);
+ $rsp->comment_trackback($rs->comment_trackback);
+ $rsp->comment_status($rs->comment_status);
+ $rsp->post_title($rs->post_title);
+ $rsp->post_url($rs->post_url);
+ $rsp->post_id($rs->post_id);
+ $rsp->post_dt($rs->post_dt);
+ $rsp->user_id($rs->user_id);
+
+ $rsp->comment_display_content($rs->getContent(true));
+
+ if ($core->auth->userID()) {
+ $rsp->comment_ip($rs->comment_ip);
+ $rsp->comment_email($rs->comment_email);
+ $rsp->comment_spam_disp(dcAntispam::statusMessage($rs));
+ }
+
+ return $rsp;
+ }
+
+ public static function quickPost($core, $get, $post)
+ {
+ # Create category
+ if (!empty($post['new_cat_title']) && $core->auth->check('categories', $core->blog->id)) {
+
+ $cur_cat = $core->con->openCursor($core->prefix . 'category');
+ $cur_cat->cat_title = $post['new_cat_title'];
+ $cur_cat->cat_url = '';
+
+ $parent_cat = !empty($post['new_cat_parent']) ? $post['new_cat_parent'] : '';
+
+ # --BEHAVIOR-- adminBeforeCategoryCreate
+ $core->callBehavior('adminBeforeCategoryCreate', $cur_cat);
+
+ $post['cat_id'] = $core->blog->addCategory($cur_cat, (integer) $parent_cat);
+
+ # --BEHAVIOR-- adminAfterCategoryCreate
+ $core->callBehavior('adminAfterCategoryCreate', $cur_cat, $post['cat_id']);
+ }
+
+ $cur = $core->con->openCursor($core->prefix . 'post');
+
+ $cur->post_title = !empty($post['post_title']) ? $post['post_title'] : '';
+ $cur->user_id = $core->auth->userID();
+ $cur->post_content = !empty($post['post_content']) ? $post['post_content'] : '';
+ $cur->cat_id = !empty($post['cat_id']) ? (integer) $post['cat_id'] : null;
+ $cur->post_format = !empty($post['post_format']) ? $post['post_format'] : 'xhtml';
+ $cur->post_lang = !empty($post['post_lang']) ? $post['post_lang'] : '';
+ $cur->post_status = !empty($post['post_status']) ? (integer) $post['post_status'] : 0;
+ $cur->post_open_comment = (integer) $core->blog->settings->system->allow_comments;
+ $cur->post_open_tb = (integer) $core->blog->settings->system->allow_trackbacks;
+
+ # --BEHAVIOR-- adminBeforePostCreate
+ $core->callBehavior('adminBeforePostCreate', $cur);
+
+ $return_id = $core->blog->addPost($cur);
+
+ # --BEHAVIOR-- adminAfterPostCreate
+ $core->callBehavior('adminAfterPostCreate', $cur, $return_id);
+
+ $rsp = new xmlTag('post');
+ $rsp->id = $return_id;
+
+ $post = $core->blog->getPosts(['post_id' => $return_id]);
+
+ $rsp->post_status = $post->post_status;
+ $rsp->post_url = $post->getURL();
+ return $rsp;
+ }
+
+ public static function validatePostMarkup($core, $get, $post)
+ {
+ if (!isset($post['excerpt'])) {
+ throw new Exception('No entry excerpt');
+ }
+
+ if (!isset($post['content'])) {
+ throw new Exception('No entry content');
+ }
+
+ if (empty($post['format'])) {
+ throw new Exception('No entry format');
+ }
+
+ if (!isset($post['lang'])) {
+ throw new Exception('No entry lang');
+ }
+
+ $excerpt = $post['excerpt'];
+ $excerpt_xhtml = '';
+ $content = $post['content'];
+ $content_xhtml = '';
+ $format = $post['format'];
+ $lang = $post['lang'];
+
+ $core->blog->setPostContent(0, $format, $lang, $excerpt, $excerpt_xhtml, $content, $content_xhtml);
+
+ $rsp = new xmlTag('result');
+
+ $v = htmlValidator::validate($excerpt_xhtml . $content_xhtml);
+
+ $rsp->valid($v['valid']);
+ $rsp->errors($v['errors']);
+
+ return $rsp;
+ }
+
+ public static function getZipMediaContent($core, $get, $post)
+ {
+ if (empty($get['id'])) {
+ throw new Exception('No media ID');
+ }
+
+ $id = (integer) $get['id'];
+
+ if (!$core->auth->check('media,media_admin', $core->blog)) {
+ throw new Exception('Permission denied');
+ }
+
+ try {
+ $core->media = new dcMedia($core);
+ $file = $core->media->getFile($id);
+ } catch (Exception $e) {}
+
+ if ($file === null || $file->type != 'application/zip' || !$file->editable) {
+ throw new Exception('Not a valid file');
+ }
+
+ $rsp = new xmlTag('result');
+ $content = $core->media->getZipContent($file);
+
+ foreach ($content as $k => $v) {
+ $rsp->file($k);
+ }
+
+ return $rsp;
+ }
+
+ public static function getMeta($core, $get)
+ {
+ $postid = !empty($get['postId']) ? $get['postId'] : null;
+ $limit = !empty($get['limit']) ? $get['limit'] : null;
+ $metaId = !empty($get['metaId']) ? $get['metaId'] : null;
+ $metaType = !empty($get['metaType']) ? $get['metaType'] : null;
+
+ $sortby = !empty($get['sortby']) ? $get['sortby'] : 'meta_type,asc';
+
+ $rs = $core->meta->getMetadata([
+ 'meta_type' => $metaType,
+ 'limit' => $limit,
+ 'meta_id' => $metaId,
+ 'post_id' => $postid]);
+ $rs = $core->meta->computeMetaStats($rs);
+
+ $sortby = explode(',', $sortby);
+ $sort = $sortby[0];
+ $order = isset($sortby[1]) ? $sortby[1] : 'asc';
+
+ switch ($sort) {
+ case 'metaId':
+ $sort = 'meta_id_lower';
+ break;
+ case 'count':
+ $sort = 'count';
+ break;
+ case 'metaType':
+ $sort = 'meta_type';
+ break;
+ default:
+ $sort = 'meta_type';
+ }
+
+ $rs->sort($sort, $order);
+
+ $rsp = new xmlTag();
+
+ while ($rs->fetch()) {
+ $metaTag = new xmlTag('meta');
+ $metaTag->type = $rs->meta_type;
+ $metaTag->uri = rawurlencode($rs->meta_id);
+ $metaTag->count = $rs->count;
+ $metaTag->percent = $rs->percent;
+ $metaTag->roundpercent = $rs->roundpercent;
+ $metaTag->CDATA($rs->meta_id);
+
+ $rsp->insertNode($metaTag);
+ }
+
+ return $rsp;
+ }
+
+ public static function setPostMeta($core, $get, $post)
+ {
+ if (empty($post['postId'])) {
+ throw new Exception('No post ID');
+ }
+
+ if (empty($post['meta']) && $post['meta'] != '0') {
+ throw new Exception('No meta');
+ }
+
+ if (empty($post['metaType'])) {
+ throw new Exception('No meta type');
+ }
+
+ # Get previous meta for post
+ $post_meta = $core->meta->getMetadata([
+ 'meta_type' => $post['metaType'],
+ 'post_id' => $post['postId']]);
+ $pm = [];
+ while ($post_meta->fetch()) {
+ $pm[] = $post_meta->meta_id;
+ }
+
+ foreach ($core->meta->splitMetaValues($post['meta']) as $m) {
+ if (!in_array($m, $pm)) {
+ $core->meta->setPostMeta($post['postId'], $post['metaType'], $m);
+ }
+ }
+
+ return true;
+ }
+
+ public static function delMeta($core, $get, $post)
+ {
+ if (empty($post['postId'])) {
+ throw new Exception('No post ID');
+ }
+
+ if (empty($post['metaId']) && $post['metaId'] != '0') {
+ throw new Exception('No meta ID');
+ }
+
+ if (empty($post['metaType'])) {
+ throw new Exception('No meta type');
+ }
+
+ $core->meta->delPostMeta($post['postId'], $post['metaType'], $post['metaId']);
+
+ return true;
+ }
+
+ public static function searchMeta($core, $get)
+ {
+ $q = !empty($get['q']) ? $get['q'] : null;
+ $metaType = !empty($get['metaType']) ? $get['metaType'] : null;
+
+ $sortby = !empty($get['sortby']) ? $get['sortby'] : 'meta_type,asc';
+
+ $rs = $core->meta->getMetadata(['meta_type' => $metaType]);
+ $rs = $core->meta->computeMetaStats($rs);
+
+ $sortby = explode(',', $sortby);
+ $sort = $sortby[0];
+ $order = isset($sortby[1]) ? $sortby[1] : 'asc';
+
+ switch ($sort) {
+ case 'metaId':
+ $sort = 'meta_id_lower';
+ break;
+ case 'count':
+ $sort = 'count';
+ break;
+ case 'metaType':
+ $sort = 'meta_type';
+ break;
+ default:
+ $sort = 'meta_type';
+ }
+
+ $rs->sort($sort, $order);
+
+ $rsp = new xmlTag();
+
+ while ($rs->fetch()) {
+ if (stripos($rs->meta_id, $q) === 0) {
+ $metaTag = new xmlTag('meta');
+ $metaTag->type = $rs->meta_type;
+ $metaTag->uri = rawurlencode($rs->meta_id);
+ $metaTag->count = $rs->count;
+ $metaTag->percent = $rs->percent;
+ $metaTag->roundpercent = $rs->roundpercent;
+ $metaTag->CDATA($rs->meta_id);
+
+ $rsp->insertNode($metaTag);
+ }
+ }
+
+ return $rsp;
+ }
+
+ public static function setSectionFold($core, $get, $post)
+ {
+ if (empty($post['section'])) {
+ throw new Exception('No section name');
+ }
+ if ($core->auth->user_prefs->toggles === null) {
+ $core->auth->user_prefs->addWorkspace('toggles');
+ }
+ $section = $post['section'];
+ $status = isset($post['value']) && ($post['value'] != 0);
+ if ($core->auth->user_prefs->toggles->prefExists('unfolded_sections')) {
+ $toggles = explode(',', trim($core->auth->user_prefs->toggles->unfolded_sections));
+ } else {
+ $toggles = [];
+ }
+ $k = array_search($section, $toggles);
+ if ($status) {
+ // true == Fold section ==> remove it from unfolded list
+ if ($k !== false) {
+ unset($toggles[$k]);
+ }
+ } else {
+ // false == unfold section ==> add it to unfolded list
+ if ($k === false) {
+ $toggles[] = $section;
+ };
+ }
+ $core->auth->user_prefs->toggles->put('unfolded_sections', join(',', $toggles));
+ return true;
+ }
+
+ public static function setDashboardPositions($core, $get, $post)
+ {
+ if (empty($post['id'])) {
+ throw new Exception('No zone name');
+ }
+ if (empty($post['list'])) {
+ throw new Exception('No sorted list of id');
+ }
+
+ if ($core->auth->user_prefs->dashboard === null) {
+ $core->auth->user_prefs->addWorkspace('dashboard');
+ }
+
+ $zone = $post['id'];
+ $order = $post['list'];
+
+ $core->auth->user_prefs->dashboard->put($zone, $order);
+ return true;
+ }
+
+ public static function getModuleById($core, $get, $post)
+ {
+ if (empty($get['id'])) {
+ throw new Exception('No module ID');
+ }
+ if (empty($get['list'])) {
+ throw new Exception('No list ID');
+ }
+
+ $id = $get['id'];
+ $list = $get['list'];
+ $module = [];
+
+ if ($list == 'plugin-activate') {
+ $modules = $core->plugins->getModules();
+ if (empty($modules) || !isset($modules[$id])) {
+ throw new Exception('Unknow module ID');
+ }
+ $module = $modules[$id];
+ } elseif ($list == 'plugin-new') {
+ $store = new dcStore(
+ $core->plugins,
+ $core->blog->settings->system->store_plugin_url
+ );
+ $store->check();
+
+ $modules = $store->get();
+ if (empty($modules) || !isset($modules[$id])) {
+ throw new Exception('Unknow module ID');
+ }
+ $module = $modules[$id];
+ } else {
+ // behavior not implemented yet
+ }
+
+ if (empty($module)) {
+ throw new Exception('Unknow module ID');
+ }
+
+ $module = adminModulesList::sanitizeModule($id, $module);
+
+ $rsp = new xmlTag('module');
+ $rsp->id = $id;
+
+ foreach ($module as $k => $v) {
+ $rsp->{$k}((string) $v);
+ }
+
+ return $rsp;
+ }
+}
diff --git a/dotclear._no/admin/style/add.png b/dotclear._no/admin/style/add.png
new file mode 100755
index 0000000..dab4ead
Binary files /dev/null and b/dotclear._no/admin/style/add.png differ
diff --git a/dotclear._no/admin/style/bg_h2.png b/dotclear._no/admin/style/bg_h2.png
new file mode 100755
index 0000000..19f2232
Binary files /dev/null and b/dotclear._no/admin/style/bg_h2.png differ
diff --git a/dotclear._no/admin/style/bg_menu.png b/dotclear._no/admin/style/bg_menu.png
new file mode 100755
index 0000000..862b415
Binary files /dev/null and b/dotclear._no/admin/style/bg_menu.png differ
diff --git a/dotclear._no/admin/style/bg_wrapper.png b/dotclear._no/admin/style/bg_wrapper.png
new file mode 100755
index 0000000..7481701
Binary files /dev/null and b/dotclear._no/admin/style/bg_wrapper.png differ
diff --git a/dotclear._no/admin/style/cancel.png b/dotclear._no/admin/style/cancel.png
new file mode 100644
index 0000000..ce71f89
Binary files /dev/null and b/dotclear._no/admin/style/cancel.png differ
diff --git a/dotclear._no/admin/style/candyUpload/cancel.png b/dotclear._no/admin/style/candyUpload/cancel.png
new file mode 100644
index 0000000..b1a5947
Binary files /dev/null and b/dotclear._no/admin/style/candyUpload/cancel.png differ
diff --git a/dotclear._no/admin/style/candyUpload/loader.png b/dotclear._no/admin/style/candyUpload/loader.png
new file mode 100644
index 0000000..267f541
Binary files /dev/null and b/dotclear._no/admin/style/candyUpload/loader.png differ
diff --git a/dotclear._no/admin/style/candyUpload/style.css b/dotclear._no/admin/style/candyUpload/style.css
new file mode 100755
index 0000000..f2f9fd3
--- /dev/null
+++ b/dotclear._no/admin/style/candyUpload/style.css
@@ -0,0 +1,113 @@
+
+.cu-ctrl {
+ background: #f5f5f5;
+ padding: .5em 0 0 0;
+ margin: 0 0 1em 0;
+ overflow: hidden;
+}
+.cu-msg {
+ font-weight: bold;
+}
+.cu-msg.cu-error {
+ color: #c00;
+}
+.cu-files {
+ padding: 0 0.5em;
+ margin: 10px 0;
+}
+.cu-file {
+ margin: 0 0 8px 0;
+ position: relative;
+}
+.cu-fileinfo {
+ margin-left: 16px;
+}
+.cu-fileinfo span.cu-filecancel {
+ display: block;
+ position: absolute;
+ top: 2px;
+ left: 0px !important;
+ left: -16px;
+}
+.cu-filecancel a {
+ display: block;
+ width: 12px;
+ height: 12px;
+ background: transparent url(cancel.png) no-repeat top left;
+ border: none;
+ text-indent: -5000px;
+ outline: none;
+}
+.cu-filemsg {
+ font-weight: bold;
+ color: green;
+}
+span.cu-filemsg.cu-error {
+ color: #c00;
+}
+.cu-progress {
+ margin-left: 16px;
+}
+.cu-progress div {
+ height: 10px;
+ width: 0;
+ font-size: 0.8em;
+ line-height: 1em;
+ height: 1em;
+ padding: .2em 0;
+ text-align: right;
+ background: green url(loader.png) repeat-x top left;
+ color: white;
+ font-weight: bold;
+ border-radius: 2px;
+}
+.cu-btn {
+ padding: 0 0.5em;
+ line-height: 1.2em;
+ height: 1.5em;
+ margin-top: 1em;
+ position: relative;
+}
+.cu-btn span {
+ display: block;
+ margin: 0 1em 0 0;
+ float: right;
+}
+.cu-btn span a {
+ display: block;
+ padding: .3em 1.5em;
+}
+span.cu-btn-browse {
+ float: none;
+ position: absolute;
+ left: 0;
+ margin: 0;
+}
+span.cu-btn-upload, span.cu-btn-clean {
+ margin-right: 0;
+}
+span.cu-btn-upload a.button {
+ color: #fff;
+ border: 1px solid #2373A8;
+ background: #2373A8;
+ background: -webkit-gradient(linear, left top, left bottom, from(#2C8FD1), to(#2373A8));
+ background: -moz-linear-gradient(top, #2C8FD1, #2373A8);
+}
+span.cu-btn-upload a.button:hover,
+span.cu-btn-upload a.button:focus {
+ color: #fff;
+ border: 1px solid #2C8FD1;
+ background: #2C8FD1;
+ background: -webkit-gradient(linear, left top, left bottom, from(#2373A8), to(#2C8FD1));
+ background: -moz-linear-gradient(top, #2373A8, #2C8FD1);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#2373A8', endColorstr='#2C8FD1');
+}
+.cu-maxsize.form-note {
+ clear: both;
+ margin: 1.5em 0 0;
+ font-style: italic;
+}
+.cu-disable {
+ margin: 1em 0;
+ padding: 0 0 0.5em 0.5em;
+}
\ No newline at end of file
diff --git a/dotclear._no/admin/style/config.rb b/dotclear._no/admin/style/config.rb
new file mode 100644
index 0000000..3eaa422
--- /dev/null
+++ b/dotclear._no/admin/style/config.rb
@@ -0,0 +1,19 @@
+# Require any additional compass plugins here.
+
+# Set this to the root of your project when deployed:
+http_path = "./"
+css_dir = "./"
+sass_dir = "scss"
+images_dir = "./"
+javascripts_dir = "scripts"
+
+# You can select your preferred output style here (can be overridden via the command line):
+# output_style = :expanded or :nested or :compact or :compressed
+# To enable relative paths to assets via compass helper functions. Uncomment:
+# relative_assets = true
+# To disable debugging comments that display the original location of your selectors. Uncomment:
+output_style = :nested
+environment = :development
+line_comments = false
+
+Sass::Script::Number.precision = 7
diff --git a/dotclear._no/admin/style/dashboard-alt.png b/dotclear._no/admin/style/dashboard-alt.png
new file mode 100644
index 0000000..e15a923
Binary files /dev/null and b/dotclear._no/admin/style/dashboard-alt.png differ
diff --git a/dotclear._no/admin/style/dashboard.png b/dotclear._no/admin/style/dashboard.png
new file mode 100644
index 0000000..52296cb
Binary files /dev/null and b/dotclear._no/admin/style/dashboard.png differ
diff --git a/dotclear._no/admin/style/date-picker.css b/dotclear._no/admin/style/date-picker.css
new file mode 100644
index 0000000..7a53110
--- /dev/null
+++ b/dotclear._no/admin/style/date-picker.css
@@ -0,0 +1,42 @@
+.date-picker {
+ border-collapse: collapse;
+ background: #fff;
+ color: #e2e2e2;
+ border: 1px solid #666;
+ width: auto;
+}
+.date-picker th {
+ border: none;
+ background-color: #2b90d8;
+ color: #fff;
+ text-align: center;
+}
+.date-picker td {
+ border: none;
+ text-align: center;
+ padding : 4px 6px;
+}
+th.date-picker-month {
+ text-align: left;
+}
+th.date-picker-year {
+ text-align: right;
+}
+.date-picker-control, th.date-picker-control {
+ color: #eee;
+ cursor: pointer;
+}
+.date-picker-day, .date-picker-today {
+ color: #000;
+ background: #e2e2e2;
+ cursor: pointer;
+}
+.date-picker-today {
+ color: #fff;
+ background: rgb(239, 83, 80);
+ font-weight: bold;
+}
+.date-picker input {
+ border: 1px solid #eee;
+ padding: .125em .5em;
+}
diff --git a/dotclear._no/admin/style/dc_bg.png b/dotclear._no/admin/style/dc_bg.png
new file mode 100755
index 0000000..15fb5f6
Binary files /dev/null and b/dotclear._no/admin/style/dc_bg.png differ
diff --git a/dotclear._no/admin/style/dc_logo.png b/dotclear._no/admin/style/dc_logo.png
new file mode 100755
index 0000000..d7204e2
Binary files /dev/null and b/dotclear._no/admin/style/dc_logo.png differ
diff --git a/dotclear._no/admin/style/dc_logo_small.png b/dotclear._no/admin/style/dc_logo_small.png
new file mode 100755
index 0000000..3ec22c9
Binary files /dev/null and b/dotclear._no/admin/style/dc_logo_small.png differ
diff --git a/dotclear._no/admin/style/dc_logos/b-dotclear120.png b/dotclear._no/admin/style/dc_logos/b-dotclear120.png
new file mode 100644
index 0000000..e6561d3
Binary files /dev/null and b/dotclear._no/admin/style/dc_logos/b-dotclear120.png differ
diff --git a/dotclear._no/admin/style/dc_logos/dc_logo_footer.png b/dotclear._no/admin/style/dc_logos/dc_logo_footer.png
new file mode 100644
index 0000000..f69a8e2
Binary files /dev/null and b/dotclear._no/admin/style/dc_logos/dc_logo_footer.png differ
diff --git a/dotclear._no/admin/style/dc_logos/sq-logo-32.png b/dotclear._no/admin/style/dc_logos/sq-logo-32.png
new file mode 100644
index 0000000..1a32298
Binary files /dev/null and b/dotclear._no/admin/style/dc_logos/sq-logo-32.png differ
diff --git a/dotclear._no/admin/style/dc_logos/w-dotclear180.png b/dotclear._no/admin/style/dc_logos/w-dotclear180.png
new file mode 100644
index 0000000..3a8def7
Binary files /dev/null and b/dotclear._no/admin/style/dc_logos/w-dotclear180.png differ
diff --git a/dotclear._no/admin/style/dc_logos/w-dotclear240.png b/dotclear._no/admin/style/dc_logos/w-dotclear240.png
new file mode 100644
index 0000000..ac13198
Binary files /dev/null and b/dotclear._no/admin/style/dc_logos/w-dotclear240.png differ
diff --git a/dotclear._no/admin/style/dc_logos/w-dotclear90.png b/dotclear._no/admin/style/dc_logos/w-dotclear90.png
new file mode 100644
index 0000000..0de5920
Binary files /dev/null and b/dotclear._no/admin/style/dc_logos/w-dotclear90.png differ
diff --git a/dotclear._no/admin/style/default-dark.css b/dotclear._no/admin/style/default-dark.css
new file mode 100644
index 0000000..8bd6826
--- /dev/null
+++ b/dotclear._no/admin/style/default-dark.css
@@ -0,0 +1,3866 @@
+@charset "UTF-8";
+/** --------------------------------------------------
+ Start
+--------------------------------------------------- */
+/* largeur des paddings et border compris dans "width" */
+*,
+*:before,
+*:after {
+ box-sizing: border-box; }
+
+html {
+ font-size: 100%;
+ -ms-text-size-adjust: 100%;
+ -webkit-text-size-adjust: 100%;
+ margin: 0;
+ padding: 0; }
+
+body {
+ margin: 0;
+ padding: 0; }
+
+a {
+ background: transparent; }
+ a:focus {
+ outline: thin dotted; }
+ a:active, a:hover {
+ outline: none; }
+ a img {
+ border: none; }
+
+q,
+cite {
+ font-style: italic; }
+
+q:before,
+q:after {
+ content: ""; }
+
+sup,
+sub {
+ font-size: .75em;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline; }
+
+sup {
+ top: -0.5em; }
+
+sub {
+ bottom: -0.25em; }
+
+abbr[title] {
+ border-bottom: 1px dotted;
+ cursor: help; }
+
+b,
+strong {
+ font-weight: bold;
+ font-size: 0.9375em; }
+
+small {
+ font-size: 80%; }
+
+dfn {
+ font-style: italic; }
+
+hr {
+ box-sizing: content-box;
+ height: 0; }
+
+mark {
+ background: #ff0;
+ color: #000; }
+
+code,
+kbd,
+pre,
+samp {
+ font-family: monospace, serif;
+ font-size: 1em; }
+
+pre {
+ white-space: pre-wrap; }
+
+fieldset {
+ margin: 0;
+ padding: 0;
+ border: none; }
+
+input,
+button,
+select {
+ vertical-align: middle; }
+
+button,
+input,
+select,
+textarea {
+ font-family: inherit;
+ font-size: 100%;
+ margin: 0; }
+
+button,
+input {
+ line-height: normal; }
+
+button,
+html input[type="button"],
+input[type="reset"],
+input[type="submit"] {
+ -webkit-appearance: button;
+ cursor: pointer; }
+
+button[disabled],
+html input[disabled] {
+ cursor: default; }
+
+input[type="checkbox"],
+input[type="radio"] {
+ padding: 0;
+ border: none; }
+
+input[type="search"] {
+ -webkit-appearance: textfield; }
+
+input[type="search"]::-webkit-search-cancel-button,
+input[type="search"]::-webkit-search-decoration {
+ -webkit-appearance: none; }
+
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+ border: 0;
+ padding: 0; }
+
+textarea {
+ overflow: auto;
+ vertical-align: top; }
+
+table {
+ border-collapse: collapse;
+ margin-bottom: 1.5em; }
+
+td,
+th {
+ padding: 1px;
+ vertical-align: top;
+ text-align: left; }
+
+td:first-child,
+th:first-child {
+ empty-cells: hide; }
+
+/* scripts */
+body > script {
+ display: none !important; }
+
+/* HTML5 for old browsers */
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+main,
+nav,
+section,
+summary {
+ display: block; }
+
+audio,
+canvas,
+video {
+ display: inline-block; }
+
+audio:not([controls]) {
+ display: none;
+ height: 0; }
+
+figure {
+ margin: 0; }
+
+[hidden],
+template {
+ display: none; }
+
+svg:not(:root) {
+ overflow: hidden; }
+
+/* Headings reset */
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-size: 1em;
+ font-weight: normal;
+ margin: 0; }
+
+/* Screen-reader only */
+.visually-hidden {
+ border: 0;
+ clip: rect(0 0 0 0);
+ clip-path: inset(50%);
+ height: 1px;
+ margin: -1px;
+ overflow: hidden;
+ padding: 0;
+ position: absolute;
+ width: 1px;
+ white-space: nowrap; }
+ .visually-hidden:focus, .visually-hidden:active {
+ clip: auto;
+ clip-path: none;
+ height: auto;
+ overflow: visible;
+ position: static;
+ width: auto;
+ white-space: normal; }
+
+.sr-only {
+ border: 0;
+ clip: rect(0 0 0 0);
+ clip-path: inset(50%);
+ height: 1px;
+ margin: -1px;
+ overflow: hidden;
+ padding: 0;
+ position: absolute;
+ width: 1px;
+ white-space: nowrap; }
+
+/** --------------------------------------------------
+ Colors
+--------------------------------------------------- */
+/* bright red */
+/* slategray */
+/* soft dark blue */
+/** --------------------------------------------------
+ Common rules
+--------------------------------------------------- */
+/* Typographie */
+:root {
+ --html-font-size: 62.5%;
+ --body-color: #dcdee0;
+ --body-background: #272b30; }
+
+html {
+ font-size: 62.5%;
+ font-size: var(--html-font-size); }
+
+body {
+ color: #dcdee0;
+ background: #272b30;
+ font-size: 1.4rem;
+ line-height: 1.5;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; }
+ @media screen and (max-width: 26.5em) {
+ body.responsive-font {
+ font-size: 1.2rem;
+ line-height: 1.3; } }
+ @media screen and (min-width: 120em) {
+ body.responsive-font {
+ font-size: 1.6rem;
+ line-height: 1.5; } }
+ @media screen and (min-width: 26.5em) and (max-width: 120em) {
+ body.responsive-font {
+ font-size: calc( 1.2rem + 0.4 * (100vw - 42.4rem) / (192 - 42.4));
+ line-height: calc( 1.3em + (1.5 - 1.3) * (100vw - 42.4rem) / (192 - 42.4)); } }
+
+@media screen and (max-width: 26.5em) {
+ #wrapper {
+ font-size: 1.2em; } }
+
+h2,
+h3,
+.as_h3,
+h4,
+.as_h4,
+h5,
+h6 {
+ margin-top: 0;
+ margin-bottom: 1em; }
+
+h2 {
+ color: #b2b5ba;
+ font-size: 1.5em;
+ font-weight: normal;
+ line-height: 1.25;
+ padding: 0 0 1.5em; }
+
+h3,
+.as_h3 {
+ color: #ff6e3a;
+ font-size: 1.34em;
+ font-weight: normal;
+ line-height: 1.5;
+ margin-top: 1em; }
+
+h4,
+.as_h4 {
+ color: #b2b5ba;
+ font-size: 1.16em;
+ line-height: 1.5; }
+
+h5 {
+ color: #b2b5ba;
+ font-size: 1em;
+ line-height: 1.5;
+ font-weight: bold; }
+
+h6 {
+ color: #b2b5ba;
+ font-size: 1em;
+ line-height: 1.5; }
+
+/** --------------------------------------------------
+ Layout
+--------------------------------------------------- */
+#dotclear-admin {
+ display: flex;
+ min-height: 100vh;
+ flex-direction: column; }
+
+#header {
+ color: #dcdee0;
+ background: #272b30;
+ border-bottom: 4px solid #9ac123;
+ width: 99.99%;
+ /* Bugfix Chrome >= 49.0.2623.108 */
+ display: table;
+ position: relative; }
+
+h1,
+#top-info-blog,
+#top-info-user {
+ display: table-cell;
+ padding: 8px 0;
+ margin: 0;
+ font-size: 1em;
+ vertical-align: top; }
+
+#wrapper {
+ background: #272b30;
+ position: relative;
+ padding-top: 1.5em;
+ float: left;
+ width: 100%;
+ z-index: 10;
+ flex: 1; }
+
+.with-js #wrapper {
+ padding-top: 0; }
+
+#main {
+ width: 100%;
+ float: right;
+ margin-left: -14em;
+ margin-top: 0; }
+
+#content {
+ background: #272b30;
+ margin: 0 0 0 14em;
+ padding: .5em 1.5em .75em 2.5em; }
+
+#main-menu {
+ background: #4c4d4f;
+ width: 14em;
+ float: left;
+ margin: 0;
+ padding-top: .5em;
+ padding-bottom: 1em;
+ overflow: hidden; }
+
+#footer {
+ background-color: #272b30;
+ border-top: 1px solid #cbced1;
+ clear: both;
+ position: relative;
+ padding: .5em 0 .5em 1em;
+ text-align: left; }
+
+/* to hide main-menu */
+#collapser {
+ background: #595b5d;
+ position: absolute;
+ top: 0;
+ left: 14em;
+ width: 10px;
+ height: 100%;
+ overflow: hidden;
+ display: block;
+ border-right: 0;
+ border-bottom: 0;
+ z-index: 1;
+ transition: none; }
+ #collapser:hover, #collapser:focus {
+ background: #9ac123; }
+ .hide-mm #collapser {
+ background: #9ac123; }
+ .hide-mm #collapser:hover, .hide-mm #collapser:focus {
+ background: #595b5d; }
+
+.expand-mm {
+ display: none; }
+
+/* if main-menu is hidden */
+.hide-mm #main {
+ margin-left: 0; }
+.hide-mm #content {
+ margin-left: 10px; }
+ .hide-mm #content > h2 {
+ margin-left: calc(-1em - 10px); }
+.hide-mm #main-menu {
+ display: none; }
+.hide-mm #collapser {
+ left: 0; }
+.hide-mm .collapse-mm {
+ display: none; }
+.hide-mm .expand-mm {
+ display: block; }
+
+#wrapper.hide-mm {
+ background: #272b30; }
+
+/* -------------------------------------------------------------- layout: two-cols */
+.two-cols {
+ position: static; }
+ .two-cols .col {
+ width: 48%;
+ margin-left: 2%;
+ float: left; }
+ .two-cols .col:first-child {
+ margin-left: 0;
+ margin-right: 2%; }
+ .two-cols .col:last-child {
+ margin-left: 2%;
+ margin-right: 0; }
+ .two-cols .col70 {
+ width: 68%;
+ margin-left: 0;
+ float: left; }
+ .two-cols .col70.last-col {
+ margin-left: 2%;
+ margin-right: 0; }
+ .two-cols .col30 {
+ width: 28%;
+ margin-left: 2%;
+ float: left; }
+ .two-cols .col30.first-col {
+ margin-left: 0;
+ margin-right: 2%; }
+ .two-cols table {
+ width: 90%; }
+
+/* -------------------------------------------------------------- layout: three-cols */
+.three-cols {
+ position: static; }
+ .three-cols .col {
+ width: 32.3%;
+ float: left;
+ margin-left: 1%; }
+ .three-cols .col:first-child {
+ margin-left: 0; }
+
+/* ------------------------------------------------- layout: optionnal one/two/three-boxes */
+.one-box {
+ text-align: justify; }
+
+.two-boxes {
+ width: 48.5%; }
+
+.three-boxes {
+ width: 30%; }
+
+.two-boxes,
+.three-boxes {
+ display: inline-block;
+ vertical-align: top;
+ margin: 0 1.5% 1em;
+ text-align: left; }
+
+.two-boxes:nth-of-type(odd),
+.three-boxes:nth-of-type(3n+1) {
+ margin-left: 0; }
+
+.two-boxes:nth-of-type(even),
+.three-boxes:nth-of-type(3n) {
+ margin-right: 0; }
+
+/* ---------------------------------------------------------------- layout: popups */
+.popup h1 {
+ color: #dcdee0;
+ background: #595b5d;
+ display: block;
+ width: 100%;
+ margin: 0;
+ font-size: 1.5em;
+ text-indent: 1em;
+ line-height: 1.5em;
+ font-weight: normal; }
+.popup #wrapper {
+ display: block;
+ float: none;
+ width: 100%;
+ margin: 0;
+ padding: 0;
+ background-position: 0 0; }
+.popup #main {
+ margin: 0;
+ padding: 0; }
+.popup #content {
+ margin: 0;
+ padding: 1em; }
+ .popup #content h2 {
+ margin: 0 0 1em 0;
+ padding: 0; }
+.popup #footer p {
+ border: none; }
+
+/* -------------------------------------------------------- layout: classes de complément */
+.constrained {
+ margin: 0;
+ padding: 0;
+ border: none;
+ background: transparent; }
+
+.table {
+ display: table; }
+
+.cell {
+ display: table-cell;
+ vertical-align: top; }
+
+.clear {
+ clear: both; }
+
+.lclear {
+ clear: left; }
+
+.clearer {
+ height: 1px;
+ font-size: 1px; }
+
+/* Micro clearfix thx to Nicolas Gallagher */
+.clearfix:before,
+.clearfix:after {
+ content: " ";
+ display: table; }
+
+.clearfix:after {
+ clear: both; }
+
+.box {
+ display: inline-block;
+ vertical-align: top;
+ margin: 0 10px 10px;
+ text-align: left;
+ flex: 1 1 auto; }
+ .box.small {
+ flex-basis: 312px; }
+ .box.medium {
+ flex-basis: 644px; }
+ .box.large {
+ width: 100%; }
+
+.odd {
+ margin-left: 0; }
+
+.even {
+ margin-right: 0; }
+
+/* ------------------------------------------------------------------------------------
+ UN POIL DE MEDIA QUERIES
+------------------------------------------------------------------------------------ */
+@media screen and (max-width: 80em) {
+ #header {
+ display: block;
+ width: 100%;
+ text-align: right; }
+ #header h1,
+ #header h1 a {
+ width: 120px;
+ margin: 0; }
+
+ h1 {
+ width: 19.5em;
+ display: inline-block;
+ vertical-align: top;
+ margin-right: 1em; }
+
+ #top-info-blog {
+ display: inline-block;
+ vertical-align: top;
+ margin-right: 1em; }
+ #top-info-blog #switchblog {
+ max-width: 16em; }
+ #top-info-blog a {
+ margin-left: 2em; }
+
+ #top-info-user {
+ display: block;
+ width: 100%; }
+
+ #collapser {
+ left: 17em; }
+
+ #main {
+ margin-left: -17em; }
+
+ #content {
+ margin: 0 0 0 17em; }
+
+ #main-menu {
+ width: 17em; }
+
+ .three-boxes,
+ .three-boxes .box,
+ .two-cols .col70,
+ .two-cols .col30 {
+ width: 100%;
+ margin-left: 0;
+ margin-right: 0; } }
+@media screen and (max-width: 48em) {
+ #dashboard-boxes .box.medium,
+ .box.medium,
+ #dashboard-boxes .box.small,
+ .box.small,
+ #dashboard-boxes .box.large,
+ .box.large {
+ width: 95%;
+ margin: 10px auto; } }
+@media screen and (max-width: 44em) {
+ #help-button {
+ height: 26px;
+ width: 26px;
+ margin: 0;
+ overflow: hidden; }
+
+ #content.with-help #help-button {
+ top: 10em; }
+
+ .one-box,
+ .two-boxes,
+ .box,
+ .two-cols .col {
+ width: 96%;
+ margin-left: 0;
+ margin-right: 0; } }
+@media screen and (max-width: 38em) {
+ #header h1,
+ #header h1 a {
+ width: 42px !important;
+ height: 42px; }
+
+ #wrapper,
+ #main,
+ #main-menu {
+ display: block;
+ float: none;
+ width: 100%;
+ margin: 0; }
+
+ #main-menu a {
+ display: block;
+ width: 100%; }
+ #main-menu h3 a {
+ display: inline; }
+
+ #content,
+ .hide-mm #content {
+ margin: 0; }
+
+ #collapser {
+ display: none; }
+
+ #main #content > h2 {
+ margin: 0 -.25em 1em; }
+
+ #dashboard-boxes .box.medium,
+ .box.medium,
+ #dashboard-boxes .box.small,
+ .box.small,
+ #dashboard-boxes .box.large,
+ .box.large {
+ width: 95%;
+ margin: 10px auto; }
+
+ .cell,
+ #filters-form .cell {
+ display: inline-block;
+ vertical-align: bottom; }
+
+ .pseudo-tabs li {
+ display: block;
+ float: left;
+ width: 50%; } }
+@media screen and (max-width: 26.5em) {
+ #top-info-blog label,
+ .nomobile {
+ display: none; }
+
+ #top-info-blog {
+ margin-bottom: .5em;
+ max-width: 75%; }
+ #top-info-blog select {
+ margin-bottom: .5em; }
+
+ #content.with-help #help-button {
+ top: 10em;
+ right: 28rem; }
+ #content.with-help #help {
+ width: 28rem; }
+
+ p.top-add {
+ margin-bottom: .5em; }
+
+ .part-tabs ul {
+ margin: 1em 0; }
+
+ .part-tabs li a {
+ display: block;
+ width: 100%; }
+
+ #icons p {
+ width: 9em; }
+
+ .media-item {
+ width: 90%; }
+
+ #theme-new,
+ #theme-activate,
+ #theme-deactivate {
+ margin-left: 0;
+ margin-right: 0; }
+
+ .box.current-theme {
+ margin: 5px;
+ width: 100%; }
+
+ .current-theme .module-sshot img {
+ margin: 0;
+ float: none;
+ max-width: 100%; }
+
+ table .maximal {
+ min-width: 14em; }
+
+ .pseudo-tabs li {
+ display: block;
+ width: 100%;
+ float: none; } }
+/** --------------------------------------------------
+ Elements
+--------------------------------------------------- */
+/* ------------------------------------------------------------------ titres */
+/* fil d'ariane */
+#content > h2 {
+ padding: 0 1em .5em 1em;
+ margin: 0 -1em 1em -1em;
+ background: #272b30;
+ border-bottom: 1px solid #86888c; }
+
+/* page courante dans le fil d'ariane */
+.page-title {
+ color: #f783b3; }
+ .page-title img {
+ padding-left: .5em;
+ vertical-align: middle; }
+
+/* autres titres */
+#main-menu h3 {
+ font-weight: bold; }
+
+.fieldset h3,
+.fieldset h4,
+.pretty-title {
+ color: #ff6e3a;
+ font-size: 1em;
+ font-weight: bold; }
+
+.fieldset h3 {
+ font-size: 1.17em; }
+
+.fieldset h3.smart-title,
+.fieldset h4.smart-title,
+.smart-title {
+ font-size: 1em;
+ text-transform: uppercase;
+ font-weight: bold;
+ color: #f3f4f5; }
+
+#entry-sidebar h5 {
+ font-weight: normal;
+ color: #ecedee; }
+
+.entry-status img.img_select_option {
+ padding-left: 4px;
+ vertical-align: -1px; }
+
+h4 label,
+h5 label {
+ color: #ecedee; }
+
+h2:first-child,
+h3:first-child,
+h4:first-child,
+h5:first-child,
+ul:first-child,
+p:first-child {
+ margin-top: 0; }
+
+/* ---------------------------------------------------------------- tableaux */
+/* Pour autoriser le scroll sur les petites largeurs
+ envelopper les tableaux dans une div.table-outer */
+.table-outer {
+ width: 100%;
+ overflow: auto; }
+
+table {
+ font-size: 1em;
+ border-collapse: collapse;
+ margin: 0 0 1em 0;
+ width: 100%; }
+
+caption {
+ color: #dcdee0;
+ font-weight: bold;
+ text-align: left;
+ margin-bottom: .5em; }
+
+th:not(.module-name) {
+ border-width: 1px 0 1px 0;
+ border-style: solid;
+ border-color: #dbdcdd;
+ background: #595b5d;
+ padding: .4em 1em .4em .5em;
+ vertical-align: top;
+ text-align: left; }
+
+td, th.module-name {
+ font-weight: normal;
+ border-width: 0 0 1px 0;
+ border-style: solid;
+ border-color: #ecedee;
+ padding: .4em 1em .4em .5em;
+ vertical-align: top; }
+
+/* ---------------------------------------------------------- autres balises */
+p {
+ margin: 0 0 1em 0; }
+
+hr {
+ height: 1px;
+ border-width: 1px 0 0;
+ border-color: #c9cbcf;
+ background: #c9cbcf;
+ border-style: solid; }
+ hr.clearer {
+ clear: both; }
+
+pre,
+code,
+#debug {
+ font: 100% "Andale Mono", AndaleMono, Consolas, Monaco, "Courier New", monospace; }
+
+code {
+ color: #272b30;
+ background: #fefacd; }
+
+pre {
+ white-space: pre;
+ white-space: -moz-pre-wrap;
+ white-space: pre-wrap;
+ white-space: pre-line;
+ word-wrap: break-word; }
+
+abbr {
+ cursor: help; }
+
+input,
+textarea,
+select,
+option,
+optgroup,
+legend,
+label {
+ font-size: 1em; }
+
+/* ------------------------------------------------------------------ liens */
+a,
+a:link,
+a:visited {
+ color: #76c2f1;
+ text-decoration: none;
+ border-bottom: 1px dotted #86888c;
+ background-color: inherit;
+ outline: 0; }
+ a img,
+ a:link img,
+ a:visited img {
+ border: none;
+ background: initial; }
+
+a:hover,
+a:active {
+ border-bottom-style: solid; }
+
+h1 a:link,
+h1 a:visited {
+ border: none; }
+
+.discrete a {
+ color: #dcdee0; }
+
+a:link {
+ transition: .5s; }
+
+a:focus,
+a:focus img {
+ outline: 2px solid #bee74b;
+ border-bottom: none;
+ text-decoration: none; }
+
+a.outgoing img, .outgoing-js {
+ width: .75em;
+ filter: contrast(100%); }
+ #header a.outgoing img, #header .outgoing-js {
+ width: 1.25em;
+ padding: 0 0 0 .5em;
+ vertical-align: initial;
+ filter: contrast(100%); }
+
+input[type=text],
+input[type=color],
+input[type=email],
+input[type=url],
+input[type=datetime-local],
+input[type=date],
+input[type=time],
+input[type=file],
+input[type=number],
+input[type=password],
+input[type=submit],
+input[type=button],
+input[type=reset],
+a.button, button, textarea, select, legend {
+ max-width: 100%; }
+
+input[type=text],
+input[type=color],
+input[type=email],
+input[type=url],
+input[type=datetime-local],
+input[type=date],
+input[type=time],
+input[type=file],
+input[type=number],
+input[type=password],
+input[type=submit],
+input[type=button],
+input[type=reset],
+a.button, button, textarea, legend {
+ border-radius: 3px; }
+
+form {
+ display: block;
+ margin: 0;
+ padding: 0; }
+
+fieldset {
+ display: block;
+ margin: 1em 0;
+ padding: 1em 0.5em;
+ border-width: 1px 0;
+ border-style: solid;
+ border-color: #cbced1;
+ background: #272b30; }
+ fieldset hr {
+ background-color: #cbced1;
+ border-width: 0;
+ margin: 1em 0; }
+ fieldset:focus-within {
+ background-color: #363a3e; }
+
+input[type=text],
+input[type=color],
+input[type=email],
+input[type=url],
+input[type=datetime-local],
+input[type=date],
+input[type=time],
+input[type=file],
+input[type=number],
+textarea {
+ font-family: inherit;
+ font-size: 100%; }
+
+legend {
+ padding: 0.2em 0.6em;
+ border-width: 1px;
+ border-style: solid;
+ border-color: #cbced1;
+ background: #595b5d;
+ margin-bottom: 0.5em; }
+
+label .maximal, textarea.maximal, input.maximal, select.maximal {
+ width: 99%; }
+
+input[type=text],
+input[type=password],
+input[type=color],
+input[type=email],
+input[type=url],
+input[type=datetime-local],
+input[type=date],
+input[type=time],
+input[type=file],
+input[type=number],
+textarea, select, input:not([type=file]):invalid, input:not([type=file]):invalid:placeholder-shown {
+ box-shadow: 1px 1px 2px #ecedee inset;
+ padding: 3px;
+ vertical-align: top; }
+
+input[type=text],
+input[type=password],
+input[type=color],
+input[type=email],
+input[type=url],
+input[type=datetime-local],
+input[type=date],
+input[type=time],
+input[type=file],
+input[type=number],
+textarea, input:not([type=file]):invalid, input:not([type=file]):invalid:placeholder-shown {
+ border-width: 1px;
+ border-style: solid;
+ border-color: #767d86; }
+
+input:invalid:not(:required), textarea:invalid:not(:required), select:invalid:not(:required), input:not([type=file]):invalid:not(:focus):not(:required) {
+ color: #000;
+ box-shadow: 0 0 0 3px rgba(174, 50, 59, 0.3); }
+
+input:invalid:not(:required), textarea:invalid:not(:required), input:not([type=file]):invalid:not(:focus):not(:required) {
+ border: 1px solid #ae323b;
+ background: #ffbaba; }
+
+input:focus, textarea:focus {
+ border-color: #bee74b; }
+
+textarea {
+ padding: 2px 0; }
+ textarea.maximal {
+ resize: vertical; }
+ .area textarea {
+ display: block;
+ width: 100%;
+ resize: vertical; }
+
+select {
+ padding: 2px 0;
+ vertical-align: middle; }
+
+@media not all and (min-resolution: 0.001dpcm) {
+ @supports (-webkit-appearance: none) {
+ /* Safari 10.1+ only (https://browserstrangeness.bitbucket.io/css_hacks.html#webkit) */
+ select {
+ font-size: initial; } } }
+select.l10n option {
+ padding-left: 16px; }
+
+option.avail10n {
+ background: transparent url(../images/check-on.png) no-repeat 0 50%; }
+
+input[type=text],
+input[type=color],
+input[type=email],
+input[type=url],
+input[type=datetime-local],
+input[type=date],
+input[type=time],
+input[type=number],
+input[type=password],
+textarea {
+ margin-right: .3em; }
+
+input[type=checkbox], input[type=radio], input[type=file] {
+ border: none;
+ margin: 0 .33em 0 0;
+ padding: 0; }
+
+input + input[type=checkbox] {
+ margin-left: .33em; }
+
+a input {
+ margin-right: .33em; }
+
+input[type=file] {
+ margin-top: .3em;
+ margin-bottom: .3em; }
+
+input[type=color] {
+ width: 4em;
+ height: 3em; }
+
+optgroup {
+ font-weight: bold;
+ font-style: normal; }
+
+option {
+ font-weight: normal; }
+
+label, label span {
+ display: block; }
+
+label.ib, input.ib {
+ display: inline-block; }
+
+label.classic {
+ display: inline; }
+
+label.classic input, label span input, label.classic select, label span select {
+ display: inline; }
+
+label.required {
+ font-weight: bold; }
+
+label.required abbr {
+ color: #f783b3;
+ font-size: 1.3em;
+ text-decoration: none; }
+
+label.bold {
+ text-transform: uppercase;
+ font-weight: bold;
+ margin-top: 2em; }
+
+label.area, p.area, div.area {
+ width: inherit !important; }
+
+div.area {
+ margin-bottom: 1em; }
+
+p.field {
+ position: relative; }
+ p.field label {
+ display: inline-block;
+ width: 14em; }
+ p.field.wide label {
+ width: 21em; }
+ p.field input, p.field select {
+ display: inline-block; }
+
+.form-note, .form-stats {
+ font-style: italic;
+ font-weight: normal;
+ color: #bce8f1; }
+
+p.form-note, p.form-stats {
+ margin-top: -.7em; }
+
+span.form-note, span.form-stats {
+ text-transform: none; }
+
+.missing {
+ background-color: inherit;
+ animation-name: kf-missing;
+ animation-duration: 1s; }
+
+@keyframes kf-missing {
+ 50% {
+ background-color: #ffbaba; } }
+.focus {
+ background-color: inherit;
+ animation-name: kf-focus;
+ animation-duration: 1s; }
+
+@keyframes kf-focus {
+ 50% {
+ background-color: #bee74b; } }
+.no-more-info {
+ display: none; }
+
+/* Removes inner padding and border in FF3+ - Knacss */
+button::-moz-focus-inner,
+input[type=button]::-moz-focus-inner,
+input[type=reset]::-moz-focus-inner,
+input[type=submit]::-moz-focus-inner {
+ border: 0;
+ padding: 0; }
+
+/* tous les boutons */
+button,
+a.button,
+input[type=button],
+input[type=reset],
+input[type=submit] {
+ border: 1px solid #86888c;
+ font-family: inherit;
+ padding: 3px 10px;
+ line-height: normal !important;
+ display: inline-block;
+ font-size: 100%;
+ text-align: center;
+ text-decoration: none;
+ cursor: pointer;
+ position: relative;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
+ border-style: solid;
+ border-width: 1px; }
+
+a.button {
+ vertical-align: middle; }
+
+/* validation */
+input[type=submit],
+button[type=submit],
+a.button.submit,
+button.submit,
+input.button.start {
+ color: #fff;
+ background-color: #25a6e1;
+ background-image: linear-gradient(to bottom, #25a6e1, #188bc0);
+ border-color: #25a6e1; }
+
+input[type=submit]:hover,
+input[type=submit]:focus,
+button[type=submit]:hover,
+button[type=submit]:focus,
+input.button.start:hover,
+input.button.start:focus,
+button.submit:hover,
+button.submit:focus,
+a.button.submit:hover,
+a.button.submit:focus {
+ background-color: #188bc0;
+ background-image: linear-gradient(to bottom, #188bc0, #25a6e1);
+ border-color: #188bc0; }
+
+/* suppression, reset, "neutres" fond gris */
+button,
+input[type=button],
+input.button,
+input[type=reset],
+input[type=submit].reset,
+input.reset,
+input[type=submit].delete,
+input.delete,
+a.button,
+a.button.delete,
+a.button.reset {
+ color: #000;
+ background-color: #eaeaea;
+ background-image: linear-gradient(to bottom, #f9f9f9, #eaeaea);
+ background-repeat: repeat-x;
+ border-color: #86888c; }
+
+button:hover,
+input[type=button]:hover,
+input.button:hover,
+button:focus,
+input[type=button]:focus,
+input.button:focus,
+input[type=reset]:hover,
+input[type=submit].reset:hover,
+input.reset:hover,
+input[type=reset]:focus,
+input[type=submit].reset:focus,
+input.reset:focus,
+input[type=submit].delete:hover,
+input.delete:hover,
+input[type=submit].delete:focus,
+input.delete:focus,
+a.button.delete:hover,
+a.button.reset:hover,
+a.button:hover,
+a.button.delete:focus,
+a.button.reset:focus,
+a.button:focus {
+ background-color: #dadada;
+ background-image: linear-gradient(to bottom, #eaeaea, #dadada);
+ background-repeat: repeat-x;
+ border-color: #86888c; }
+
+/* suppression */
+input[type=submit].delete,
+input.delete,
+button[type=submit].delete,
+button.delete,
+a.button.delete {
+ color: #ae323b; }
+
+input[type=submit].delete:hover,
+input.delete:hover,
+button[type=submit].delete:hover,
+button.delete:hover,
+a.button.delete:hover,
+input[type=submit].delete:focus,
+input.delete:focus,
+button[type=submit].delete:focus,
+button.delete:focus,
+a.button.delete:focus {
+ color: #fff;
+ background-color: #b33630;
+ background-image: linear-gradient(to bottom, #dc5f59, #b33630);
+ background-repeat: repeat-x;
+ border-color: #dc5f59; }
+
+#info-box a.button,
+#info-box button {
+ padding: 0 .5em;
+ margin-left: 2em; }
+
+.button.add,
+button.add {
+ color: #000;
+ background-color: #bee74b;
+ background-image: linear-gradient(to bottom, #bee74b, #9bca1c);
+ border-color: #bee74b;
+ padding: .33em 1.33em .5em; }
+
+.button.add:hover,
+.button.add:active,
+.button.add:focus,
+button.add:hover,
+button.add:active,
+button.add:focus {
+ background-color: #9bca1c;
+ background-image: linear-gradient(to bottom, #9bca1c, #bee74b);
+ border-color: #9bca1c; }
+
+.button-add:focus {
+ outline: dotted 1px; }
+
+/* paragraphe pour bouton Nouveau bidule */
+p.top-add {
+ text-align: right;
+ margin: 0; }
+
+/* disabled */
+input.disabled,
+input[type=submit].disabled,
+button.disabled,
+button[type=submit].disabled {
+ color: #676e78;
+ background: #f3f4f5;
+ border: 1px solid #86888c; }
+
+input.disabled:hover,
+input[type=submit].disabled:hover,
+button.disabled:hover,
+button[type=submit].disabled:hover {
+ color: #676e78;
+ background: #ecedee;
+ border: 1px solid #86888c; }
+
+/* Boutons javascript (dépliage/repliage, …) */
+.void-btn {
+ border: none;
+ border-radius: 0;
+ padding: 0; }
+
+button.details-cmd {
+ font-size: 0.9em;
+ border: none;
+ border-radius: 0;
+ padding: 0;
+ margin: 0 5px 0 0;
+ color: currentColor;
+ background: transparent;
+ box-shadow: none; }
+ button.details-cmd:hover, button.details-cmd:focus {
+ background: transparent;
+ color: #bee74b; }
+
+/* specific buttons */
+.checkbox-helper,
+#gototop,
+.metaGetList,
+.metaGetMore,
+a.checkbox-helper,
+a#gototop,
+a.metaGetList,
+a.metaGetMore {
+ font-size: 0.825em;
+ color: #323334;
+ background: #fff;
+ box-shadow: none;
+ border: 1px solid #676e78;
+ margin-bottom: .25em;
+ text-align: center; }
+ .checkbox-helper:hover,
+ #gototop:hover,
+ .metaGetList:hover,
+ .metaGetMore:hover,
+ a.checkbox-helper:hover,
+ a#gototop:hover,
+ a.metaGetList:hover,
+ a.metaGetMore:hover {
+ background: #a2cbe9;
+ box-shadow: none;
+ border: 1px solid #676e78; }
+
+#gototop {
+ display: none;
+ z-index: 1000;
+ position: fixed;
+ bottom: 0;
+ right: .5em;
+ width: 10em;
+ padding: .25em;
+ border-radius: .25em; }
+ #gototop a,
+ #gototop a:link,
+ #gototop a:hover,
+ #gototop a:active {
+ color: #323334;
+ background: transparent;
+ border: none; }
+
+.metaRemove,
+.addMeta button:not(.metaGetMore),
+.addMeta a:not(.metaGetMore) {
+ border: none;
+ border-radius: 0;
+ padding: 0;
+ color: #fff;
+ background: transparent; }
+
+.addMeta button:not(.metaGetMore),
+.addMeta a:not(.metaGetMore) {
+ box-shadow: initial;
+ margin-bottom: 2px; }
+ .addMeta button:not(.metaGetMore):hover, .addMeta button:not(.metaGetMore):focus,
+ .addMeta a:not(.metaGetMore):hover,
+ .addMeta a:not(.metaGetMore):focus {
+ color: #000;
+ background: #abd0eb; }
+
+.warn,
+.warning,
+.info {
+ font-style: normal;
+ padding: .2em .66em .2em;
+ text-indent: 24px;
+ display: inline-block;
+ line-height: 1.5em;
+ border-radius: 3px; }
+
+.info {
+ color: #323334;
+ background: #d9edf7 url(msg-info.png) no-repeat 0.3em 0.3em;
+ border: 1px solid #bce8f1; }
+
+.warn,
+.warning {
+ color: #323334;
+ background: #fefacd url(msg-warning.png) no-repeat 0.3em 0.3em;
+ border: 1px solid #ffd478; }
+
+div.warn,
+div.warning,
+div.info {
+ display: block;
+ padding: 1em 1em .33em 1em;
+ margin-bottom: 1em; }
+
+span.warn,
+span.warning,
+span.info {
+ padding-top: 1px;
+ padding-bottom: 1px;
+ background-position: .3em .2em; }
+
+.error,
+.message,
+.static-msg,
+.success,
+.warning-msg {
+ padding: 1em 0.5em 0.5em 48px;
+ margin-bottom: 1em;
+ border-radius: 8px;
+ box-shadow: 1px 1px 2px rgba(50, 51, 52, 0.1); }
+
+p.error,
+p.message,
+p.static-msg,
+p.success,
+p.warning-msg {
+ padding-top: 1em;
+ padding-bottom: 1em;
+ margin-top: .5em; }
+
+.error {
+ background: #ffbaba url(msg-error.png) no-repeat 0.7em 0.7em;
+ color: #000;
+ animation-name: kf-error;
+ animation-duration: .5s; }
+
+@keyframes kf-error {
+ 0% {
+ background-color: #fefacd; }
+ 100% {
+ background-color: #ffbaba; } }
+.message,
+.static-msg {
+ color: #fff;
+ background: #676e78 url(msg-std.png) no-repeat 0.7em 0.7em; }
+
+.message {
+ animation-name: kf-message;
+ animation-duration: .5s; }
+
+@keyframes kf-message {
+ 0% {
+ background-color: #cbced1; }
+ 100% {
+ background-color: #676e78; } }
+.message a,
+.static-msg a,
+.message h3,
+.static-msg h3 {
+ color: #fff; }
+
+.success {
+ color: #000; }
+
+.success {
+ background: #bee74b url(msg-success.png) no-repeat 0.7em 0.7em;
+ animation-name: kf-success;
+ animation-duration: .5s; }
+
+@keyframes kf-success {
+ 0% {
+ background-color: #9bca1c; }
+ 100% {
+ background-color: #bee74b; } }
+.warning-msg {
+ color: #323334;
+ background: #ffd478 url(msg-warning.png) no-repeat 0.7em 0.7em;
+ border: 1px solid #ffd478;
+ animation-name: kf-warning;
+ animation-duration: .5s; }
+
+@keyframes kf-warning {
+ 0% {
+ background-color: #fefacd; }
+ 100% {
+ background-color: #ffd478; } }
+.success a,
+.warning-msg a,
+.info a {
+ color: #323334; }
+
+.close-notice-parent {
+ display: flex;
+ justify-content: space-between; }
+ .close-notice-parent ul, .close-notice-parent p + p {
+ flex: 1; }
+ .close-notice-parent p + p {
+ padding-left: .25em; }
+
+.close-notice {
+ background: none;
+ border: none;
+ border-radius: 0;
+ box-shadow: none;
+ padding-left: 1em; }
+ .close-notice:hover, .close-notice:focus {
+ background: none; }
+
+.dc-update {
+ padding: 1em 48px 0.5em 48px;
+ margin-bottom: 1em;
+ border-radius: 8px;
+ color: #000;
+ background: #a2cbe9 url(msg-success.png) no-repeat 0.7em 0.7em;
+ box-shadow: 1px 1px 2px rgba(50, 51, 52, 0.1); }
+ .dc-update h3 {
+ margin-top: 0;
+ color: #000; }
+ .dc-update p {
+ display: inline-block;
+ vertical-align: middle; }
+ .dc-update a {
+ color: #000;
+ margin-right: 1em; }
+ .dc-update a.button {
+ padding: .5em 1em; }
+
+.updt-info a {
+ margin-left: 2em;
+ border-color: #000;
+ font-weight: bold; }
+
+body.ajax-loader #header {
+ border-bottom-color: #92b7d2;
+ transition: border-bottom-color .3s ease; }
+body.ajax-loader #collapser {
+ background-color: #92b7d2;
+ transition: background-color .3s ease; }
+
+/** --------------------------------------------------
+ Components
+--------------------------------------------------- */
+/* prelude */
+#prelude {
+ line-height: 1.5;
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ background: #a2cbe9;
+ width: 100%; }
+ #prelude li {
+ list-style-type: none;
+ margin: 0;
+ background: transparent;
+ display: inline; }
+ #prelude li a {
+ padding: 3px 16px 3px 8px;
+ color: #323334;
+ background: #a2cbe9;
+ text-decoration: underline; }
+ #prelude li a:hover, #prelude li a:focus {
+ background: #fff; }
+
+/* si le prélude est affiché on repousse les trucs dessous */
+#wrapper.with-prelude {
+ padding-top: 1em; }
+
+#help-button.with-prelude,
+#collapser.with-prelude {
+ top: 1em; }
+
+/* header global h1, form#top-info-blog, ul#top-info-user */
+#header a {
+ color: #dcdee0; }
+#header img {
+ vertical-align: middle;
+ padding-left: .5em; }
+
+/* h1 */
+h1 {
+ text-indent: 100%;
+ width: 16.5em; }
+ h1 a {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 150px;
+ height: 36px;
+ color: #dcdee0;
+ background: transparent url(dc_logos/b-dotclear120.png) no-repeat 0 6px;
+ transition: none; }
+ h1 a:hover, h1 a:focus {
+ background-position: 0 -94px;
+ background-color: transparent;
+ transition: none; }
+ h1 a:link {
+ transition-timing-function: ease-in-out; }
+
+/* top-info-blog */
+#top-info-blog select {
+ max-width: 20em; }
+#top-info-blog a {
+ margin-left: 1.5em; }
+#top-info-blog input[type=submit] {
+ background: #25a6e1;
+ border-color: #86888c;
+ margin-left: .33em; }
+#top-info-blog input[type=submit]:hover {
+ color: #fff;
+ background: #188bc0; }
+#top-info-blog p {
+ display: inline-block;
+ margin: 0; }
+
+/* top-info-user */
+#top-info-user {
+ padding-right: .5em;
+ list-style-type: none;
+ text-align: right; }
+ #top-info-user li {
+ display: inline-block;
+ margin-left: .5em;
+ padding-left: .5em;
+ border-left: 1px solid #86888c; }
+ #top-info-user li:first-child {
+ border-left: none; }
+ #top-info-user a.active {
+ border-width: 0;
+ border-radius: 4px;
+ margin: 0;
+ padding: 2px 8px 3px;
+ color: #fff;
+ background-color: #86888c;
+ font-weight: bold; }
+
+/* ------------------------------------------------------------------------------------
+ UN POIL DE MEDIA QUERIES
+------------------------------------------------------------------------------------ */
+@media screen and (max-width: 26.5em) {
+ h1,
+ h1 a {
+ padding: 0; } }
+@media screen and (max-width: 38em) {
+ h1 a:link {
+ background: transparent url(dc_logos/b-dotclear120.png) no-repeat -270px 6px; }
+
+ h1 a:hover,
+ h1 a:focus {
+ background: url(dc_logos/b-dotclear120.png) no-repeat -270px -94px; } }
+#main-menu div:last-child {
+ border-bottom: none; }
+#main-menu h3 {
+ margin: 0;
+ padding: 10px 0 10px 8px;
+ color: #c9cbcf;
+ font-size: 1.15em; }
+#main-menu a {
+ color: #dcdee0;
+ border-bottom-color: #cbced1; }
+#main-menu ul {
+ margin: 0 0 1.5em 0;
+ padding: 0;
+ list-style: none; }
+ #main-menu ul li {
+ display: block;
+ margin: 0.5em 0 0;
+ padding: 4px 0 1px 32px;
+ background-repeat: no-repeat;
+ background-position: 8px .3em;
+ position: relative; }
+ #main-menu ul li a::after {
+ position: absolute;
+ content: "";
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0; }
+ #main-menu ul li:hover {
+ background-color: #86888c; }
+ #main-menu ul li:first-child {
+ margin-top: 0; }
+#main-menu .active {
+ background-color: #86888c;
+ font-weight: bold; }
+ #main-menu .active a {
+ border-bottom: none;
+ color: #fff; }
+
+#favorites-menu,
+#blog-menu,
+#system-menu,
+#plugins-menu {
+ border-bottom: 1px dashed #a2cbe9; }
+
+#favorites-menu h3 {
+ font-variant: small-caps;
+ padding-top: .2em; }
+
+#search-menu {
+ padding: 4px 0 0 4px;
+ font-size: .91em; }
+ #search-menu p {
+ width: 95%;
+ margin: 0 0 .5em 0; }
+ #search-menu input[type="submit"] {
+ float: right; }
+
+#qx {
+ width: 75%;
+ background: transparent url(search.svg) no-repeat 0 center;
+ text-indent: 20px; }
+
+.part-tabs ul {
+ padding: .5em 0 0 1em;
+ border-bottom: 1px solid #ecedee;
+ line-height: 1.8; }
+.part-tabs li {
+ list-style: none;
+ margin: 0;
+ display: inline; }
+ .part-tabs li:first-child a {
+ border-top-left-radius: 3px; }
+ .part-tabs li:last-child a {
+ border-top-right-radius: 3px; }
+ .part-tabs li a {
+ padding: .33em 1.5em;
+ margin-right: -1px;
+ border: 1px solid #ecedee;
+ border-bottom: none;
+ text-decoration: none;
+ color: #323334;
+ background-color: #c9cbcf;
+ display: inline-block; }
+ .part-tabs li a:hover, .part-tabs li a:focus {
+ color: #323334;
+ background: #86888c;
+ border-bottom-color: #86888c; }
+ .part-tabs li a:link {
+ transition: unset; }
+ .part-tabs li.part-tabs-active a {
+ color: #fff;
+ background: #86888c;
+ font-weight: bold;
+ border-bottom-color: #86888c; }
+
+.multi-part {
+ padding-left: 1em; }
+
+.pseudo-tabs {
+ margin: -.75em 0 2em 0;
+ border-bottom: 1px solid #ecedee;
+ display: table;
+ width: 100%;
+ padding: 0;
+ line-height: 24px;
+ border-collapse: collapse; }
+ .pseudo-tabs li {
+ display: table-cell;
+ border-width: 0 1px;
+ border-style: solid;
+ border-color: #ecedee;
+ padding: 0;
+ margin: 0;
+ text-align: center; }
+ .pseudo-tabs a {
+ display: block;
+ font-weight: bold;
+ padding: 0 24px;
+ border-bottom: none; }
+ .pseudo-tabs a:hover, .pseudo-tabs a:focus {
+ background-color: #c9cbcf;
+ color: #323334; }
+ .pseudo-tabs a.active {
+ background-color: #86888c;
+ color: #fff; }
+
+/* contextual help */
+#help {
+ margin-top: 4em;
+ background: #323334;
+ z-index: 100;
+ clear: both;
+ padding: 0 1em; }
+ #content.with-help #help {
+ display: block;
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: 32rem;
+ border-left: 2px solid #ffd478;
+ border-top: 2px solid #ffd478;
+ margin-top: 0;
+ padding: .5em 0 0 0;
+ overflow: auto; }
+
+#help-button {
+ background: transparent url(help-mini.png) no-repeat 6px center;
+ position: absolute;
+ top: 0;
+ right: 0;
+ padding: 0 1.5em 0 30px;
+ cursor: pointer;
+ color: #76c2f1;
+ line-height: 3; }
+ #help-button.floatable {
+ border-top: 2px solid #676e78;
+ border-left: 2px solid #676e78;
+ border-bottom: 2px solid #676e78;
+ border-bottom-left-radius: 1em;
+ border-top-left-radius: 1em;
+ background-color: #323334;
+ position: fixed;
+ top: 10px;
+ -webkit-transform: translateZ(0); }
+ .no-js #help-button {
+ top: 1em; }
+ #help-button span {
+ padding: .5em 0 .1em 0; }
+ #content.with-help #help-button {
+ right: 32rem;
+ background-color: #323334;
+ position: fixed;
+ top: 6em;
+ z-index: 100;
+ border-top: 2px solid #ffd478;
+ border-left: 2px solid #ffd478;
+ border-bottom: 2px solid #ffd478;
+ border-bottom-left-radius: 1em;
+ border-top-left-radius: 1em; }
+
+.help-box {
+ display: none; }
+ .help-box ul {
+ padding-left: 20px;
+ margin-left: 0; }
+
+#content.with-help .help-content {
+ padding: 0 1em 1em; }
+.help-content dt {
+ font-weight: bold;
+ color: #a2cbe9;
+ margin: 0; }
+.help-content dd {
+ margin: 0.3em 0 1.5em 0; }
+
+/* lien d'aide générale dans le help content */
+#helplink p {
+ padding: 0 0 0 .5em; }
+
+#footer p {
+ margin: 0;
+ padding: 0 1em;
+ font-size: 1em; }
+#footer a:hover span.tooltip {
+ padding: 10px;
+ color: #910ed3;
+ height: auto;
+ width: auto;
+ left: 0;
+ bottom: 0;
+ background: #f3f4f5;
+ z-index: 99;
+ font-family: monospace;
+ text-align: left;
+ border-top: 1px solid #910ed3;
+ border-right: 1px solid #910ed3;
+ border-radius: 0 2em 0 0; }
+
+span.credit {
+ font-size: 1em;
+ font-weight: normal; }
+
+span.tooltip {
+ position: absolute;
+ padding: 0;
+ border: 0;
+ height: 1px;
+ width: 1px;
+ overflow: hidden; }
+
+/** --------------------------------------------------
+ Tables and Filters
+--------------------------------------------------- */
+table .maximal {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ max-width: 1px; }
+table .maximal, table.maximal {
+ width: 100%; }
+table .minimal {
+ width: 1px; }
+table .nowrap {
+ white-space: nowrap;
+ vertical-align: top; }
+table .count {
+ text-align: right;
+ padding-right: 1.5em; }
+
+th.first input {
+ padding-right: 34px; }
+
+th img, tr.line img {
+ vertical-align: middle; }
+ th img.expand, tr.line img.expand {
+ margin-right: 6px;
+ margin-bottom: -2px; }
+
+tr.line p {
+ margin: 0; }
+tr.line input, tr.line select {
+ vertical-align: middle;
+ box-shadow: none; }
+tr.line select {
+ width: 6em; }
+tr.line:hover {
+ background: #4c4d4f; }
+tr.line:focus-within {
+ background-color: #4c4d4f; }
+
+td.status {
+ vertical-align: middle; }
+ td.status a {
+ border: none; }
+
+.noborder td, td.noborder, .noborder th, th.noborder {
+ border-width: 0 0 1px 0;
+ border-color: #dbdcdd;
+ line-height: 2em;
+ padding-bottom: 0; }
+
+.noborder p {
+ margin-bottom: 0; }
+
+table.posts-list {
+ min-width: 50%; }
+
+table.settings, table.prefs {
+ margin-bottom: 3em; }
+ table.settings th:first-child, table.prefs th:first-child {
+ width: 20%; }
+ table.settings th + th, table.prefs th + th {
+ width: 30%; }
+ table.settings th + th + th, table.prefs th + th + th {
+ width: 10%; }
+ table.settings th:last-child, table.prefs th:last-child {
+ width: 40%; }
+
+/* js */
+td.expand {
+ padding: 1em; }
+ td.expand td {
+ border-bottom: none; }
+
+.handle {
+ padding: 0; }
+
+.handler {
+ cursor: move;
+ background: transparent url(drag.png) no-repeat 0 50%;
+ padding-left: 15px; }
+
+/* Responsive Cell Header */
+.rch td::before {
+ display: none; }
+
+@media screen and (max-width: 44em), print and (max-width: 5in) {
+ table.rch {
+ display: block; }
+ table.rch caption, table.rch tbody, table.rch tr, table.rch td {
+ display: block; }
+ table.rch th, table.rch tr:first-of-type {
+ display: none; }
+ table.rch td:first-of-type {
+ border-top: 1px solid #ecedee;
+ color: #c9cbcf;
+ background: #595b5d; }
+ table.rch td::before {
+ display: inline;
+ font-weight: bold; }
+ table.rch td {
+ display: grid;
+ grid-template-columns: 10em auto;
+ grid-gap: 1em 0.5em;
+ text-align: left;
+ border: none; }
+ table.rch .maximal {
+ max-width: inherit; }
+ table.rch .nowrap {
+ white-space: inherit; }
+ table.rch td.expand {
+ grid-template-columns: auto !important;
+ color: #dcdee0;
+ background-color: #272b30;
+ border-top: 1px dashed #ecedee; }
+ table.rch input, table.rch select {
+ align-self: center; }
+
+ table.rch-thead thead {
+ display: none; }
+ table.rch-thead tr:first-of-type {
+ display: block; } }
+a.form-control {
+ display: none;
+ color: #fff; }
+ a.form-control::before {
+ content: "►";
+ margin-right: 5px; }
+ a.form-control.open::before {
+ content: "▼"; }
+
+#filters-form {
+ border: 1px solid #a2cbe9;
+ border-radius: .3em;
+ margin-bottom: 2em;
+ padding: .5em 1em 0; }
+ #filters-form .table {
+ width: 100%;
+ padding: 0;
+ margin-bottom: 1em;
+ margin-top: .5em; }
+ #filters-form .cell {
+ padding: 0 2em 0 0; }
+ #filters-form .filters-sibling-cell {
+ padding-top: 3.8em; }
+ #filters-form .filters-options {
+ padding-left: 2em;
+ border-left: 1px solid #c9cbcf; }
+ #filters-form select {
+ width: 14em;
+ vertical-align: middle; }
+ #filters-form h4 {
+ margin-top: 0;
+ margin-bottom: 2em; }
+ #filters-form label.ib,
+ #filters-form span.ib {
+ width: 7em; }
+ #filters-form label.ibw,
+ #filters-form span.ibw {
+ width: 9em;
+ display: inline-block; }
+ #filters-form:focus-within {
+ background-color: #4c4d4f; }
+
+span.ib {
+ width: 7em; }
+
+span.ibw {
+ width: 9em;
+ display: inline-block; }
+
+/** --------------------------------------------------
+ Pages
+--------------------------------------------------- */
+#login-screen {
+ display: block;
+ width: 20em;
+ margin: 1.5em auto 0;
+ font-size: 1.16em; }
+ #login-screen h1 {
+ text-indent: -2000px;
+ background: transparent url(dc_logos/w-dotclear240.png) no-repeat top left;
+ height: 66px;
+ width: 20em;
+ margin-bottom: .5em;
+ margin-left: 0; }
+ #login-screen .fieldset {
+ border: 1px solid #9bca1c;
+ padding: 1em 1em 0 1em;
+ background: #fff;
+ margin-bottom: 0;
+ margin-top: 1em; }
+ #login-screen input[type=text],
+ #login-screen input[type=color],
+ #login-screen input[type=email],
+ #login-screen input[type=url],
+ #login-screen input[type=datetime-local],
+ #login-screen input[type=date],
+ #login-screen input[type=time],
+ #login-screen input[type=file],
+ #login-screen input[type=number],
+ #login-screen input[type=password],
+ #login-screen input[type=submit],
+ #login-screen input[type=text]:focus,
+ #login-screen input[type=color]:focus,
+ #login-screen input[type=email]:focus,
+ #login-screen input[type=url]:focus,
+ #login-screen input[type=datetime-local]:focus,
+ #login-screen input[type=date]:focus,
+ #login-screen input[type=time]:focus,
+ #login-screen input[type=file]:focus,
+ #login-screen input[type=number]:focus,
+ #login-screen input[type=password]:focus,
+ #login-screen input[type=submit]:focus {
+ width: 100%;
+ margin: 0;
+ padding: 5px 3px; }
+ #login-screen input.login,
+ #login-screen input.login:focus {
+ padding-top: 6px;
+ padding-bottom: 6px;
+ font-size: 1em; }
+ #login-screen #issue {
+ margin-left: 1.33em;
+ font-size: .91em; }
+ #login-screen #issue p:first-child {
+ text-align: right; }
+ #login-screen #issue strong {
+ font-weight: normal; }
+
+#dashboard-main {
+ text-align: center; }
+ #dashboard-main > *:last-child {
+ margin-bottom: 1em; }
+
+/* raccourcis */
+#icons {
+ margin: 1em auto 2em;
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center; }
+ #icons p {
+ width: 13em;
+ margin: 1em 0 2em;
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+ align-items: center; }
+ #icons a, #icons a:link, #icons a:visited, #icons a:hover, #icons a:focus {
+ border-bottom-width: 0;
+ text-decoration: none; }
+ #icons a span {
+ color: #ecedee;
+ border-bottom: 1px dotted #86888c; }
+ #icons a img {
+ padding: 1.5em;
+ background-color: #595b5d;
+ border-radius: 8px;
+ border: 1px solid #ecedee;
+ display: inline-block;
+ filter: contrast(130%); }
+ #icons a:focus {
+ outline: 0; }
+ #icons a:focus span {
+ border: 2px solid #bee74b; }
+ #icons a:focus img, #icons a:hover img {
+ background: #bee74b;
+ outline: 0;
+ border-color: #ecedee; }
+ #icons a:focus span, #icons a:hover span {
+ border-bottom-style: solid; }
+
+/* billet rapide */
+#quick {
+ max-width: 72em;
+ margin: 1em auto 2em;
+ padding: 1em;
+ background: #323334;
+ border: 1px solid #dbdcdd;
+ text-align: left; }
+ #quick h3 {
+ margin-bottom: 0.2em;
+ font-size: 1.2em; }
+ #quick p.qinfo {
+ margin: -.7em -1em 1em;
+ background: #d9edf7 url(msg-info.png) no-repeat 0.2em 0.2em;
+ border: 1px solid #bce8f1;
+ padding: .2em 1em .1em 24px;
+ color: #323334; }
+ #quick #new_cat, #quick .q-cat, #quick .q-cat label {
+ display: inline-block;
+ vertical-align: top;
+ margin-right: 1em;
+ margin-top: 0; }
+ #quick .q-cat label {
+ margin-right: .3em; }
+ #quick #new_cat {
+ margin-bottom: 2em; }
+
+/* modules additionnels */
+#dashboard-boxes {
+ margin: 1em auto 2em;
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center; }
+ #dashboard-boxes .box {
+ padding: 10px;
+ border: 1px solid #cbced1;
+ border-radius: 3px;
+ min-height: 200px;
+ margin: 10px;
+ text-align: left; }
+
+.db-items, .db-contents {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ flex: 1 1 auto; }
+ .db-items img, .db-contents img {
+ vertical-align: middle; }
+ .db-items ul, .db-contents ul {
+ display: block;
+ padding-left: 1.5em;
+ list-style: square; }
+ .db-items li, .db-contents li {
+ margin: 0.25em 0 0 0; }
+
+.no-js .outgoing img {
+ display: none; }
+
+.dc-box {
+ background: transparent url(dc_logos/sq-logo-32.png) no-repeat top right; }
+
+#news dt {
+ font-weight: bold;
+ margin: 0 0 0.4em 0; }
+#news dd {
+ margin: 0 0 1em 0; }
+ #news dd p {
+ margin: 0.2em 0 0 0; }
+
+#dragndrop {
+ position: absolute; }
+ .no-js #dragndrop {
+ display: none; }
+ #dragndrop + label {
+ position: absolute;
+ display: inline-block;
+ line-height: 1; }
+ #dragndrop + label .dragndrop-svg {
+ width: 2em;
+ height: 1.5em;
+ fill: #86888c; }
+ #dragndrop:checked + label .dragndrop-svg {
+ fill: #bee74b;
+ background-color: var(--body-background); }
+
+#media_img_title_pattern {
+ margin-right: 1em; }
+
+#part-users > div {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: left; }
+
+.user-perm {
+ margin: 0 1em 1em 0;
+ background: transparent url(user.png) no-repeat 0.5em 0.5em;
+ width: 20em;
+ border: 1px solid #cbced1;
+ border-radius: .5em;
+ padding: 0 1em .5em; }
+ .user-perm h4,
+ .user-perm h5,
+ .user-perm p,
+ .user-perm ul,
+ .user-perm li {
+ margin: .5em 0 .33em;
+ padding: 0; }
+ .user-perm h4 {
+ padding-left: 28px; }
+ .user-perm h5 {
+ margin: 1em 0 0 0; }
+ .user-perm li {
+ margin-left: 1em;
+ padding-left: 0; }
+ .user-perm.user_super {
+ border-color: #bee74b;
+ background-color: rgba(217, 237, 247, 0.15); }
+
+li.user_super,
+li.user_admin {
+ margin-left: 0;
+ padding-left: 20px;
+ list-style: none;
+ background: transparent url(../images/superadmin.png) no-repeat 0 0.3em; }
+
+li.user_admin {
+ background-image: url(../images/admin.png); }
+
+/* pour les alignements verticaux */
+#theme-new,
+#theme-activate,
+#theme-deactivate,
+#theme-update {
+ margin-left: -10px;
+ margin-right: -10px; }
+
+.box.theme {
+ margin: 5px;
+ padding: 10px 10px 5px 10px;
+ border: 1px solid #dbdcdd;
+ position: relative; }
+ .box.theme:hover {
+ background: #595b5d; }
+ .box.theme input {
+ margin-bottom: 1em; }
+
+.module-name,
+.module-name label {
+ margin-bottom: .5em;
+ color: #c9cbcf; }
+
+.module-sshot {
+ text-align: center; }
+ .module-sshot img {
+ padding: 5px;
+ background: #f3f4f5;
+ box-shadow: 1px 1px 2px rgba(50, 51, 52, 0.1);
+ border: 3px solid #fff;
+ max-width: 100%; }
+
+.module-actions {
+ margin-top: 1em; }
+
+.bloc-toggler {
+ text-align: right; }
+ .bloc-toggler img {
+ opacity: .4; }
+ .bloc-toggler img:hover {
+ opacity: 1; }
+ .bloc-toggler a:focus img {
+ opacity: 1; }
+
+span.module-version:before {
+ content: "- "; }
+
+.toggle-bloc .mod-more {
+ display: block;
+ margin-left: 0; }
+
+.module-name input[type="checkbox"] {
+ margin-bottom: 0; }
+
+/**
+Les screenshots des thèmes ont deux tailles possibles :
+- dans Ajouter des thèmes : 240px (+ 10 padding image + 20 padding boîte + 6 bordure + 2 ombrage = 278)
+- dans Thèmes installés : 280px (+ 10 padding-image + 20 padding-boîte + 2 ombrage = 318)
+On adapte largeur et hauteur en fonction
+*/
+#theme-new .box.theme,
+#theme-update .box.theme {
+ /* Ajouter un thème */
+ width: 278px;
+ min-height: 275px; }
+
+#theme-new .module-sshot img {
+ /* Pour ceux qui n'ont pas de miniature on contraint l'image */
+ max-width: 240px;
+ max-height: 210px;
+ overflow: hidden; }
+
+#theme-deactivate .box.theme {
+ /* Thèmes désactivés */
+ width: 278px; }
+ #theme-deactivate .box.theme:hover {
+ background: transparent url(dc_logos/sq-logo-32.png) no-repeat top right; }
+
+#theme-activate .box.theme {
+ /* Thèmes installés */
+ width: 318px;
+ min-height: 304px;
+ max-width: 100%; }
+
+/* si js est là, les infos viennent par dessus le screenshot */
+.with-js #theme-new .module-infos.toggle-bloc,
+.with-js #theme-new .module-actions.toggle-bloc {
+ position: absolute;
+ left: 10px;
+ width: 239px;
+ margin: 0;
+ padding: 10px;
+ background: #595b5d; }
+.with-js #theme-new .module-infos.toggle-bloc {
+ top: 128px;
+ height: 80px;
+ border-top: 1px solid transparent; }
+.with-js #theme-new .module-actions.toggle-bloc {
+ top: 208px;
+ height: 40px;
+ border-bottom: 1px solid transparent; }
+
+.with-js .module-sshot:hover {
+ cursor: pointer; }
+
+/* mise en forme pour la boîte du thème courant */
+.box.current-theme {
+ /* Thème courant */
+ width: 646px;
+ margin: 5px;
+ padding: 20px 18px 6px;
+ background: #595b5d;
+ border: 1px solid #ecedee;
+ border-radius: .5em;
+ min-height: 326px;
+ box-shadow: 1px 1px 2px rgba(50, 51, 52, 0.1);
+ position: relative; }
+ .box.current-theme .module-sshot:hover {
+ cursor: auto; }
+ .box.current-theme .module-sshot img {
+ float: left;
+ margin-right: 2em;
+ border: 9px solid #fff;
+ padding: 5px;
+ max-width: 308px;
+ max-height: 273px; }
+ .box.current-theme .module-name {
+ color: #ff6e3a;
+ font-size: 1.5em;
+ margin-bottom: 1em; }
+ .box.current-theme .module-actions {
+ display: flex;
+ flex-wrap: wrap; }
+
+.current-actions {
+ width: auto;
+ overflow: hidden;
+ padding-top: 2em;
+ background: transparent url(../images/minus-theme.png) no-repeat left top; }
+
+#categories {
+ margin: 1em 0; }
+ #categories ul {
+ list-style: none;
+ margin-top: 2em;
+ padding: 0; }
+ #categories ul ul {
+ margin-right: 2em;
+ margin-left: 2em; }
+ #categories .placeholder {
+ outline: 1px dashed #76c2f1;
+ min-height: 2.5em; }
+
+.cat-line {
+ position: relative;
+ margin: .66em 0;
+ padding: .66em 1em;
+ border: 1px solid #cbced1;
+ border-radius: 3px; }
+ .cat-line label {
+ margin-right: .25em; }
+ .cat-line label a {
+ font-weight: bold; }
+ .cat-line p,
+ .cat-line label {
+ margin: 0;
+ display: inline-block; }
+ .cat-line .cat-line {
+ border: 1px solid #dbdcdd; }
+
+p.cat-title {
+ margin-right: 1em; }
+
+.cat-nb-posts a {
+ color: #f3f4f5; }
+
+.cat-url {
+ padding-left: 1em; }
+
+.cat-buttons {
+ float: right;
+ margin-top: -.2em;
+ font-size: .91em; }
+ .cat-buttons select {
+ padding: 1px 2px 3px 2px;
+ margin-right: .25em; }
+ .cat-buttons .reset {
+ padding-left: 4px;
+ padding-right: 4px; }
+
+.cat-actions {
+ line-height: 2; }
+
+#del_cat {
+ width: 100%; }
+
+.media-file-mode a {
+ border-bottom: none; }
+.media-file-mode img {
+ margin-right: 1em; }
+
+span.media-file-mode {
+ margin-right: 1em; }
+
+.media-item {
+ position: relative;
+ border: 1px solid #dbdcdd;
+ margin: 9px;
+ padding: 10px 12px 6px;
+ width: 320px;
+ display: inline-block;
+ vertical-align: top;
+ min-height: 140px;
+ word-wrap: break-word; }
+ .media-item p {
+ margin: 0 0 .5em; }
+ .media-item object {
+ margin-top: .5em; }
+ .media-item ul {
+ display: block;
+ list-style: none;
+ margin: 0;
+ padding: 0; }
+ .media-item audio {
+ width: 100%;
+ margin-top: .5em; }
+ .media-item.media-private {
+ border-color: #c44d58; }
+ .media-item.media-private img.media-private {
+ padding-right: .5em; }
+
+tr.media-private img.media-private {
+ padding-right: .5em; }
+
+a.media-icon {
+ display: block;
+ border-bottom: none;
+ margin: 0 auto; }
+
+.media-icon img {
+ display: block; }
+
+a.media-flag {
+ border-bottom: none; }
+
+.media-flag img {
+ float: left;
+ margin-right: .5em; }
+
+.media-link {
+ font-size: 1.1em; }
+
+.media-action-box {
+ position: relative;
+ margin: 3em 3em 1em 1em;
+ display: inline-block;
+ vertical-align: top; }
+
+li.media-action {
+ display: block;
+ position: absolute;
+ top: 4px;
+ right: 8px;
+ height: 16px; }
+ li.media-action a {
+ border: none; }
+ li.media-action a.attach-media {
+ margin-right: 5px; }
+ li.media-action form {
+ display: inline; }
+ li.media-action input {
+ border: none; }
+
+#entry-sidebar .media-item {
+ width: 100%;
+ min-height: 0;
+ padding: 4px;
+ margin: .33em 0; }
+
+.folders-group .media-item {
+ min-height: 70px; }
+ .folders-group .media-item p {
+ margin-bottom: 0; }
+
+.media-folder {
+ background: transparent;
+ border-color: #ecedee;
+ border-left-width: 8px; }
+ .media-folder .media-link {
+ font-size: 1.125em;
+ margin-left: 2em;
+ color: #c9cbcf;
+ border-bottom: none; }
+
+tr.media-folder {
+ background: transparent; }
+ tr.media-folder .media-link {
+ margin-left: 0; }
+
+.media-folder-up {
+ border-color: transparent;
+ padding-bottom: 6px; }
+
+.medias-delete,
+.medias-select {
+ text-align: right; }
+
+.media-recent {
+ float: left;
+ margin-right: 2em; }
+
+#media-fav-dir {
+ border-bottom: none; }
+ #media-fav-dir img {
+ vertical-align: middle; }
+
+/* upload multiple */
+.enhanced_uploader .choose_files,
+.enhanced_uploader .cancel,
+.enhanced_uploader .clean,
+.enhanced_uploader .start {
+ margin-right: .4em; }
+.enhanced_uploader #upfile {
+ visibility: hidden;
+ width: 0;
+ height: 0;
+ margin: 0;
+ opacity: 0;
+ filter: alpha(opacity=0);
+ cursor: pointer; }
+.enhanced_uploader .button.choose_files {
+ display: inline-block; }
+.enhanced_uploader .max-size {
+ display: block; }
+.enhanced_uploader .one-file {
+ display: none; }
+.enhanced_uploader p.clear {
+ padding-top: 1em;
+ margin-bottom: 1em; }
+
+.button.clean,
+.button.cancel,
+.button.choose_files {
+ display: none; }
+
+label span.one-file {
+ display: inline; }
+
+#add-file-f p.clear {
+ margin-top: 1em;
+ margin-bottom: 0;
+ clear: both; }
+
+.files {
+ list-style-type: none;
+ margin-left: 0;
+ padding-left: 0;
+ border-bottom: 1px solid #dbdcdd; }
+ .files li {
+ margin-left: 0;
+ padding-left: 0; }
+
+.upload-msg {
+ font-weight: bold; }
+ .upload-msg.upload-error {
+ color: #ae323b; }
+
+.upload-files {
+ padding: 0 0.5em;
+ margin: 1em 0; }
+
+.upload-file {
+ margin: 0;
+ padding: .3em 0;
+ border-top: 1px solid #dbdcdd;
+ position: relative; }
+
+.upload-fileinfo {
+ margin-left: 0; }
+ .upload-fileinfo input {
+ position: absolute;
+ top: .5em;
+ right: .5em; }
+ .upload-fileinfo span {
+ padding-right: 8px; }
+ .upload-fileinfo .upload-filecancel {
+ display: block;
+ padding-right: 0;
+ margin-top: 3px;
+ width: 20px;
+ height: 20px;
+ background: transparent url("cancel.png") no-repeat left top;
+ text-indent: -1000px;
+ cursor: pointer;
+ float: left; }
+
+.upload-filemsg {
+ font-weight: bold;
+ color: #fff; }
+ .upload-filemsg.upload-error {
+ color: #ae323b; }
+
+.upload-progress {
+ padding: .3em 0; }
+ .upload-progress div {
+ width: 0;
+ height: 1.2em;
+ font-weight: bold;
+ line-height: 1.2em;
+ text-align: right;
+ background: #556f0f url(loader.png) repeat-x left top;
+ color: #fff;
+ border-radius: 3px; }
+
+div.template-upload {
+ clear: both; }
+
+.queue-message {
+ font-weight: bold; }
+
+#media-icon {
+ float: left; }
+
+.near-icon {
+ margin-left: 70px;
+ margin-bottom: 3em; }
+
+#media-details ul {
+ display: block;
+ margin-left: 0;
+ padding: 0; }
+#media-details li {
+ list-style: square inside;
+ margin: 0;
+ padding: 0; }
+
+#media-original-image {
+ overflow: auto; }
+ #media-original-image.overheight {
+ height: 500px; }
+
+#media-attribute {
+ margin-left: .5em;
+ padding: .5em;
+ border-left: 1px solid currentColor;
+ overflow-wrap: break-word; }
+ #media-attribute .media-title {
+ font-weight: bolder; }
+ #media-attribute .media-desc {
+ font-style: italic; }
+
+.modules td.module-actions, .modules td.module-icon {
+ vertical-align: middle; }
+.modules td.module-icon img:last-child {
+ width: 16px;
+ height: 16px; }
+.modules td.module-icon img.expand {
+ margin-bottom: 3px; }
+.modules td.module-distrib img {
+ display: block;
+ float: right; }
+
+.modules tr.expand,
+.modules td.expand {
+ background: #323334;
+ border-color: #a2cbe9; }
+
+.modules tr.expand td:first-child {
+ font-weight: bold;
+ background: #323334; }
+
+.modules td.expand {
+ padding: 0 0 1em; }
+ .modules td.expand div {
+ display: inline-block;
+ vertical-align: top;
+ margin-right: 3em; }
+
+.modules dt {
+ font-weight: bold; }
+
+.modules a.module-details {
+ background: transparent url(search.svg) no-repeat 0 center;
+ padding: 4px 4px 0 20px; }
+.modules a.module-support {
+ background: transparent url(help12.png) no-repeat 2px center;
+ padding: 4px 4px 0 20px; }
+.modules a.module-config {
+ background: transparent url(settings.png) no-repeat 2px 6px;
+ padding: 4px 4px 0 18px; }
+
+#m_search {
+ color: #fff;
+ background: transparent url(search.svg) no-repeat 0 center;
+ padding-left: 20px; }
+
+.mod-more {
+ padding-top: .5em; }
+ .mod-more,
+ .mod-more li {
+ margin: .25em 0 0 1em;
+ padding: 0;
+ list-style-type: none; }
+
+#plugin-update td {
+ vertical-align: baseline; }
+
+#entry-form {
+ display: flex;
+ flex-wrap: wrap; }
+
+#entry-wrapper {
+ flex-grow: 1;
+ width: calc(100% - 19em); }
+ @media screen and (max-width: 80em) {
+ #entry-wrapper {
+ width: 100%; } }
+
+#entry-content {
+ margin-left: 0; }
+ @media screen and (min-width: 80.01em) {
+ #entry-content {
+ padding-right: 3em; } }
+ @media screen and (max-width: 38em) {
+ #entry-content {
+ padding-right: 1em; } }
+ @media screen and (max-width: 26.5em) {
+ #entry-content {
+ padding-right: 0; } }
+
+#entry-sidebar {
+ display: flex;
+ flex-wrap: wrap;
+ flex-direction: column; }
+ @media screen and (max-width: 80em) {
+ #entry-sidebar {
+ flex-direction: row; } }
+ @media screen and (max-width: 38em) {
+ #entry-sidebar {
+ padding-right: 1em; } }
+ #entry-sidebar h4 {
+ font-size: 1.08em;
+ margin-top: .3em;
+ border-bottom: 1px solid #fff; }
+ #entry-sidebar select {
+ width: 100%; }
+ #entry-sidebar input#post_position {
+ width: 4em; }
+
+.sb-box {
+ width: 18em;
+ margin-bottom: 1em;
+ margin-right: 1em;
+ padding: .5em 1em;
+ background-color: #4c4d4f; }
+ .sb-box:focus-within {
+ background-color: #595b5d; }
+
+#tb_excerpt {
+ width: 100%; }
+
+.fav-list {
+ list-style-type: none;
+ margin-left: 0;
+ padding-left: 0; }
+ #my-favs .fav-list {
+ border-top: 1px solid #ecedee; }
+ .fav-list li {
+ margin-left: 0;
+ padding-left: 0;
+ padding-top: 3px;
+ padding-bottom: 3px;
+ position: relative; }
+ #my-favs .fav-list li {
+ line-height: 2;
+ border-bottom: 1px solid #ecedee;
+ padding-top: 3px;
+ padding-bottom: 3px;
+ position: relative; }
+ #my-favs:focus-within .fav-list li {
+ border-bottom-color: #dbdcdd; }
+ .fav-list li span.zoom {
+ display: none; }
+ .fav-list li:hover span.zoom {
+ display: block;
+ position: absolute;
+ bottom: 0;
+ left: 10em;
+ background-color: #595b5d;
+ border: 1px solid #dbdcdd;
+ padding: .2em;
+ border-radius: .5em; }
+ .fav-list img {
+ vertical-align: middle;
+ margin-right: .2em; }
+
+#my-favs {
+ border-color: #9ac123; }
+ #my-favs input.position {
+ margin: 0 0 .4em .2em; }
+
+#available-favs input,
+#available-favs label,
+#available-favs label span {
+ white-space: normal;
+ display: inline; }
+#available-favs label span.zoom {
+ display: none; }
+#available-favs li:hover label span.zoom {
+ display: block;
+ position: absolute;
+ bottom: 0;
+ left: 10em;
+ background-color: #323334;
+ border: 1px solid #dbdcdd;
+ padding: .2em;
+ border-radius: .5em; }
+
+#user-options label.ib {
+ display: inline-block;
+ width: 14em;
+ padding-right: 1em; }
+
+.blog-perm {
+ margin-top: 2em;
+ padding-top: 2em;
+ font-weight: bold; }
+
+.ul-perm {
+ list-style-type: square;
+ margin-left: 0;
+ padding-left: 3.5em;
+ margin-bottom: 0; }
+
+.add-perm {
+ padding-top: .5em;
+ padding-left: 2.5em;
+ margin-left: 0; }
+
+.guideline #content h2 {
+ color: #ff6e3a;
+ padding: 2em 0 0 0;
+ margin: 1em 0;
+ font-size: 2em; }
+ .guideline #content h2:first-child {
+ margin-top: 0;
+ padding-top: .5em; }
+.guideline h3 {
+ margin-top: 2em; }
+.guideline .dc-update h3 {
+ margin-top: 0; }
+.guideline .one-box .box {
+ border: 1px solid #dbdcdd;
+ padding: 2px .5em; }
+.guideline #main-menu ul {
+ margin: 0;
+ padding: 0;
+ font-weight: normal; }
+.guideline #main-menu li {
+ padding-left: 1em; }
+
+/** --------------------------------------------------
+ Misc
+--------------------------------------------------- */
+/* jQuery Autocomplete plugin */
+.ac_results {
+ padding: 0px;
+ background-color: #323334;
+ border: 1px dotted #ffd478;
+ overflow: hidden;
+ z-index: 99999; }
+ .ac_results ul {
+ width: 100%;
+ list-style-position: outside;
+ list-style: none;
+ padding: 0;
+ margin: 0; }
+ .ac_results li {
+ margin: 0px;
+ padding: 2px 5px;
+ cursor: default;
+ display: block;
+ font-size: 1em;
+ line-height: 16px;
+ overflow: hidden; }
+
+.ac_loading {
+ background: transparent url("loader.gif") right center no-repeat; }
+
+.ac_odd {
+ background-color: #323334; }
+
+.ac_over {
+ color: #fff;
+ background-color: #5e9bc1; }
+
+/* password indicator */
+.pw-table {
+ display: table;
+ margin-bottom: 1em; }
+
+.pw-cell {
+ display: table-cell;
+ margin-bottom: 1em; }
+
+#pwindicator {
+ display: table-cell;
+ vertical-align: bottom;
+ padding-left: 1.5em;
+ height: 3.8em; }
+ #pwindicator .bar {
+ height: 6px;
+ margin-bottom: 4px; }
+
+.pw-very-weak .bar {
+ background: #c44d58;
+ width: 30px; }
+
+.pw-weak .bar {
+ background: #e1732c;
+ width: 60px; }
+
+.pw-mediocre .bar {
+ background: #ff9900;
+ width: 90px; }
+
+.pw-strong .bar {
+ background: #cdad12;
+ width: 120px; }
+
+.pw-very-strong .bar {
+ background: #9ac123;
+ width: 150px; }
+
+/* ------------------------------------------------------------------ navigation */
+/* selects accès rapide */
+.anchor-nav {
+ background: #272b30;
+ padding: 4px 1em; }
+ .anchor-nav label {
+ vertical-align: bottom; }
+
+/* nav links */
+.nav_prevnext {
+ margin-bottom: 2em;
+ color: #272b30; }
+
+.nav_prevnext a,
+a.back {
+ color: #76c2f1;
+ border: 1px solid #dbdcdd;
+ padding: 2px 1.5em;
+ border-radius: .75em;
+ background-color: #323334; }
+
+a.back:before {
+ content: "\ab\a0"; }
+
+a.onblog_link {
+ color: #323334;
+ float: right;
+ border: 1px solid #ecedee;
+ padding: 2px 1.5em;
+ border-radius: .75em;
+ background-color: #ecedee;
+ box-shadow: 0 1px 1px rgba(50, 51, 52, 0.3); }
+
+/* Pagination */
+.pager {
+ margin: 2em 0 1em 0;
+ clear: left; }
+ .pager ul {
+ list-style-type: none;
+ margin: 0;
+ padding: 0; }
+ .pager li,
+ .pager input {
+ display: inline-block;
+ vertical-align: middle;
+ margin: 0 .33em 0 0;
+ padding: 0;
+ text-align: center; }
+ .pager .btn {
+ border: 1px solid #dbdcdd;
+ background-color: #f3f4f5;
+ color: #76c2f1;
+ border-radius: 3px;
+ overflow: hidden; }
+ .pager .btn.no-link {
+ border-color: #dbdcdd;
+ background-color: #fff;
+ padding: 1px 3px 0; }
+ .pager .active {
+ padding: 4px 12px;
+ color: #c9cbcf; }
+ .pager .direct-access {
+ margin-left: 2em; }
+ .pager .direct-access input[type=text] {
+ border: 1px solid #dbdcdd;
+ padding: 3px 8px;
+ margin-left: .25em;
+ background-color: #fff; }
+ .pager .direct-access input[type=submit] {
+ padding: 3px 6px; }
+ .pager a {
+ display: block;
+ padding: 1px 3px 0;
+ border: none; }
+ .pager a:hover, .pager a:focus {
+ background-color: #dbdcdd; }
+
+.index .btn.no-link,
+.index a {
+ padding: 2px 8px 3px;
+ font-variant: small-caps; }
+.index li {
+ margin-bottom: 3px; }
+.index a {
+ font-weight: bold; }
+.index .btn.no-link {
+ color: #c9cbcf; }
+.index .active {
+ padding: 4px 8px;
+ color: #fff;
+ background: #676e78;
+ border-radius: 3px;
+ font-variant: small-caps; }
+
+/* Etapes */
+.step {
+ display: inline-block;
+ float: left;
+ margin: 3px 10px 2px 0;
+ padding: 5px .5em;
+ color: #676e78;
+ background: #f3f4f5;
+ border: 1px solid #cbced1;
+ border-radius: 3px;
+ font-weight: bold; }
+
+/* ------------------------------------------------------------------------- indicateurs */
+.mark-attach {
+ display: inline-block;
+ box-sizing: border-box;
+ width: 12px;
+ height: 12px;
+ padding-left: 12px;
+ background: url("../images/attach.svg") no-repeat;
+ filter: hue-rotate(260deg) saturate(100) saturate(0.2) brightness(220%); }
+
+/* ---------------------------------------------------------------- utilisables partout */
+.legible {
+ font-size: 1.16em;
+ max-width: 62em; }
+
+.fieldset {
+ background: #272b30;
+ border: 1px solid #cbced1;
+ border-radius: 3px;
+ padding: 1em .7em .5em;
+ margin-bottom: 1em; }
+ .fieldset h3 {
+ margin-top: 0; }
+ .fieldset hr {
+ background-color: #cbced1;
+ border-width: 0;
+ margin: 1em 0; }
+ .fieldset:focus-within {
+ background-color: #363a3e; }
+
+.right,
+.txt-right {
+ text-align: right; }
+
+.txt-center {
+ text-align: center; }
+
+.txt-left {
+ text-align: left; }
+
+.no-margin,
+label.no-margin {
+ margin-top: 0;
+ margin-bottom: 0; }
+
+.vertical-separator {
+ margin-top: 2em; }
+
+p.clear.vertical-separator {
+ padding-top: 2em; }
+
+.border-top {
+ border-top: 1px solid #86888c;
+ padding-top: 1em;
+ margin-top: 1em; }
+
+.grid {
+ background: transparent repeat url("grid.png") 0 0; }
+
+ul.nice {
+ margin: 1em 0;
+ padding: 0 0 0 2em;
+ list-style: square; }
+ ul.nice li {
+ margin: 0;
+ padding: 0; }
+
+ul.from-left {
+ list-style-type: none;
+ padding-left: 0;
+ margin: 1em 0; }
+ ul.from-left > li {
+ margin-top: 1em;
+ margin-bottom: 1em; }
+ ul.from-left ul {
+ list-style-type: square; }
+
+.offline {
+ color: #86888c;
+ background: #323334; }
+
+/* caché pour tout le monde */
+.hide,
+.button.hide {
+ display: none !important; }
+
+/* Caché sauf pour les revues d'écran */
+.hidden,
+.with-js .out-of-screen-if-js {
+ position: absolute !important;
+ clip: rect(1px 1px 1px 1px);
+ /* IE6, IE7 */
+ clip: rect(1px, 1px, 1px, 1px);
+ padding: 0 !important;
+ border: 0 !important;
+ height: 1px !important;
+ width: 1px !important;
+ overflow: hidden; }
+
+/* caché si js est inactif */
+.no-js .hidden-if-no-js {
+ display: none; }
+
+/* caché si js est actif */
+.with-js .hidden-if-js {
+ display: none; }
+
+/* ---------------------------------------------- Couleurs ajoutées via javascript
+
+/* Sortable (jQuery UI) */
+.sortable-area {
+ border: 1px dashed currentColor;
+ background-color: #4c4d4f; }
+
+div.ui-sortable-handle {
+ border: 1px dashed currentColor !important;
+ border-radius: .75em !important; }
+
+/* color-picker.js */
+.color-color-picker {
+ border: 1px solid #000;
+ width: 195px;
+ background: #fff; }
+
+/* _media_item.js */
+.color-div {
+ border: 1px solid #c9cbcf; }
+
+/* Badges (common.js) */
+.badgeable {
+ /* class to set to badge parent's, not mandatory for menu items */
+ position: relative; }
+
+/* Badge design */
+.badge {
+ color: #fff;
+ background-color: #d54e21;
+ border: 1px solid #dcdee0;
+ vertical-align: top;
+ border-radius: 1em;
+ padding: 0 .6em; }
+
+.badge-icon {
+ background-color: #607d8b; }
+
+/* Badge background override */
+.badge-std {
+ background-color: #d54e21; }
+
+.badge-info {
+ background-color: #3f51b5; }
+
+.badge-soft {
+ background-color: #607d8b; }
+
+/* Badge position */
+.badge-block {
+ /* Dashboard module → badge on top right */
+ position: absolute;
+ top: -10px;
+ right: -10px; }
+
+.badge-icon {
+ /* Dashboard icon → badge on top right */
+ right: 20px; }
+
+.badge-inline {
+ /* Menu item */
+ margin-left: .5em; }
+
+.badge-left {
+ right: auto;
+ left: -10px; }
+ .badge-left.badge-icon {
+ left: 20px; }
+
+/* Badge design option */
+.badge-noborder {
+ border: none; }
+
+.badge-small {
+ font-size: smaller; }
+ .badge-small.badge-icon {
+ right: 25px; }
+ .badge-small.badge-icon.badge-left {
+ right: auto;
+ left: 25px; }
+
+.badge-square {
+ border-radius: 0; }
+
+/* ------------------------------------------------------------------------------------
+ UN POIL DE MEDIA QUERIES
+------------------------------------------------------------------------------------ */
+@media screen and (max-width: 80em) {
+ #header,
+ h1 {
+ background: #595b5d; }
+
+ #top-info-user {
+ background: #272b30; }
+ #top-info-user a.active {
+ color: #fff;
+ background: #86888c; }
+
+ #wrapper {
+ background: #272b30; }
+
+ .modules th.module-desc,
+ .modules td.module-desc {
+ display: none; } }
+@media screen and (max-width: 44em) {
+ #help-button {
+ background-color: #9ac123;
+ padding: 0;
+ font-size: .83em;
+ line-height: 68px; } }
+@media screen and (max-width: 38em) {
+ h1 a:link {
+ border-right: 1px solid #ecedee; }
+ h1 a:hover, h1 a:focus {
+ border-right: 1px solid #ecedee; }
+
+ #dashboard-main {
+ padding: 0; }
+
+ #content,
+ .hide-mm #content {
+ padding: 0 .5em !important; }
+
+ #main #content > h2 {
+ padding: 6px 30px 4px .5em; }
+
+ .cell,
+ #filters-form .cell {
+ border: none;
+ padding: 1em; }
+
+ .pseudo-tabs li {
+ border-top: 1px solid #ecedee;
+ padding: .25em; }
+ .pseudo-tabs li:first-child, .pseudo-tabs li:nth-of-type(2) {
+ border-top: none; } }
+@media screen and (max-width: 26.5em) {
+ h1,
+ h1 a {
+ border-right: #323334 !important; }
+
+ #content.with-help #help {
+ font-size: 1.2rem; }
+
+ p.top-add {
+ text-align: center; }
+
+ .multi-part {
+ padding-left: 0; }
+
+ .part-tabs ul {
+ padding: 0 .5em; }
+
+ #icons p {
+ padding: 1em .25em; }
+
+ .box.current-theme {
+ padding: 10px; }
+
+ th,
+ td {
+ padding: 0.3em 1em 0.3em 0; }
+
+ .pseudo-tabs li {
+ border-top: 1px solid #ecedee !important; }
+
+ .pseudo-tabs li:first-child {
+ border-top: none; } }
+/** --------------------------------------------------
+ Plugins
+--------------------------------------------------- */
+/* dcLegacyEditor */
+/* WYSIWYG Document */
+body.wysiwygDoc {
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; }
+
+.wysiwygDoc pre, .wysiwygDoc code, .wysiwygDoc kbd, .wysiwygDoc samp {
+ font-family: "Andale Mono", AndaleMono, Consolas, Monaco, "Courier New", monospace; }
+
+/* dcCKEditor */
+.cke textarea.cke_source {
+ font-family: "Andale Mono", AndaleMono, Consolas, Monaco, "Courier New", monospace;
+ font-size: 100%; }
+
+/** --------------------------------------------------
+ 3rd parties
+--------------------------------------------------- */
+/* Magnific Popup CSS */
+.mfp-bg {
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 1042;
+ overflow: hidden;
+ position: fixed;
+ background: #0b0b0b;
+ opacity: 0.8; }
+
+.mfp-wrap {
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 1043;
+ position: fixed;
+ outline: none !important;
+ -webkit-backface-visibility: hidden; }
+
+.mfp-container {
+ text-align: center;
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ left: 0;
+ top: 0;
+ padding: 0 8px;
+ box-sizing: border-box; }
+
+.mfp-container:before {
+ content: '';
+ display: inline-block;
+ height: 100%;
+ vertical-align: middle; }
+
+.mfp-align-top .mfp-container:before {
+ display: none; }
+
+.mfp-content {
+ position: relative;
+ display: inline-block;
+ vertical-align: middle;
+ margin: 0 auto;
+ text-align: left;
+ z-index: 1045; }
+
+.mfp-inline-holder .mfp-content,
+.mfp-ajax-holder .mfp-content {
+ width: 100%;
+ cursor: auto; }
+
+.mfp-ajax-cur {
+ cursor: progress; }
+
+.mfp-zoom-out-cur, .mfp-zoom-out-cur .mfp-image-holder .mfp-close {
+ cursor: -moz-zoom-out;
+ cursor: -webkit-zoom-out;
+ cursor: zoom-out; }
+
+.mfp-zoom {
+ cursor: pointer;
+ cursor: -webkit-zoom-in;
+ cursor: -moz-zoom-in;
+ cursor: zoom-in; }
+
+.mfp-auto-cursor .mfp-content {
+ cursor: auto; }
+
+.mfp-close,
+.mfp-arrow,
+.mfp-preloader,
+.mfp-counter {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none; }
+
+.mfp-loading.mfp-figure {
+ display: none; }
+
+.mfp-hide {
+ display: none !important; }
+
+.mfp-preloader {
+ color: #CCC;
+ position: absolute;
+ top: 50%;
+ width: auto;
+ text-align: center;
+ margin-top: -0.8em;
+ left: 8px;
+ right: 8px;
+ z-index: 1044; }
+ .mfp-preloader a {
+ color: #CCC; }
+ .mfp-preloader a:hover {
+ color: #FFF; }
+
+.mfp-s-ready .mfp-preloader {
+ display: none; }
+
+.mfp-s-error .mfp-content {
+ display: none; }
+
+button.mfp-close, button.mfp-arrow {
+ overflow: visible;
+ cursor: pointer;
+ background: transparent;
+ border: 0;
+ -webkit-appearance: none;
+ display: block;
+ outline: none;
+ padding: 0;
+ z-index: 1046;
+ box-shadow: none;
+ touch-action: manipulation; }
+button::-moz-focus-inner {
+ padding: 0;
+ border: 0; }
+
+.mfp-close {
+ width: 44px;
+ height: 44px;
+ line-height: 44px;
+ position: absolute;
+ right: 0;
+ top: 0;
+ text-decoration: none;
+ text-align: center;
+ opacity: 0.65;
+ padding: 0 0 18px 10px;
+ color: #FFF;
+ font-style: normal;
+ font-size: 28px;
+ font-family: Arial, Baskerville, monospace; }
+ .mfp-close:hover, .mfp-close:focus {
+ opacity: 1; }
+ .mfp-close:active {
+ top: 1px; }
+
+.mfp-close-btn-in .mfp-close {
+ color: #333; }
+
+.mfp-image-holder .mfp-close,
+.mfp-iframe-holder .mfp-close {
+ color: #FFF;
+ right: -6px;
+ text-align: right;
+ padding-right: 6px;
+ width: 100%; }
+
+.mfp-counter {
+ position: absolute;
+ top: 0;
+ right: 0;
+ color: #CCC;
+ font-size: 12px;
+ line-height: 18px;
+ white-space: nowrap; }
+
+.mfp-arrow {
+ position: absolute;
+ opacity: 0.65;
+ margin: 0;
+ top: 50%;
+ margin-top: -55px;
+ padding: 0;
+ width: 90px;
+ height: 110px;
+ -webkit-tap-highlight-color: transparent; }
+ .mfp-arrow:active {
+ margin-top: -54px; }
+ .mfp-arrow:hover, .mfp-arrow:focus {
+ opacity: 1; }
+ .mfp-arrow:before, .mfp-arrow:after {
+ content: '';
+ display: block;
+ width: 0;
+ height: 0;
+ position: absolute;
+ left: 0;
+ top: 0;
+ margin-top: 35px;
+ margin-left: 35px;
+ border: medium inset transparent; }
+ .mfp-arrow:after {
+ border-top-width: 13px;
+ border-bottom-width: 13px;
+ top: 8px; }
+ .mfp-arrow:before {
+ border-top-width: 21px;
+ border-bottom-width: 21px;
+ opacity: 0.7; }
+
+.mfp-arrow-left {
+ left: 0; }
+ .mfp-arrow-left:after {
+ border-right: 17px solid #FFF;
+ margin-left: 31px; }
+ .mfp-arrow-left:before {
+ margin-left: 25px;
+ border-right: 27px solid #3F3F3F; }
+
+.mfp-arrow-right {
+ right: 0; }
+ .mfp-arrow-right:after {
+ border-left: 17px solid #FFF;
+ margin-left: 39px; }
+ .mfp-arrow-right:before {
+ border-left: 27px solid #3F3F3F; }
+
+.mfp-iframe-holder {
+ padding-top: 40px;
+ padding-bottom: 40px; }
+ .mfp-iframe-holder .mfp-content {
+ line-height: 0;
+ width: 100%;
+ max-width: 98%; }
+ .mfp-iframe-holder .mfp-close {
+ top: -40px; }
+
+.mfp-iframe-scaler {
+ width: 100%;
+ height: 0;
+ overflow: hidden;
+ padding-top: 56.25%; }
+ .mfp-iframe-scaler iframe {
+ position: absolute;
+ display: block;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
+ background: #000; }
+
+/* Main image in popup */
+img.mfp-img {
+ width: auto;
+ max-width: 100%;
+ height: auto;
+ display: block;
+ line-height: 0;
+ box-sizing: border-box;
+ padding: 40px 0 40px;
+ margin: 0 auto; }
+
+/* The shadow behind the image */
+.mfp-figure {
+ line-height: 0; }
+ .mfp-figure:after {
+ content: '';
+ position: absolute;
+ left: 0;
+ top: 40px;
+ bottom: 40px;
+ display: block;
+ right: 0;
+ width: auto;
+ height: auto;
+ z-index: -1;
+ box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
+ background: #444; }
+ .mfp-figure small {
+ color: #BDBDBD;
+ display: block;
+ font-size: 12px;
+ line-height: 14px; }
+ .mfp-figure figure {
+ margin: 0; }
+
+.mfp-bottom-bar {
+ margin-top: -36px;
+ position: absolute;
+ top: 100%;
+ left: 0;
+ width: 100%;
+ cursor: auto; }
+
+.mfp-title {
+ text-align: left;
+ line-height: 18px;
+ color: #F3F3F3;
+ word-wrap: break-word;
+ padding-right: 36px; }
+
+.mfp-image-holder .mfp-content {
+ max-width: 100%; }
+
+.mfp-gallery .mfp-image-holder .mfp-figure {
+ cursor: pointer; }
+
+@media screen and (max-width: 800px) and (orientation: landscape), screen and (max-height: 300px) {
+ /**
+ * Remove all paddings around the image on small screen
+ */
+ .mfp-img-mobile .mfp-image-holder {
+ padding-left: 0;
+ padding-right: 0; }
+ .mfp-img-mobile img.mfp-img {
+ padding: 0; }
+ .mfp-img-mobile .mfp-figure:after {
+ top: 0;
+ bottom: 0; }
+ .mfp-img-mobile .mfp-figure small {
+ display: inline;
+ margin-left: 5px; }
+ .mfp-img-mobile .mfp-bottom-bar {
+ background: rgba(0, 0, 0, 0.6);
+ bottom: 0;
+ margin: 0;
+ top: auto;
+ padding: 3px 5px;
+ position: fixed;
+ box-sizing: border-box; }
+ .mfp-img-mobile .mfp-bottom-bar:empty {
+ padding: 0; }
+ .mfp-img-mobile .mfp-counter {
+ right: 5px;
+ top: 3px; }
+ .mfp-img-mobile .mfp-close {
+ top: 0;
+ right: 0;
+ width: 35px;
+ height: 35px;
+ line-height: 35px;
+ background: rgba(0, 0, 0, 0.6);
+ position: fixed;
+ text-align: center;
+ padding: 0; } }
+@media all and (max-width: 900px) {
+ .mfp-arrow {
+ -webkit-transform: scale(0.75);
+ transform: scale(0.75); }
+
+ .mfp-arrow-left {
+ -webkit-transform-origin: 0;
+ transform-origin: 0; }
+
+ .mfp-arrow-right {
+ -webkit-transform-origin: 100%;
+ transform-origin: 100%; }
+
+ .mfp-container {
+ padding-left: 6px;
+ padding-right: 6px; } }
+/* CodeMirror CSS */
+.CodeMirror {
+ /* Set height, width, borders, and global font properties here */
+ font-family: "Andale Mono", AndaleMono, Consolas, Monaco, "Courier New", monospace !important; }
+
+/** --------------------------------------------------
+ Debug
+--------------------------------------------------- */
+/* debug */
+#debug {
+ position: absolute;
+ top: 0;
+ width: 100%;
+ height: 4px;
+ background: #ffd478; }
+ #debug div {
+ display: none;
+ padding: 3px 0.5em 2px; }
+ #debug p {
+ margin: 0.5em 0; }
+ #debug:hover {
+ height: auto;
+ padding: 2px 1em;
+ z-index: 100; }
+ #debug:hover div {
+ display: block; }
+
+.debug {
+ color: #ae323b;
+ background: #ffd478;
+ padding: 3px 0.5em 2px; }
+
+input[type=submit].delete.debug,
+a.delete.debug {
+ border-color: #ffd478; }
+ input[type=submit].delete.debug:hover,
+ a.delete.debug:hover {
+ background: #ffd478;
+ color: #ae323b;
+ border-color: #ffd478; }
diff --git a/dotclear._no/admin/style/default-rtl.css b/dotclear._no/admin/style/default-rtl.css
new file mode 100644
index 0000000..c98362f
--- /dev/null
+++ b/dotclear._no/admin/style/default-rtl.css
@@ -0,0 +1,11 @@
+body {
+ direction: rtl;
+}
+
+.right {
+ text-align: left;
+}
+
+th {
+ text-align: left;
+}
diff --git a/dotclear._no/admin/style/default.css b/dotclear._no/admin/style/default.css
new file mode 100644
index 0000000..58db678
--- /dev/null
+++ b/dotclear._no/admin/style/default.css
@@ -0,0 +1,3867 @@
+@charset "UTF-8";
+/** --------------------------------------------------
+ Start
+--------------------------------------------------- */
+/* largeur des paddings et border compris dans "width" */
+*,
+*:before,
+*:after {
+ box-sizing: border-box; }
+
+html {
+ font-size: 100%;
+ -ms-text-size-adjust: 100%;
+ -webkit-text-size-adjust: 100%;
+ margin: 0;
+ padding: 0; }
+
+body {
+ margin: 0;
+ padding: 0; }
+
+a {
+ background: transparent; }
+ a:focus {
+ outline: thin dotted; }
+ a:active, a:hover {
+ outline: none; }
+ a img {
+ border: none; }
+
+q,
+cite {
+ font-style: italic; }
+
+q:before,
+q:after {
+ content: ""; }
+
+sup,
+sub {
+ font-size: .75em;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline; }
+
+sup {
+ top: -0.5em; }
+
+sub {
+ bottom: -0.25em; }
+
+abbr[title] {
+ border-bottom: 1px dotted;
+ cursor: help; }
+
+b,
+strong {
+ font-weight: bold;
+ font-size: 0.9375em; }
+
+small {
+ font-size: 80%; }
+
+dfn {
+ font-style: italic; }
+
+hr {
+ box-sizing: content-box;
+ height: 0; }
+
+mark {
+ background: #ff0;
+ color: #000; }
+
+code,
+kbd,
+pre,
+samp {
+ font-family: monospace, serif;
+ font-size: 1em; }
+
+pre {
+ white-space: pre-wrap; }
+
+fieldset {
+ margin: 0;
+ padding: 0;
+ border: none; }
+
+input,
+button,
+select {
+ vertical-align: middle; }
+
+button,
+input,
+select,
+textarea {
+ font-family: inherit;
+ font-size: 100%;
+ margin: 0; }
+
+button,
+input {
+ line-height: normal; }
+
+button,
+html input[type="button"],
+input[type="reset"],
+input[type="submit"] {
+ -webkit-appearance: button;
+ cursor: pointer; }
+
+button[disabled],
+html input[disabled] {
+ cursor: default; }
+
+input[type="checkbox"],
+input[type="radio"] {
+ padding: 0;
+ border: none; }
+
+input[type="search"] {
+ -webkit-appearance: textfield; }
+
+input[type="search"]::-webkit-search-cancel-button,
+input[type="search"]::-webkit-search-decoration {
+ -webkit-appearance: none; }
+
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+ border: 0;
+ padding: 0; }
+
+textarea {
+ overflow: auto;
+ vertical-align: top; }
+
+table {
+ border-collapse: collapse;
+ margin-bottom: 1.5em; }
+
+td,
+th {
+ padding: 1px;
+ vertical-align: top;
+ text-align: left; }
+
+td:first-child,
+th:first-child {
+ empty-cells: hide; }
+
+/* scripts */
+body > script {
+ display: none !important; }
+
+/* HTML5 for old browsers */
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+main,
+nav,
+section,
+summary {
+ display: block; }
+
+audio,
+canvas,
+video {
+ display: inline-block; }
+
+audio:not([controls]) {
+ display: none;
+ height: 0; }
+
+figure {
+ margin: 0; }
+
+[hidden],
+template {
+ display: none; }
+
+svg:not(:root) {
+ overflow: hidden; }
+
+/* Headings reset */
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-size: 1em;
+ font-weight: normal;
+ margin: 0; }
+
+/* Screen-reader only */
+.visually-hidden {
+ border: 0;
+ clip: rect(0 0 0 0);
+ clip-path: inset(50%);
+ height: 1px;
+ margin: -1px;
+ overflow: hidden;
+ padding: 0;
+ position: absolute;
+ width: 1px;
+ white-space: nowrap; }
+ .visually-hidden:focus, .visually-hidden:active {
+ clip: auto;
+ clip-path: none;
+ height: auto;
+ overflow: visible;
+ position: static;
+ width: auto;
+ white-space: normal; }
+
+.sr-only {
+ border: 0;
+ clip: rect(0 0 0 0);
+ clip-path: inset(50%);
+ height: 1px;
+ margin: -1px;
+ overflow: hidden;
+ padding: 0;
+ position: absolute;
+ width: 1px;
+ white-space: nowrap; }
+
+/** --------------------------------------------------
+ Colors
+--------------------------------------------------- */
+/* bright red */
+/* slategray */
+/* soft dark blue */
+/** --------------------------------------------------
+ Common rules
+--------------------------------------------------- */
+/* Typographie */
+:root {
+ --html-font-size: 62.5%;
+ --body-color: #323232;
+ --body-background: #fff; }
+
+html {
+ font-size: 62.5%;
+ font-size: var(--html-font-size); }
+
+body {
+ color: #323232;
+ background: #fff;
+ font-size: 1.4rem;
+ line-height: 1.5;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; }
+ @media screen and (max-width: 26.5em) {
+ body.responsive-font {
+ font-size: 1.2rem;
+ line-height: 1.3; } }
+ @media screen and (min-width: 120em) {
+ body.responsive-font {
+ font-size: 1.6rem;
+ line-height: 1.5; } }
+ @media screen and (min-width: 26.5em) and (max-width: 120em) {
+ body.responsive-font {
+ font-size: calc( 1.2rem + 0.4 * (100vw - 42.4rem) / (192 - 42.4));
+ line-height: calc( 1.3em + (1.5 - 1.3) * (100vw - 42.4rem) / (192 - 42.4)); } }
+
+@media screen and (max-width: 26.5em) {
+ #wrapper {
+ font-size: 1.2em; } }
+
+h2,
+h3,
+.as_h3,
+h4,
+.as_h4,
+h5,
+h6 {
+ margin-top: 0;
+ margin-bottom: 1em; }
+
+h2 {
+ color: #676e78;
+ font-size: 1.5em;
+ font-weight: normal;
+ line-height: 1.25;
+ padding: 0 0 1.5em; }
+
+h3,
+.as_h3 {
+ color: #d33800;
+ font-size: 1.34em;
+ font-weight: normal;
+ line-height: 1.5;
+ margin-top: 1em; }
+
+h4,
+.as_h4 {
+ color: #676e78;
+ font-size: 1.16em;
+ line-height: 1.5; }
+
+h5 {
+ color: #676e78;
+ font-size: 1em;
+ line-height: 1.5;
+ font-weight: bold; }
+
+h6 {
+ color: #676e78;
+ font-size: 1em;
+ line-height: 1.5; }
+
+/** --------------------------------------------------
+ Layout
+--------------------------------------------------- */
+#dotclear-admin {
+ display: flex;
+ min-height: 100vh;
+ flex-direction: column; }
+
+#header {
+ color: #fff;
+ background: #676e78;
+ border-bottom: 4px solid #a2cbe9;
+ width: 99.99%;
+ /* Bugfix Chrome >= 49.0.2623.108 */
+ display: table;
+ position: relative; }
+
+h1,
+#top-info-blog,
+#top-info-user {
+ display: table-cell;
+ padding: 8px 0;
+ margin: 0;
+ font-size: 1em;
+ vertical-align: top; }
+
+#wrapper {
+ background: #fff;
+ position: relative;
+ padding-top: 1.5em;
+ float: left;
+ width: 100%;
+ z-index: 10;
+ flex: 1; }
+
+.with-js #wrapper {
+ padding-top: 0; }
+
+#main {
+ width: 100%;
+ float: right;
+ margin-left: -14em;
+ margin-top: 0; }
+
+#content {
+ background: #fff;
+ margin: 0 0 0 14em;
+ padding: .5em 1.5em .75em 2.5em; }
+
+#main-menu {
+ background: #f3f3f3;
+ width: 14em;
+ float: left;
+ margin: 0;
+ padding-top: .5em;
+ padding-bottom: 1em;
+ overflow: hidden; }
+
+#footer {
+ background-color: #fff;
+ border-top: 1px solid #cbced1;
+ clear: both;
+ position: relative;
+ padding: .5em 0 .5em 1em;
+ text-align: left; }
+
+/* to hide main-menu */
+#collapser {
+ background: #c9c9c9;
+ position: absolute;
+ top: 0;
+ left: 14em;
+ width: 10px;
+ height: 100%;
+ overflow: hidden;
+ display: block;
+ border-right: 0;
+ border-bottom: 0;
+ z-index: 1;
+ transition: none; }
+ #collapser:hover, #collapser:focus {
+ background: #a2cbe9; }
+ .hide-mm #collapser {
+ background: #a2cbe9; }
+ .hide-mm #collapser:hover, .hide-mm #collapser:focus {
+ background: #c9c9c9; }
+
+.expand-mm {
+ display: none; }
+
+/* if main-menu is hidden */
+.hide-mm #main {
+ margin-left: 0; }
+.hide-mm #content {
+ margin-left: 10px; }
+ .hide-mm #content > h2 {
+ margin-left: calc(-1em - 10px); }
+.hide-mm #main-menu {
+ display: none; }
+.hide-mm #collapser {
+ left: 0; }
+.hide-mm .collapse-mm {
+ display: none; }
+.hide-mm .expand-mm {
+ display: block; }
+
+#wrapper.hide-mm {
+ background: #fff; }
+
+/* -------------------------------------------------------------- layout: two-cols */
+.two-cols {
+ position: static; }
+ .two-cols .col {
+ width: 48%;
+ margin-left: 2%;
+ float: left; }
+ .two-cols .col:first-child {
+ margin-left: 0;
+ margin-right: 2%; }
+ .two-cols .col:last-child {
+ margin-left: 2%;
+ margin-right: 0; }
+ .two-cols .col70 {
+ width: 68%;
+ margin-left: 0;
+ float: left; }
+ .two-cols .col70.last-col {
+ margin-left: 2%;
+ margin-right: 0; }
+ .two-cols .col30 {
+ width: 28%;
+ margin-left: 2%;
+ float: left; }
+ .two-cols .col30.first-col {
+ margin-left: 0;
+ margin-right: 2%; }
+ .two-cols table {
+ width: 90%; }
+
+/* -------------------------------------------------------------- layout: three-cols */
+.three-cols {
+ position: static; }
+ .three-cols .col {
+ width: 32.3%;
+ float: left;
+ margin-left: 1%; }
+ .three-cols .col:first-child {
+ margin-left: 0; }
+
+/* ------------------------------------------------- layout: optionnal one/two/three-boxes */
+.one-box {
+ text-align: justify; }
+
+.two-boxes {
+ width: 48.5%; }
+
+.three-boxes {
+ width: 30%; }
+
+.two-boxes,
+.three-boxes {
+ display: inline-block;
+ vertical-align: top;
+ margin: 0 1.5% 1em;
+ text-align: left; }
+
+.two-boxes:nth-of-type(odd),
+.three-boxes:nth-of-type(3n+1) {
+ margin-left: 0; }
+
+.two-boxes:nth-of-type(even),
+.three-boxes:nth-of-type(3n) {
+ margin-right: 0; }
+
+/* ---------------------------------------------------------------- layout: popups */
+.popup h1 {
+ color: #fff;
+ background: #676e78;
+ display: block;
+ width: 100%;
+ margin: 0;
+ font-size: 1.5em;
+ text-indent: 1em;
+ line-height: 1.5em;
+ font-weight: normal; }
+.popup #wrapper {
+ display: block;
+ float: none;
+ width: 100%;
+ margin: 0;
+ padding: 0;
+ background-position: 0 0; }
+.popup #main {
+ margin: 0;
+ padding: 0; }
+.popup #content {
+ margin: 0;
+ padding: 1em; }
+ .popup #content h2 {
+ margin: 0 0 1em 0;
+ padding: 0; }
+.popup #footer p {
+ border: none; }
+
+/* -------------------------------------------------------- layout: classes de complément */
+.constrained {
+ margin: 0;
+ padding: 0;
+ border: none;
+ background: transparent; }
+
+.table {
+ display: table; }
+
+.cell {
+ display: table-cell;
+ vertical-align: top; }
+
+.clear {
+ clear: both; }
+
+.lclear {
+ clear: left; }
+
+.clearer {
+ height: 1px;
+ font-size: 1px; }
+
+/* Micro clearfix thx to Nicolas Gallagher */
+.clearfix:before,
+.clearfix:after {
+ content: " ";
+ display: table; }
+
+.clearfix:after {
+ clear: both; }
+
+.box {
+ display: inline-block;
+ vertical-align: top;
+ margin: 0 10px 10px;
+ text-align: left;
+ flex: 1 1 auto; }
+ .box.small {
+ flex-basis: 312px; }
+ .box.medium {
+ flex-basis: 644px; }
+ .box.large {
+ width: 100%; }
+
+.odd {
+ margin-left: 0; }
+
+.even {
+ margin-right: 0; }
+
+/* ------------------------------------------------------------------------------------
+ UN POIL DE MEDIA QUERIES
+------------------------------------------------------------------------------------ */
+@media screen and (max-width: 80em) {
+ #header {
+ display: block;
+ width: 100%;
+ text-align: right; }
+ #header h1,
+ #header h1 a {
+ width: 120px;
+ margin: 0; }
+
+ h1 {
+ width: 19.5em;
+ display: inline-block;
+ vertical-align: top;
+ margin-right: 1em; }
+
+ #top-info-blog {
+ display: inline-block;
+ vertical-align: top;
+ margin-right: 1em; }
+ #top-info-blog #switchblog {
+ max-width: 16em; }
+ #top-info-blog a {
+ margin-left: 2em; }
+
+ #top-info-user {
+ display: block;
+ width: 100%; }
+
+ #collapser {
+ left: 17em; }
+
+ #main {
+ margin-left: -17em; }
+
+ #content {
+ margin: 0 0 0 17em; }
+
+ #main-menu {
+ width: 17em; }
+
+ .three-boxes,
+ .three-boxes .box,
+ .two-cols .col70,
+ .two-cols .col30 {
+ width: 100%;
+ margin-left: 0;
+ margin-right: 0; } }
+@media screen and (max-width: 48em) {
+ #dashboard-boxes .box.medium,
+ .box.medium,
+ #dashboard-boxes .box.small,
+ .box.small,
+ #dashboard-boxes .box.large,
+ .box.large {
+ width: 95%;
+ margin: 10px auto; } }
+@media screen and (max-width: 44em) {
+ #help-button {
+ height: 26px;
+ width: 26px;
+ margin: 0;
+ overflow: hidden; }
+
+ #content.with-help #help-button {
+ top: 10em; }
+
+ .one-box,
+ .two-boxes,
+ .box,
+ .two-cols .col {
+ width: 96%;
+ margin-left: 0;
+ margin-right: 0; } }
+@media screen and (max-width: 38em) {
+ #header h1,
+ #header h1 a {
+ width: 42px !important;
+ height: 42px; }
+
+ #wrapper,
+ #main,
+ #main-menu {
+ display: block;
+ float: none;
+ width: 100%;
+ margin: 0; }
+
+ #main-menu a {
+ display: block;
+ width: 100%; }
+ #main-menu h3 a {
+ display: inline; }
+
+ #content,
+ .hide-mm #content {
+ margin: 0; }
+
+ #collapser {
+ display: none; }
+
+ #main #content > h2 {
+ margin: 0 -.25em 1em; }
+
+ #dashboard-boxes .box.medium,
+ .box.medium,
+ #dashboard-boxes .box.small,
+ .box.small,
+ #dashboard-boxes .box.large,
+ .box.large {
+ width: 95%;
+ margin: 10px auto; }
+
+ .cell,
+ #filters-form .cell {
+ display: inline-block;
+ vertical-align: bottom; }
+
+ .pseudo-tabs li {
+ display: block;
+ float: left;
+ width: 50%; } }
+@media screen and (max-width: 26.5em) {
+ #top-info-blog label,
+ .nomobile {
+ display: none; }
+
+ #top-info-blog {
+ margin-bottom: .5em;
+ max-width: 75%; }
+ #top-info-blog select {
+ margin-bottom: .5em; }
+
+ #content.with-help #help-button {
+ top: 10em;
+ right: 28rem; }
+ #content.with-help #help {
+ width: 28rem; }
+
+ p.top-add {
+ margin-bottom: .5em; }
+
+ .part-tabs ul {
+ margin: 1em 0; }
+
+ .part-tabs li a {
+ display: block;
+ width: 100%; }
+
+ #icons p {
+ width: 9em; }
+
+ .media-item {
+ width: 90%; }
+
+ #theme-new,
+ #theme-activate,
+ #theme-deactivate {
+ margin-left: 0;
+ margin-right: 0; }
+
+ .box.current-theme {
+ margin: 5px;
+ width: 100%; }
+
+ .current-theme .module-sshot img {
+ margin: 0;
+ float: none;
+ max-width: 100%; }
+
+ table .maximal {
+ min-width: 14em; }
+
+ .pseudo-tabs li {
+ display: block;
+ width: 100%;
+ float: none; } }
+/** --------------------------------------------------
+ Elements
+--------------------------------------------------- */
+/* ------------------------------------------------------------------ titres */
+/* fil d'ariane */
+#content > h2 {
+ padding: 0 1em .5em 1em;
+ margin: 0 -1em 1em -1em;
+ background: #fff;
+ border-bottom: 1px solid #868686; }
+
+/* page courante dans le fil d'ariane */
+.page-title {
+ color: #d30e60; }
+ .page-title img {
+ padding-left: .5em;
+ vertical-align: middle; }
+
+/* autres titres */
+#main-menu h3 {
+ font-weight: bold; }
+
+.fieldset h3,
+.fieldset h4,
+.pretty-title {
+ color: #d33800;
+ font-size: 1em;
+ font-weight: bold; }
+
+.fieldset h3 {
+ font-size: 1.17em; }
+
+.fieldset h3.smart-title,
+.fieldset h4.smart-title,
+.smart-title {
+ font-size: 1em;
+ text-transform: uppercase;
+ font-weight: bold;
+ color: #323232; }
+
+#entry-sidebar h5 {
+ font-weight: normal;
+ color: #323232; }
+
+.entry-status img.img_select_option {
+ padding-left: 4px;
+ vertical-align: -1px; }
+
+h4 label,
+h5 label {
+ color: #323232; }
+
+h2:first-child,
+h3:first-child,
+h4:first-child,
+h5:first-child,
+ul:first-child,
+p:first-child {
+ margin-top: 0; }
+
+/* ---------------------------------------------------------------- tableaux */
+/* Pour autoriser le scroll sur les petites largeurs
+ envelopper les tableaux dans une div.table-outer */
+.table-outer {
+ width: 100%;
+ overflow: auto; }
+
+table {
+ font-size: 1em;
+ border-collapse: collapse;
+ margin: 0 0 1em 0;
+ width: 100%; }
+
+caption {
+ color: #323232;
+ font-weight: bold;
+ text-align: left;
+ margin-bottom: .5em; }
+
+th:not(.module-name) {
+ border-width: 1px 0 1px 0;
+ border-style: solid;
+ border-color: #dbdbdb;
+ background: #f3f3f3;
+ padding: .4em 1em .4em .5em;
+ vertical-align: top;
+ text-align: left; }
+
+td, th.module-name {
+ font-weight: normal;
+ border-width: 0 0 1px 0;
+ border-style: solid;
+ border-color: #ececec;
+ padding: .4em 1em .4em .5em;
+ vertical-align: top; }
+
+/* ---------------------------------------------------------- autres balises */
+p {
+ margin: 0 0 1em 0; }
+
+hr {
+ height: 1px;
+ border-width: 1px 0 0;
+ border-color: #dbdbdb;
+ background: #dbdbdb;
+ border-style: solid; }
+ hr.clearer {
+ clear: both; }
+
+pre,
+code,
+#debug {
+ font: 100% "Andale Mono", AndaleMono, Consolas, Monaco, "Courier New", monospace; }
+
+code {
+ color: #323232;
+ background: #fefacd; }
+
+pre {
+ white-space: pre;
+ white-space: -moz-pre-wrap;
+ white-space: pre-wrap;
+ white-space: pre-line;
+ word-wrap: break-word; }
+
+abbr {
+ cursor: help; }
+
+input,
+textarea,
+select,
+option,
+optgroup,
+legend,
+label {
+ font-size: 1em; }
+
+/* ------------------------------------------------------------------ liens */
+a,
+a:link,
+a:visited {
+ color: #2373a8;
+ text-decoration: none;
+ border-bottom: 1px dotted #868686;
+ background-color: inherit;
+ outline: 0; }
+ a img,
+ a:link img,
+ a:visited img {
+ border: none;
+ background: initial; }
+
+a:hover,
+a:active {
+ border-bottom-style: solid; }
+
+h1 a:link,
+h1 a:visited {
+ border: none; }
+
+.discrete a {
+ color: #323232; }
+
+a:link {
+ transition: .5s; }
+
+a:focus,
+a:focus img {
+ outline: 2px solid #bee74b;
+ border-bottom: none;
+ text-decoration: none; }
+
+a.outgoing img, .outgoing-js {
+ width: .75em;
+ filter: contrast(50%); }
+ #header a.outgoing img, #header .outgoing-js {
+ width: 1.25em;
+ padding: 0 0 0 .5em;
+ vertical-align: initial;
+ filter: contrast(100%); }
+
+input[type=text],
+input[type=color],
+input[type=email],
+input[type=url],
+input[type=datetime-local],
+input[type=date],
+input[type=time],
+input[type=file],
+input[type=number],
+input[type=password],
+input[type=submit],
+input[type=button],
+input[type=reset],
+a.button, button, textarea, select, legend {
+ max-width: 100%; }
+
+input[type=text],
+input[type=color],
+input[type=email],
+input[type=url],
+input[type=datetime-local],
+input[type=date],
+input[type=time],
+input[type=file],
+input[type=number],
+input[type=password],
+input[type=submit],
+input[type=button],
+input[type=reset],
+a.button, button, textarea, legend {
+ border-radius: 3px; }
+
+form {
+ display: block;
+ margin: 0;
+ padding: 0; }
+
+fieldset {
+ display: block;
+ margin: 1em 0;
+ padding: 1em 0.5em;
+ border-width: 1px 0;
+ border-style: solid;
+ border-color: #cbced1;
+ background: #fff; }
+ fieldset hr {
+ background-color: #cbced1;
+ border-width: 0;
+ margin: 1em 0; }
+ fieldset:focus-within {
+ background-color: #ededed; }
+
+input[type=text],
+input[type=color],
+input[type=email],
+input[type=url],
+input[type=datetime-local],
+input[type=date],
+input[type=time],
+input[type=file],
+input[type=number],
+textarea {
+ font-family: inherit;
+ font-size: 100%; }
+
+legend {
+ padding: 0.2em 0.6em;
+ border-width: 1px;
+ border-style: solid;
+ border-color: #676e78;
+ background: #fff;
+ margin-bottom: 0.5em; }
+
+label .maximal, textarea.maximal, input.maximal, select.maximal {
+ width: 99%; }
+
+input[type=text],
+input[type=password],
+input[type=color],
+input[type=email],
+input[type=url],
+input[type=datetime-local],
+input[type=date],
+input[type=time],
+input[type=file],
+input[type=number],
+textarea, select, input:not([type=file]):invalid, input:not([type=file]):invalid:placeholder-shown {
+ box-shadow: 1px 1px 2px #f3f3f3 inset;
+ padding: 3px;
+ vertical-align: top; }
+
+input[type=text],
+input[type=password],
+input[type=color],
+input[type=email],
+input[type=url],
+input[type=datetime-local],
+input[type=date],
+input[type=time],
+input[type=file],
+input[type=number],
+textarea, input:not([type=file]):invalid, input:not([type=file]):invalid:placeholder-shown {
+ border-width: 1px;
+ border-style: solid;
+ border-color: #dbdbdb; }
+
+input:invalid:not(:required), textarea:invalid:not(:required), select:invalid:not(:required), input:not([type=file]):invalid:not(:focus):not(:required) {
+ color: #000;
+ box-shadow: 0 0 0 3px rgba(174, 50, 59, 0.3); }
+
+input:invalid:not(:required), textarea:invalid:not(:required), input:not([type=file]):invalid:not(:focus):not(:required) {
+ border: 1px solid #ae323b;
+ background: #ffbaba; }
+
+input:focus, textarea:focus {
+ border-color: #bee74b; }
+
+textarea {
+ padding: 2px 0; }
+ textarea.maximal {
+ resize: vertical; }
+ .area textarea {
+ display: block;
+ width: 100%;
+ resize: vertical; }
+
+select {
+ padding: 2px 0;
+ vertical-align: middle; }
+
+@media not all and (min-resolution: 0.001dpcm) {
+ @supports (-webkit-appearance: none) {
+ /* Safari 10.1+ only (https://browserstrangeness.bitbucket.io/css_hacks.html#webkit) */
+ select {
+ font-size: initial; } } }
+select.l10n option {
+ padding-left: 16px; }
+
+option.avail10n {
+ background: transparent url(../images/check-on.png) no-repeat 0 50%; }
+
+input[type=text],
+input[type=color],
+input[type=email],
+input[type=url],
+input[type=datetime-local],
+input[type=date],
+input[type=time],
+input[type=number],
+input[type=password],
+textarea {
+ margin-right: .3em; }
+
+input[type=checkbox], input[type=radio], input[type=file] {
+ border: none;
+ margin: 0 .33em 0 0;
+ padding: 0; }
+
+input + input[type=checkbox] {
+ margin-left: .33em; }
+
+a input {
+ margin-right: .33em; }
+
+input[type=file] {
+ margin-top: .3em;
+ margin-bottom: .3em; }
+
+input[type=color] {
+ width: 4em;
+ height: 3em; }
+
+optgroup {
+ font-weight: bold;
+ font-style: normal; }
+
+option {
+ font-weight: normal; }
+
+label, label span {
+ display: block; }
+
+label.ib, input.ib {
+ display: inline-block; }
+
+label.classic {
+ display: inline; }
+
+label.classic input, label span input, label.classic select, label span select {
+ display: inline; }
+
+label.required {
+ font-weight: bold; }
+
+label.required abbr {
+ color: #ae323b;
+ font-size: 1.3em;
+ text-decoration: none; }
+
+label.bold {
+ text-transform: uppercase;
+ font-weight: bold;
+ margin-top: 2em; }
+
+label.area, p.area, div.area {
+ width: inherit !important; }
+
+div.area {
+ margin-bottom: 1em; }
+
+p.field {
+ position: relative; }
+ p.field label {
+ display: inline-block;
+ width: 14em; }
+ p.field.wide label {
+ width: 21em; }
+ p.field input, p.field select {
+ display: inline-block; }
+
+.form-note, .form-stats {
+ font-style: italic;
+ font-weight: normal;
+ color: #676e78; }
+
+p.form-note, p.form-stats {
+ margin-top: -.7em; }
+
+span.form-note, span.form-stats {
+ text-transform: none; }
+
+.missing {
+ background-color: inherit;
+ animation-name: kf-missing;
+ animation-duration: 1s; }
+
+@keyframes kf-missing {
+ 50% {
+ background-color: #ffbaba; } }
+.focus {
+ background-color: inherit;
+ animation-name: kf-focus;
+ animation-duration: 1s; }
+
+@keyframes kf-focus {
+ 50% {
+ background-color: #bee74b; } }
+.no-more-info {
+ display: none; }
+
+/* Removes inner padding and border in FF3+ - Knacss */
+button::-moz-focus-inner,
+input[type=button]::-moz-focus-inner,
+input[type=reset]::-moz-focus-inner,
+input[type=submit]::-moz-focus-inner {
+ border: 0;
+ padding: 0; }
+
+/* tous les boutons */
+button,
+a.button,
+input[type=button],
+input[type=reset],
+input[type=submit] {
+ border: 1px solid #cbced1;
+ font-family: inherit;
+ padding: 3px 10px;
+ line-height: normal !important;
+ display: inline-block;
+ font-size: 100%;
+ text-align: center;
+ text-decoration: none;
+ cursor: pointer;
+ position: relative;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
+ border-style: solid;
+ border-width: 1px; }
+
+a.button {
+ vertical-align: middle; }
+
+/* validation */
+input[type=submit],
+button[type=submit],
+a.button.submit,
+button.submit,
+input.button.start {
+ color: #fff;
+ background-color: #25a6e1;
+ background-image: linear-gradient(to bottom, #25a6e1, #188bc0);
+ border-color: #25a6e1; }
+
+input[type=submit]:hover,
+input[type=submit]:focus,
+button[type=submit]:hover,
+button[type=submit]:focus,
+input.button.start:hover,
+input.button.start:focus,
+button.submit:hover,
+button.submit:focus,
+a.button.submit:hover,
+a.button.submit:focus {
+ background-color: #188bc0;
+ background-image: linear-gradient(to bottom, #188bc0, #25a6e1);
+ border-color: #188bc0; }
+
+/* suppression, reset, "neutres" fond gris */
+button,
+input[type=button],
+input.button,
+input[type=reset],
+input[type=submit].reset,
+input.reset,
+input[type=submit].delete,
+input.delete,
+a.button,
+a.button.delete,
+a.button.reset {
+ color: #000;
+ background-color: #eaeaea;
+ background-image: linear-gradient(to bottom, #f9f9f9, #eaeaea);
+ background-repeat: repeat-x;
+ border-color: #cbced1; }
+
+button:hover,
+input[type=button]:hover,
+input.button:hover,
+button:focus,
+input[type=button]:focus,
+input.button:focus,
+input[type=reset]:hover,
+input[type=submit].reset:hover,
+input.reset:hover,
+input[type=reset]:focus,
+input[type=submit].reset:focus,
+input.reset:focus,
+input[type=submit].delete:hover,
+input.delete:hover,
+input[type=submit].delete:focus,
+input.delete:focus,
+a.button.delete:hover,
+a.button.reset:hover,
+a.button:hover,
+a.button.delete:focus,
+a.button.reset:focus,
+a.button:focus {
+ background-color: #dadada;
+ background-image: linear-gradient(to bottom, #eaeaea, #dadada);
+ background-repeat: repeat-x;
+ border-color: #cbced1; }
+
+/* suppression */
+input[type=submit].delete,
+input.delete,
+button[type=submit].delete,
+button.delete,
+a.button.delete {
+ color: #ae323b; }
+
+input[type=submit].delete:hover,
+input.delete:hover,
+button[type=submit].delete:hover,
+button.delete:hover,
+a.button.delete:hover,
+input[type=submit].delete:focus,
+input.delete:focus,
+button[type=submit].delete:focus,
+button.delete:focus,
+a.button.delete:focus {
+ color: #fff;
+ background-color: #b33630;
+ background-image: linear-gradient(to bottom, #dc5f59, #b33630);
+ background-repeat: repeat-x;
+ border-color: #dc5f59; }
+
+#info-box a.button,
+#info-box button {
+ padding: 0 .5em;
+ margin-left: 2em; }
+
+.button.add,
+button.add {
+ color: #000;
+ background-color: #bee74b;
+ background-image: linear-gradient(to bottom, #bee74b, #9bca1c);
+ border-color: #bee74b;
+ padding: .33em 1.33em .5em; }
+
+.button.add:hover,
+.button.add:active,
+.button.add:focus,
+button.add:hover,
+button.add:active,
+button.add:focus {
+ background-color: #9bca1c;
+ background-image: linear-gradient(to bottom, #9bca1c, #bee74b);
+ border-color: #9bca1c; }
+
+.button-add:focus {
+ outline: dotted 1px; }
+
+/* paragraphe pour bouton Nouveau bidule */
+p.top-add {
+ text-align: right;
+ margin: 0; }
+
+/* disabled */
+input.disabled,
+input[type=submit].disabled,
+button.disabled,
+button[type=submit].disabled {
+ color: #676e78;
+ background: #f3f3f3;
+ border: 1px solid #cbced1; }
+
+input.disabled:hover,
+input[type=submit].disabled:hover,
+button.disabled:hover,
+button[type=submit].disabled:hover {
+ color: #676e78;
+ background: #ececec;
+ border: 1px solid #cbced1; }
+
+/* Boutons javascript (dépliage/repliage, …) */
+.void-btn {
+ border: none;
+ border-radius: 0;
+ padding: 0; }
+
+button.details-cmd {
+ font-size: 0.9em;
+ border: none;
+ border-radius: 0;
+ padding: 0;
+ margin: 0 5px 0 0;
+ color: currentColor;
+ background: transparent;
+ box-shadow: none;
+ }
+ button.details-cmd:hover, button.details-cmd:focus {
+ background: transparent;
+ color: #bee74b; }
+
+/* specific buttons */
+.checkbox-helper,
+#gototop,
+.metaGetList,
+.metaGetMore,
+a.checkbox-helper,
+a#gototop,
+a.metaGetList,
+a.metaGetMore {
+ font-size: 0.825em;
+ color: #323232;
+ background: #fff;
+ box-shadow: none;
+ border: 1px solid #676e78;
+ margin-bottom: .25em;
+ text-align: center; }
+ .checkbox-helper:hover,
+ #gototop:hover,
+ .metaGetList:hover,
+ .metaGetMore:hover,
+ a.checkbox-helper:hover,
+ a#gototop:hover,
+ a.metaGetList:hover,
+ a.metaGetMore:hover {
+ background: #a2cbe9;
+ box-shadow: none;
+ border: 1px solid #676e78; }
+
+#gototop {
+ display: none;
+ z-index: 1000;
+ position: fixed;
+ bottom: 0;
+ right: .5em;
+ width: 10em;
+ padding: .25em;
+ border-radius: .25em; }
+ #gototop a,
+ #gototop a:link,
+ #gototop a:hover,
+ #gototop a:active {
+ color: #323232;
+ background: transparent;
+ border: none; }
+
+.metaRemove,
+.addMeta button:not(.metaGetMore),
+.addMeta a:not(.metaGetMore) {
+ border: none;
+ border-radius: 0;
+ padding: 0;
+ color: #000;
+ background: transparent; }
+
+.addMeta button:not(.metaGetMore),
+.addMeta a:not(.metaGetMore) {
+ box-shadow: initial;
+ margin-bottom: 2px; }
+ .addMeta button:not(.metaGetMore):hover, .addMeta button:not(.metaGetMore):focus,
+ .addMeta a:not(.metaGetMore):hover,
+ .addMeta a:not(.metaGetMore):focus {
+ color: #000;
+ background: #abd0eb; }
+
+.warn,
+.warning,
+.info {
+ font-style: normal;
+ padding: .2em .66em .2em;
+ text-indent: 24px;
+ display: inline-block;
+ line-height: 1.5em;
+ border-radius: 3px; }
+
+.info {
+ color: #323232;
+ background: #d9edf7 url(msg-info.png) no-repeat 0.3em 0.3em;
+ border: 1px solid #bce8f1; }
+
+.warn,
+.warning {
+ color: #323232;
+ background: #fefacd url(msg-warning.png) no-repeat 0.3em 0.3em;
+ border: 1px solid #ffd478; }
+
+div.warn,
+div.warning,
+div.info {
+ display: block;
+ padding: 1em 1em .33em 1em;
+ margin-bottom: 1em; }
+
+span.warn,
+span.warning,
+span.info {
+ padding-top: 1px;
+ padding-bottom: 1px;
+ background-position: .3em .2em; }
+
+.error,
+.message,
+.static-msg,
+.success,
+.warning-msg {
+ padding: 1em 0.5em 0.5em 48px;
+ margin-bottom: 1em;
+ border-radius: 8px;
+ box-shadow: 1px 1px 2px rgba(50, 50, 50, 0.1); }
+
+p.error,
+p.message,
+p.static-msg,
+p.success,
+p.warning-msg {
+ padding-top: 1em;
+ padding-bottom: 1em;
+ margin-top: .5em; }
+
+.error {
+ background: #ffbaba url(msg-error.png) no-repeat 0.7em 0.7em;
+ color: #000;
+ animation-name: kf-error;
+ animation-duration: .5s; }
+
+@keyframes kf-error {
+ 0% {
+ background-color: #fefacd; }
+ 100% {
+ background-color: #ffbaba; } }
+.message,
+.static-msg {
+ color: #fff;
+ background: #676e78 url(msg-std.png) no-repeat 0.7em 0.7em; }
+
+.message {
+ animation-name: kf-message;
+ animation-duration: .5s; }
+
+@keyframes kf-message {
+ 0% {
+ background-color: #cbced1; }
+ 100% {
+ background-color: #676e78; } }
+.message a,
+.static-msg a,
+.message h3,
+.static-msg h3 {
+ color: #fff; }
+
+.success {
+ color: #000; }
+
+.success {
+ background: #bee74b url(msg-success.png) no-repeat 0.7em 0.7em;
+ animation-name: kf-success;
+ animation-duration: .5s; }
+
+@keyframes kf-success {
+ 0% {
+ background-color: #9bca1c; }
+ 100% {
+ background-color: #bee74b; } }
+.warning-msg {
+ color: #323232;
+ background: #ffd478 url(msg-warning.png) no-repeat 0.7em 0.7em;
+ border: 1px solid #ffd478;
+ animation-name: kf-warning;
+ animation-duration: .5s; }
+
+@keyframes kf-warning {
+ 0% {
+ background-color: #fefacd; }
+ 100% {
+ background-color: #ffd478; } }
+.success a,
+.warning-msg a,
+.info a {
+ color: #323232; }
+
+.close-notice-parent {
+ display: flex;
+ justify-content: space-between; }
+ .close-notice-parent ul, .close-notice-parent p + p {
+ flex: 1; }
+ .close-notice-parent p + p {
+ padding-left: .25em; }
+
+.close-notice {
+ background: none;
+ border: none;
+ border-radius: 0;
+ box-shadow: none;
+ padding-left: 1em; }
+ .close-notice:hover, .close-notice:focus {
+ background: none; }
+
+.dc-update {
+ padding: 1em 48px 0.5em 48px;
+ margin-bottom: 1em;
+ border-radius: 8px;
+ color: #000;
+ background: #a2cbe9 url(msg-success.png) no-repeat 0.7em 0.7em;
+ box-shadow: 1px 1px 2px rgba(50, 50, 50, 0.1); }
+ .dc-update h3 {
+ margin-top: 0;
+ color: #000; }
+ .dc-update p {
+ display: inline-block;
+ vertical-align: middle; }
+ .dc-update a {
+ color: #000;
+ margin-right: 1em; }
+ .dc-update a.button {
+ padding: .5em 1em; }
+
+.updt-info a {
+ margin-left: 2em;
+ border-color: #000;
+ font-weight: bold; }
+
+body.ajax-loader #header {
+ border-bottom-color: #92b7d2;
+ transition: border-bottom-color .3s ease; }
+body.ajax-loader #collapser {
+ background-color: #92b7d2;
+ transition: background-color .3s ease; }
+
+/** --------------------------------------------------
+ Components
+--------------------------------------------------- */
+/* prelude */
+#prelude {
+ line-height: 1.5;
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ background: #a2cbe9;
+ width: 100%; }
+ #prelude li {
+ list-style-type: none;
+ margin: 0;
+ background: transparent;
+ display: inline; }
+ #prelude li a {
+ padding: 3px 16px 3px 8px;
+ color: #323232;
+ background: #a2cbe9;
+ text-decoration: underline; }
+ #prelude li a:hover, #prelude li a:focus {
+ background: #fff; }
+
+/* si le prélude est affiché on repousse les trucs dessous */
+#wrapper.with-prelude {
+ padding-top: 1em; }
+
+#help-button.with-prelude,
+#collapser.with-prelude {
+ top: 1em; }
+
+/* header global h1, form#top-info-blog, ul#top-info-user */
+#header a {
+ color: #fff; }
+#header img {
+ vertical-align: middle;
+ padding-left: .5em; }
+
+/* h1 */
+h1 {
+ text-indent: 100%;
+ width: 16.5em; }
+ h1 a {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 150px;
+ height: 36px;
+ color: #fff;
+ background: transparent url(dc_logos/b-dotclear120.png) no-repeat 0 6px;
+ transition: none; }
+ h1 a:hover, h1 a:focus {
+ background-position: 0 -94px;
+ background-color: transparent;
+ transition: none; }
+ h1 a:link {
+ transition-timing-function: ease-in-out; }
+
+/* top-info-blog */
+#top-info-blog select {
+ max-width: 20em; }
+#top-info-blog a {
+ margin-left: 1.5em; }
+#top-info-blog input[type=submit] {
+ background: #25a6e1;
+ border-color: #cbced1;
+ margin-left: .33em; }
+#top-info-blog input[type=submit]:hover {
+ color: #fff;
+ background: #188bc0; }
+#top-info-blog p {
+ display: inline-block;
+ margin: 0; }
+
+/* top-info-user */
+#top-info-user {
+ padding-right: .5em;
+ list-style-type: none;
+ text-align: right; }
+ #top-info-user li {
+ display: inline-block;
+ margin-left: .5em;
+ padding-left: .5em;
+ border-left: 1px solid #868686; }
+ #top-info-user li:first-child {
+ border-left: none; }
+ #top-info-user a.active {
+ border-width: 0;
+ border-radius: 4px;
+ margin: 0;
+ padding: 2px 8px 3px;
+ color: #d33800;
+ background-color: #f3f3f3;
+ font-weight: bold; }
+
+/* ------------------------------------------------------------------------------------
+ UN POIL DE MEDIA QUERIES
+------------------------------------------------------------------------------------ */
+@media screen and (max-width: 26.5em) {
+ h1,
+ h1 a {
+ padding: 0; } }
+@media screen and (max-width: 38em) {
+ h1 a:link {
+ background: transparent url(dc_logos/b-dotclear120.png) no-repeat -270px 6px; }
+
+ h1 a:hover,
+ h1 a:focus {
+ background: url(dc_logos/b-dotclear120.png) no-repeat -270px -94px; } }
+#main-menu div:last-child {
+ border-bottom: none; }
+#main-menu h3 {
+ margin: 0;
+ padding: 10px 0 10px 8px;
+ color: #676e78;
+ font-size: 1.15em; }
+#main-menu a {
+ color: #323232;
+ border-bottom-color: #cbced1; }
+#main-menu ul {
+ margin: 0 0 1.5em 0;
+ padding: 0;
+ list-style: none; }
+ #main-menu ul li {
+ display: block;
+ margin: 0.5em 0 0;
+ padding: 4px 0 1px 32px;
+ background-repeat: no-repeat;
+ background-position: 8px .3em;
+ position: relative; }
+ #main-menu ul li a::after {
+ position: absolute;
+ content: "";
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0; }
+ #main-menu ul li:hover {
+ background-color: #fff; }
+ #main-menu ul li:first-child {
+ margin-top: 0; }
+#main-menu .active {
+ background-color: #fff;
+ font-weight: bold; }
+ #main-menu .active a {
+ border-bottom: none;
+ color: #d30e60; }
+
+#favorites-menu,
+#blog-menu,
+#system-menu,
+#plugins-menu {
+ border-bottom: 1px dashed #a2cbe9; }
+
+#favorites-menu h3 {
+ font-variant: small-caps;
+ padding-top: .2em; }
+
+#search-menu {
+ padding: 4px 0 0 4px;
+ font-size: .91em; }
+ #search-menu p {
+ width: 95%;
+ margin: 0 0 .5em 0; }
+ #search-menu input[type="submit"] {
+ float: right; }
+
+#qx {
+ width: 75%;
+ background: transparent url(search.svg) no-repeat 0 center;
+ text-indent: 20px; }
+
+.part-tabs ul {
+ padding: .5em 0 0 1em;
+ border-bottom: 1px solid #c9c9c9;
+ line-height: 1.8; }
+.part-tabs li {
+ list-style: none;
+ margin: 0;
+ display: inline; }
+ .part-tabs li:first-child a {
+ border-top-left-radius: 3px; }
+ .part-tabs li:last-child a {
+ border-top-right-radius: 3px; }
+ .part-tabs li a {
+ padding: .33em 1.5em;
+ margin-right: -1px;
+ border: 1px solid #c9c9c9;
+ border-bottom: none;
+ text-decoration: none;
+ color: #323232;
+ background-color: #f3f3f3;
+ display: inline-block; }
+ .part-tabs li a:hover, .part-tabs li a:focus {
+ color: #323232;
+ background: #fff;
+ border-bottom-color: #fff; }
+ .part-tabs li a:link {
+ transition: unset; }
+ .part-tabs li.part-tabs-active a {
+ color: #d33800;
+ background: #fff;
+ font-weight: bold;
+ border-bottom-color: #fff; }
+
+.multi-part {
+ padding-left: 1em; }
+
+.pseudo-tabs {
+ margin: -.75em 0 2em 0;
+ border-bottom: 1px solid #c9c9c9;
+ display: table;
+ width: 100%;
+ padding: 0;
+ line-height: 24px;
+ border-collapse: collapse; }
+ .pseudo-tabs li {
+ display: table-cell;
+ border-width: 0 1px;
+ border-style: solid;
+ border-color: #c9c9c9;
+ padding: 0;
+ margin: 0;
+ text-align: center; }
+ .pseudo-tabs a {
+ display: block;
+ font-weight: bold;
+ padding: 0 24px;
+ border-bottom: none; }
+ .pseudo-tabs a:hover, .pseudo-tabs a:focus {
+ background-color: #f3f3f3;
+ color: #323232; }
+ .pseudo-tabs a.active {
+ background-color: #fff;
+ color: #d33800; }
+
+/* contextual help */
+#help {
+ margin-top: 4em;
+ background: #f3f3f3;
+ z-index: 100;
+ clear: both;
+ padding: 0 1em; }
+ #content.with-help #help {
+ display: block;
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: 32rem;
+ border-left: 2px solid #ffd478;
+ border-top: 2px solid #ffd478;
+ margin-top: 0;
+ padding: .5em 0 0 0;
+ overflow: auto; }
+
+#help-button {
+ background: transparent url(help-mini.png) no-repeat 6px center;
+ position: absolute;
+ top: 0;
+ right: 0;
+ padding: 0 1.5em 0 30px;
+ cursor: pointer;
+ color: #2373a8;
+ line-height: 3; }
+ #help-button.floatable {
+ border-top: 2px solid #c9c9c9;
+ border-left: 2px solid #c9c9c9;
+ border-bottom: 2px solid #c9c9c9;
+ border-bottom-left-radius: 1em;
+ border-top-left-radius: 1em;
+ background-color: #f3f3f3;
+ position: fixed;
+ top: 10px;
+ -webkit-transform: translateZ(0); }
+ .no-js #help-button {
+ top: 1em; }
+ #help-button span {
+ padding: .5em 0 .1em 0; }
+ #content.with-help #help-button {
+ right: 32rem;
+ background-color: #f3f3f3;
+ position: fixed;
+ top: 6em;
+ z-index: 100;
+ border-top: 2px solid #ffd478;
+ border-left: 2px solid #ffd478;
+ border-bottom: 2px solid #ffd478;
+ border-bottom-left-radius: 1em;
+ border-top-left-radius: 1em; }
+
+.help-box {
+ display: none; }
+ .help-box ul {
+ padding-left: 20px;
+ margin-left: 0; }
+
+#content.with-help .help-content {
+ padding: 0 1em 1em; }
+.help-content dt {
+ font-weight: bold;
+ color: #676e78;
+ margin: 0; }
+.help-content dd {
+ margin: 0.3em 0 1.5em 0; }
+
+/* lien d'aide générale dans le help content */
+#helplink p {
+ padding: 0 0 0 .5em; }
+
+#footer p {
+ margin: 0;
+ padding: 0 1em;
+ font-size: 1em; }
+#footer a:hover span.tooltip {
+ padding: 10px;
+ color: #910ed3;
+ height: auto;
+ width: auto;
+ left: 0;
+ bottom: 0;
+ background: rgba(255, 255, 255, 0.9);
+ z-index: 99;
+ font-family: monospace;
+ text-align: left;
+ border-top: 1px solid #910ed3;
+ border-right: 1px solid #910ed3;
+ border-radius: 0 2em 0 0; }
+
+span.credit {
+ font-size: 1em;
+ font-weight: normal; }
+
+span.tooltip {
+ position: absolute;
+ padding: 0;
+ border: 0;
+ height: 1px;
+ width: 1px;
+ overflow: hidden; }
+
+/** --------------------------------------------------
+ Tables and Filters
+--------------------------------------------------- */
+table .maximal {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ max-width: 1px; }
+table .maximal, table.maximal {
+ width: 100%; }
+table .minimal {
+ width: 1px; }
+table .nowrap {
+ white-space: nowrap;
+ vertical-align: top; }
+table .count {
+ text-align: right;
+ padding-right: 1.5em; }
+
+th.first input {
+ padding-right: 34px; }
+
+th img, tr.line img {
+ vertical-align: middle; }
+ th img.expand, tr.line img.expand {
+ margin-right: 6px;
+ margin-bottom: -2px; }
+
+tr.line p {
+ margin: 0; }
+tr.line input, tr.line select {
+ vertical-align: middle;
+ box-shadow: none; }
+tr.line select {
+ width: 6em; }
+tr.line:hover {
+ background: #f3f3f3; }
+tr.line:focus-within {
+ background-color: #ededed; }
+
+td.status {
+ vertical-align: middle; }
+ td.status a {
+ border: none; }
+
+.noborder td, td.noborder, .noborder th, th.noborder {
+ border-width: 0 0 1px 0;
+ border-color: #dbdbdb;
+ line-height: 2em;
+ padding-bottom: 0; }
+
+.noborder p {
+ margin-bottom: 0; }
+
+table.posts-list {
+ min-width: 50%; }
+
+table.settings, table.prefs {
+ margin-bottom: 3em; }
+ table.settings th:first-child, table.prefs th:first-child {
+ width: 20%; }
+ table.settings th + th, table.prefs th + th {
+ width: 30%; }
+ table.settings th + th + th, table.prefs th + th + th {
+ width: 10%; }
+ table.settings th:last-child, table.prefs th:last-child {
+ width: 40%; }
+
+/* js */
+td.expand {
+ padding: 1em; }
+ td.expand td {
+ border-bottom: none; }
+
+.handle {
+ padding: 0; }
+
+.handler {
+ cursor: move;
+ background: transparent url(drag.png) no-repeat 0 50%;
+ padding-left: 15px; }
+
+/* Responsive Cell Header */
+.rch td::before {
+ display: none; }
+
+@media screen and (max-width: 44em), print and (max-width: 5in) {
+ table.rch {
+ display: block; }
+ table.rch caption, table.rch tbody, table.rch tr, table.rch td {
+ display: block; }
+ table.rch th, table.rch tr:first-of-type {
+ display: none; }
+ table.rch td:first-of-type {
+ border-top: 1px solid #ececec;
+ color: #c9c9c9;
+ background: #595959; }
+ table.rch td::before {
+ display: inline;
+ font-weight: bold; }
+ table.rch td {
+ display: grid;
+ grid-template-columns: 10em auto;
+ grid-gap: 1em 0.5em;
+ text-align: left;
+ border: none; }
+ table.rch .maximal {
+ max-width: inherit; }
+ table.rch .nowrap {
+ white-space: inherit; }
+ table.rch td.expand {
+ grid-template-columns: auto !important;
+ color: #323232;
+ background-color: #fff;
+ border-top: 1px dashed #ececec; }
+ table.rch input, table.rch select {
+ align-self: center; }
+
+ table.rch-thead thead {
+ display: none; }
+ table.rch-thead tr:first-of-type {
+ display: block; } }
+a.form-control {
+ display: none;
+ color: #000; }
+ a.form-control::before {
+ content: "►";
+ margin-right: 5px; }
+ a.form-control.open::before {
+ content: "▼"; }
+
+#filters-form {
+ border: 1px solid #a2cbe9;
+ border-radius: .3em;
+ margin-bottom: 2em;
+ padding: .5em 1em 0; }
+ #filters-form .table {
+ width: 100%;
+ padding: 0;
+ margin-bottom: 1em;
+ margin-top: .5em; }
+ #filters-form .cell {
+ padding: 0 2em 0 0; }
+ #filters-form .filters-sibling-cell {
+ padding-top: 3.8em; }
+ #filters-form .filters-options {
+ padding-left: 2em;
+ border-left: 1px solid #c9c9c9; }
+ #filters-form select {
+ width: 14em;
+ vertical-align: middle; }
+ #filters-form h4 {
+ margin-top: 0;
+ margin-bottom: 2em; }
+ #filters-form label.ib,
+ #filters-form span.ib {
+ width: 7em; }
+ #filters-form label.ibw,
+ #filters-form span.ibw {
+ width: 9em;
+ display: inline-block; }
+ #filters-form:focus-within {
+ background-color: #ededed; }
+
+span.ib {
+ width: 7em; }
+
+span.ibw {
+ width: 9em;
+ display: inline-block; }
+
+/** --------------------------------------------------
+ Pages
+--------------------------------------------------- */
+#login-screen {
+ display: block;
+ width: 20em;
+ margin: 1.5em auto 0;
+ font-size: 1.16em; }
+ #login-screen h1 {
+ text-indent: -2000px;
+ background: transparent url(dc_logos/w-dotclear240.png) no-repeat top left;
+ height: 66px;
+ width: 20em;
+ margin-bottom: .5em;
+ margin-left: 0; }
+ #login-screen .fieldset {
+ border: 1px solid #9bca1c;
+ padding: 1em 1em 0 1em;
+ background: #fff;
+ margin-bottom: 0;
+ margin-top: 1em; }
+ #login-screen input[type=text],
+ #login-screen input[type=color],
+ #login-screen input[type=email],
+ #login-screen input[type=url],
+ #login-screen input[type=datetime-local],
+ #login-screen input[type=date],
+ #login-screen input[type=time],
+ #login-screen input[type=file],
+ #login-screen input[type=number],
+ #login-screen input[type=password],
+ #login-screen input[type=submit],
+ #login-screen input[type=text]:focus,
+ #login-screen input[type=color]:focus,
+ #login-screen input[type=email]:focus,
+ #login-screen input[type=url]:focus,
+ #login-screen input[type=datetime-local]:focus,
+ #login-screen input[type=date]:focus,
+ #login-screen input[type=time]:focus,
+ #login-screen input[type=file]:focus,
+ #login-screen input[type=number]:focus,
+ #login-screen input[type=password]:focus,
+ #login-screen input[type=submit]:focus {
+ width: 100%;
+ margin: 0;
+ padding: 5px 3px; }
+ #login-screen input.login,
+ #login-screen input.login:focus {
+ padding-top: 6px;
+ padding-bottom: 6px;
+ font-size: 1em; }
+ #login-screen #issue {
+ margin-left: 1.33em;
+ font-size: .91em; }
+ #login-screen #issue p:first-child {
+ text-align: right; }
+ #login-screen #issue strong {
+ font-weight: normal; }
+
+#dashboard-main {
+ text-align: center; }
+ #dashboard-main > *:last-child {
+ margin-bottom: 1em; }
+
+/* raccourcis */
+#icons {
+ margin: 1em auto 2em;
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center; }
+ #icons p {
+ width: 13em;
+ margin: 1em 0 2em;
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+ align-items: center; }
+ #icons a, #icons a:link, #icons a:visited, #icons a:hover, #icons a:focus {
+ border-bottom-width: 0;
+ text-decoration: none; }
+ #icons a span {
+ color: #323232;
+ border-bottom: 1px dotted #868686; }
+ #icons a img {
+ padding: 1.5em;
+ background-color: #f9f9f9;
+ border-radius: 8px;
+ border: 1px solid #dbdbdb;
+ display: inline-block;
+ filter: contrast(100%); }
+ #icons a:focus {
+ outline: 0; }
+ #icons a:focus span {
+ border: 2px solid #bee74b; }
+ #icons a:focus img, #icons a:hover img {
+ background: #bee74b;
+ outline: 0;
+ border-color: #dbdbdb; }
+ #icons a:focus span, #icons a:hover span {
+ border-bottom-style: solid; }
+
+/* billet rapide */
+#quick {
+ max-width: 72em;
+ margin: 1em auto 2em;
+ padding: 1em;
+ background: #f3f3f3;
+ border: 1px solid #dbdbdb;
+ text-align: left; }
+ #quick h3 {
+ margin-bottom: 0.2em;
+ font-size: 1.2em; }
+ #quick p.qinfo {
+ margin: -.7em -1em 1em;
+ background: #d9edf7 url(msg-info.png) no-repeat 0.2em 0.2em;
+ border: 1px solid #bce8f1;
+ padding: .2em 1em .1em 24px;
+ color: #323232; }
+ #quick #new_cat, #quick .q-cat, #quick .q-cat label {
+ display: inline-block;
+ vertical-align: top;
+ margin-right: 1em;
+ margin-top: 0; }
+ #quick .q-cat label {
+ margin-right: .3em; }
+ #quick #new_cat {
+ margin-bottom: 2em; }
+
+/* modules additionnels */
+#dashboard-boxes {
+ margin: 1em auto 2em;
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center; }
+ #dashboard-boxes .box {
+ padding: 10px;
+ border: 1px solid #cbced1;
+ border-radius: 3px;
+ min-height: 200px;
+ margin: 10px;
+ text-align: left; }
+
+.db-items, .db-contents {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ flex: 1 1 auto; }
+ .db-items img, .db-contents img {
+ vertical-align: middle; }
+ .db-items ul, .db-contents ul {
+ display: block;
+ padding-left: 1.5em;
+ list-style: square; }
+ .db-items li, .db-contents li {
+ margin: 0.25em 0 0 0; }
+
+.no-js .outgoing img {
+ display: none; }
+
+.dc-box {
+ background: transparent url(dc_logos/sq-logo-32.png) no-repeat top right; }
+
+#news dt {
+ font-weight: bold;
+ margin: 0 0 0.4em 0; }
+#news dd {
+ margin: 0 0 1em 0; }
+ #news dd p {
+ margin: 0.2em 0 0 0; }
+
+#dragndrop {
+ position: absolute; }
+ .no-js #dragndrop {
+ display: none; }
+ #dragndrop + label {
+ position: absolute;
+ display: inline-block;
+ line-height: 1; }
+ #dragndrop + label .dragndrop-svg {
+ width: 2em;
+ height: 1.5em;
+ fill: #868686; }
+ #dragndrop:checked + label .dragndrop-svg {
+ fill: #137bbb;
+ background-color: var(--body-background); }
+
+#media_img_title_pattern {
+ margin-right: 1em; }
+
+#part-users > div {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: left; }
+
+.user-perm {
+ margin: 0 1em 1em 0;
+ background: transparent url(user.png) no-repeat 0.5em 0.5em;
+ width: 20em;
+ border: 1px solid #cbced1;
+ border-radius: .5em;
+ padding: 0 1em .5em; }
+ .user-perm h4,
+ .user-perm h5,
+ .user-perm p,
+ .user-perm ul,
+ .user-perm li {
+ margin: .5em 0 .33em;
+ padding: 0; }
+ .user-perm h4 {
+ padding-left: 28px; }
+ .user-perm h5 {
+ margin: 1em 0 0 0; }
+ .user-perm li {
+ margin-left: 1em;
+ padding-left: 0; }
+ .user-perm.user_super {
+ border-color: #bee74b;
+ background-color: rgba(217, 237, 247, 0.5); }
+
+li.user_super,
+li.user_admin {
+ margin-left: 0;
+ padding-left: 20px;
+ list-style: none;
+ background: transparent url(../images/superadmin.png) no-repeat 0 0.3em; }
+
+li.user_admin {
+ background-image: url(../images/admin.png); }
+
+/* pour les alignements verticaux */
+#theme-new,
+#theme-activate,
+#theme-deactivate,
+#theme-update {
+ margin-left: -10px;
+ margin-right: -10px; }
+
+.box.theme {
+ margin: 5px;
+ padding: 10px 10px 5px 10px;
+ border: 1px solid #dbdbdb;
+ position: relative; }
+ .box.theme:hover {
+ background: #f3f3f3; }
+ .box.theme input {
+ margin-bottom: 1em; }
+
+.module-name,
+.module-name label {
+ margin-bottom: .5em;
+ color: #676e78; }
+
+.module-sshot {
+ text-align: center; }
+ .module-sshot img {
+ padding: 5px;
+ background: #f3f3f3;
+ box-shadow: 1px 1px 2px rgba(50, 50, 50, 0.1);
+ border: 3px solid #fff;
+ max-width: 100%; }
+
+.module-actions {
+ margin-top: 1em; }
+
+.bloc-toggler {
+ text-align: right; }
+ .bloc-toggler img {
+ opacity: .4; }
+ .bloc-toggler img:hover {
+ opacity: 1; }
+ .bloc-toggler a:focus img {
+ opacity: 1; }
+
+span.module-version:before {
+ content: "- "; }
+
+.toggle-bloc .mod-more {
+ display: block;
+ margin-left: 0; }
+
+.module-name input[type="checkbox"] {
+ margin-bottom: 0; }
+
+/**
+Les screenshots des thèmes ont deux tailles possibles :
+- dans Ajouter des thèmes : 240px (+ 10 padding image + 20 padding boîte + 6 bordure + 2 ombrage = 278)
+- dans Thèmes installés : 280px (+ 10 padding-image + 20 padding-boîte + 2 ombrage = 318)
+On adapte largeur et hauteur en fonction
+*/
+#theme-new .box.theme,
+#theme-update .box.theme {
+ /* Ajouter un thème */
+ width: 278px;
+ min-height: 275px; }
+
+#theme-new .module-sshot img {
+ /* Pour ceux qui n'ont pas de miniature on contraint l'image */
+ max-width: 240px;
+ max-height: 210px;
+ overflow: hidden; }
+
+#theme-deactivate .box.theme {
+ /* Thèmes désactivés */
+ width: 278px; }
+ #theme-deactivate .box.theme:hover {
+ background: transparent url(dc_logos/sq-logo-32.png) no-repeat top right; }
+
+#theme-activate .box.theme {
+ /* Thèmes installés */
+ width: 318px;
+ min-height: 304px;
+ max-width: 100%; }
+
+/* si js est là, les infos viennent par dessus le screenshot */
+.with-js #theme-new .module-infos.toggle-bloc,
+.with-js #theme-new .module-actions.toggle-bloc {
+ position: absolute;
+ left: 10px;
+ width: 239px;
+ margin: 0;
+ padding: 10px;
+ background: #f3f3f3; }
+.with-js #theme-new .module-infos.toggle-bloc {
+ top: 128px;
+ height: 80px;
+ border-top: 1px solid #ececec; }
+.with-js #theme-new .module-actions.toggle-bloc {
+ top: 208px;
+ height: 40px;
+ border-bottom: 1px solid #ececec; }
+
+.with-js .module-sshot:hover {
+ cursor: pointer; }
+
+/* mise en forme pour la boîte du thème courant */
+.box.current-theme {
+ /* Thème courant */
+ width: 646px;
+ margin: 5px;
+ padding: 20px 18px 6px;
+ background: #f3f3f3;
+ border: 1px solid #ececec;
+ border-radius: .5em;
+ min-height: 326px;
+ box-shadow: 1px 1px 2px rgba(50, 50, 50, 0.1);
+ position: relative; }
+ .box.current-theme .module-sshot:hover {
+ cursor: auto; }
+ .box.current-theme .module-sshot img {
+ float: left;
+ margin-right: 2em;
+ border: 9px solid #fff;
+ padding: 5px;
+ max-width: 308px;
+ max-height: 273px; }
+ .box.current-theme .module-name {
+ color: #d33800;
+ font-size: 1.5em;
+ margin-bottom: 1em; }
+ .box.current-theme .module-actions {
+ display: flex;
+ flex-wrap: wrap; }
+
+.current-actions {
+ width: auto;
+ overflow: hidden;
+ padding-top: 2em;
+ background: transparent url(../images/minus-theme.png) no-repeat left top; }
+
+#categories {
+ margin: 1em 0; }
+ #categories ul {
+ list-style: none;
+ margin-top: 2em;
+ padding: 0; }
+ #categories ul ul {
+ margin-right: 2em;
+ margin-left: 2em; }
+ #categories .placeholder {
+ outline: 1px dashed #2373a8;
+ min-height: 2.5em; }
+
+.cat-line {
+ position: relative;
+ margin: .66em 0;
+ padding: .66em 1em;
+ border: 1px solid #cbced1;
+ border-radius: 3px; }
+ .cat-line label {
+ margin-right: .25em; }
+ .cat-line label a {
+ font-weight: bold; }
+ .cat-line p,
+ .cat-line label {
+ margin: 0;
+ display: inline-block; }
+ .cat-line .cat-line {
+ border: 1px solid #dbdbdb; }
+
+p.cat-title {
+ margin-right: 1em; }
+
+.cat-nb-posts a {
+ color: #323232; }
+
+.cat-url {
+ padding-left: 1em; }
+
+.cat-buttons {
+ float: right;
+ margin-top: -.2em;
+ font-size: .91em; }
+ .cat-buttons select {
+ padding: 1px 2px 3px 2px;
+ margin-right: .25em; }
+ .cat-buttons .reset {
+ padding-left: 4px;
+ padding-right: 4px; }
+
+.cat-actions {
+ line-height: 2; }
+
+#del_cat {
+ width: 100%; }
+
+.media-file-mode a {
+ border-bottom: none; }
+.media-file-mode img {
+ margin-right: 1em; }
+
+span.media-file-mode {
+ margin-right: 1em; }
+
+.media-item {
+ position: relative;
+ border: 1px solid #dbdbdb;
+ margin: 9px;
+ padding: 10px 12px 6px;
+ width: 320px;
+ display: inline-block;
+ vertical-align: top;
+ min-height: 140px;
+ word-wrap: break-word; }
+ .media-item p {
+ margin: 0 0 .5em; }
+ .media-item object {
+ margin-top: .5em; }
+ .media-item ul {
+ display: block;
+ list-style: none;
+ margin: 0;
+ padding: 0; }
+ .media-item audio {
+ width: 100%;
+ margin-top: .5em; }
+ .media-item.media-private {
+ border-color: #c44d58; }
+ .media-item.media-private img.media-private {
+ padding-right: .5em; }
+
+tr.media-private img.media-private {
+ padding-right: .5em; }
+
+a.media-icon {
+ display: block;
+ border-bottom: none;
+ margin: 0 auto; }
+
+.media-icon img {
+ display: block; }
+
+a.media-flag {
+ border-bottom: none; }
+
+.media-flag img {
+ float: left;
+ margin-right: .5em; }
+
+.media-link {
+ font-size: 1.1em; }
+
+.media-action-box {
+ position: relative;
+ margin: 3em 3em 1em 1em;
+ display: inline-block;
+ vertical-align: top; }
+
+li.media-action {
+ display: block;
+ position: absolute;
+ top: 4px;
+ right: 8px;
+ height: 16px; }
+ li.media-action a {
+ border: none; }
+ li.media-action a.attach-media {
+ margin-right: 5px; }
+ li.media-action form {
+ display: inline; }
+ li.media-action input {
+ border: none; }
+
+#entry-sidebar .media-item {
+ width: 100%;
+ min-height: 0;
+ padding: 4px;
+ margin: .33em 0; }
+
+.folders-group .media-item {
+ min-height: 70px; }
+ .folders-group .media-item p {
+ margin-bottom: 0; }
+
+.media-folder {
+ background: transparent;
+ border-color: #ececec;
+ border-left-width: 8px; }
+ .media-folder .media-link {
+ font-size: 1.125em;
+ margin-left: 2em;
+ color: #676e78;
+ border-bottom: none; }
+
+tr.media-folder {
+ background: transparent; }
+ tr.media-folder .media-link {
+ margin-left: 0; }
+
+.media-folder-up {
+ border-color: transparent;
+ padding-bottom: 6px; }
+
+.medias-delete,
+.medias-select {
+ text-align: right; }
+
+.media-recent {
+ float: left;
+ margin-right: 2em; }
+
+#media-fav-dir {
+ border-bottom: none; }
+ #media-fav-dir img {
+ vertical-align: middle; }
+
+/* upload multiple */
+.enhanced_uploader .choose_files,
+.enhanced_uploader .cancel,
+.enhanced_uploader .clean,
+.enhanced_uploader .start {
+ margin-right: .4em; }
+.enhanced_uploader #upfile {
+ visibility: hidden;
+ width: 0;
+ height: 0;
+ margin: 0;
+ opacity: 0;
+ filter: alpha(opacity=0);
+ cursor: pointer; }
+.enhanced_uploader .button.choose_files {
+ display: inline-block; }
+.enhanced_uploader .max-size {
+ display: block; }
+.enhanced_uploader .one-file {
+ display: none; }
+.enhanced_uploader p.clear {
+ padding-top: 1em;
+ margin-bottom: 1em; }
+
+.button.clean,
+.button.cancel,
+.button.choose_files {
+ display: none; }
+
+label span.one-file {
+ display: inline; }
+
+#add-file-f p.clear {
+ margin-top: 1em;
+ margin-bottom: 0;
+ clear: both; }
+
+.files {
+ list-style-type: none;
+ margin-left: 0;
+ padding-left: 0;
+ border-bottom: 1px solid #dbdbdb; }
+ .files li {
+ margin-left: 0;
+ padding-left: 0; }
+
+.upload-msg {
+ font-weight: bold; }
+ .upload-msg.upload-error {
+ color: #ae323b; }
+
+.upload-files {
+ padding: 0 0.5em;
+ margin: 1em 0; }
+
+.upload-file {
+ margin: 0;
+ padding: .3em 0;
+ border-top: 1px solid #dbdbdb;
+ position: relative; }
+
+.upload-fileinfo {
+ margin-left: 0; }
+ .upload-fileinfo input {
+ position: absolute;
+ top: .5em;
+ right: .5em; }
+ .upload-fileinfo span {
+ padding-right: 8px; }
+ .upload-fileinfo .upload-filecancel {
+ display: block;
+ padding-right: 0;
+ margin-top: 3px;
+ width: 20px;
+ height: 20px;
+ background: transparent url("cancel.png") no-repeat left top;
+ text-indent: -1000px;
+ cursor: pointer;
+ float: left; }
+
+.upload-filemsg {
+ font-weight: bold;
+ color: #fff; }
+ .upload-filemsg.upload-error {
+ color: #ae323b; }
+
+.upload-progress {
+ padding: .3em 0; }
+ .upload-progress div {
+ width: 0;
+ height: 1.2em;
+ font-weight: bold;
+ line-height: 1.2em;
+ text-align: right;
+ background: #556f0f url(loader.png) repeat-x left top;
+ color: #fff;
+ border-radius: 3px; }
+
+div.template-upload {
+ clear: both; }
+
+.queue-message {
+ font-weight: bold; }
+
+#media-icon {
+ float: left; }
+
+.near-icon {
+ margin-left: 70px;
+ margin-bottom: 3em; }
+
+#media-details ul {
+ display: block;
+ margin-left: 0;
+ padding: 0; }
+#media-details li {
+ list-style: square inside;
+ margin: 0;
+ padding: 0; }
+
+#media-original-image {
+ overflow: auto; }
+ #media-original-image.overheight {
+ height: 500px; }
+
+#media-attribute {
+ margin-left: .5em;
+ padding: .5em;
+ border-left: 1px solid currentColor;
+ overflow-wrap: break-word; }
+ #media-attribute .media-title {
+ font-weight: bolder; }
+ #media-attribute .media-desc {
+ font-style: italic; }
+
+.modules td.module-actions, .modules td.module-icon {
+ vertical-align: middle; }
+.modules td.module-icon img:last-child {
+ width: 16px;
+ height: 16px; }
+.modules td.module-icon img.expand {
+ margin-bottom: 3px; }
+.modules td.module-distrib img {
+ display: block;
+ float: right; }
+
+.modules tr.expand,
+.modules td.expand {
+ background: #f3f3f3;
+ border-color: #a2cbe9; }
+
+.modules tr.expand td:first-child {
+ font-weight: bold;
+ background: #f3f3f3; }
+
+.modules td.expand {
+ padding: 0 0 1em; }
+ .modules td.expand div {
+ display: inline-block;
+ vertical-align: top;
+ margin-right: 3em; }
+
+.modules dt {
+ font-weight: bold; }
+
+.modules a.module-details {
+ background: transparent url(search.svg) no-repeat 0 center;
+ padding: 4px 4px 0 20px; }
+.modules a.module-support {
+ background: transparent url(help12.png) no-repeat 2px center;
+ padding: 4px 4px 0 20px; }
+.modules a.module-config {
+ background: transparent url(settings.png) no-repeat 2px 6px;
+ padding: 4px 4px 0 18px; }
+
+#m_search {
+ color: #323232;
+ background: transparent url(search.svg) no-repeat 0 center;
+ padding-left: 20px; }
+
+.mod-more {
+ padding-top: .5em; }
+ .mod-more,
+ .mod-more li {
+ margin: .25em 0 0 1em;
+ padding: 0;
+ list-style-type: none; }
+
+#plugin-update td {
+ vertical-align: baseline; }
+
+#entry-form {
+ display: flex;
+ flex-wrap: wrap; }
+
+#entry-wrapper {
+ flex-grow: 1;
+ width: calc(100% - 19em); }
+ @media screen and (max-width: 80em) {
+ #entry-wrapper {
+ width: 100%; } }
+
+#entry-content {
+ margin-left: 0; }
+ @media screen and (min-width: 80.01em) {
+ #entry-content {
+ padding-right: 3em; } }
+ @media screen and (max-width: 38em) {
+ #entry-content {
+ padding-right: 1em; } }
+ @media screen and (max-width: 26.5em) {
+ #entry-content {
+ padding-right: 0; } }
+
+#entry-sidebar {
+ display: flex;
+ flex-wrap: wrap;
+ flex-direction: column; }
+ @media screen and (max-width: 80em) {
+ #entry-sidebar {
+ flex-direction: row; } }
+ @media screen and (max-width: 38em) {
+ #entry-sidebar {
+ padding-right: 1em; } }
+ #entry-sidebar h4 {
+ font-size: 1.08em;
+ margin-top: .3em;
+ border-bottom: 1px solid #676e78; }
+ #entry-sidebar select {
+ width: 100%; }
+ #entry-sidebar input#post_position {
+ width: 4em; }
+
+.sb-box {
+ width: 18em;
+ margin-bottom: 1em;
+ margin-right: 1em;
+ padding: .5em 1em;
+ background-color: #f3f3f3; }
+ .sb-box:focus-within {
+ background-color: #ededed; }
+
+#tb_excerpt {
+ width: 100%; }
+
+.fav-list {
+ list-style-type: none;
+ margin-left: 0;
+ padding-left: 0; }
+ #my-favs .fav-list {
+ border-top: 1px solid #ececec; }
+ .fav-list li {
+ margin-left: 0;
+ padding-left: 0;
+ padding-top: 3px;
+ padding-bottom: 3px;
+ position: relative; }
+ #my-favs .fav-list li {
+ line-height: 2;
+ border-bottom: 1px solid #ececec;
+ padding-top: 3px;
+ padding-bottom: 3px;
+ position: relative; }
+ #my-favs:focus-within .fav-list li {
+ border-bottom-color: #dbdbdb; }
+ .fav-list li span.zoom {
+ display: none; }
+ .fav-list li:hover span.zoom {
+ display: block;
+ position: absolute;
+ bottom: 0;
+ left: 10em;
+ background-color: #f3f3f3;
+ border: 1px solid #dbdbdb;
+ padding: .2em;
+ border-radius: .5em; }
+ .fav-list img {
+ vertical-align: middle;
+ margin-right: .2em; }
+
+#my-favs {
+ border-color: #9ac123; }
+ #my-favs input.position {
+ margin: 0 0 .4em .2em; }
+
+#available-favs input,
+#available-favs label,
+#available-favs label span {
+ white-space: normal;
+ display: inline; }
+#available-favs label span.zoom {
+ display: none; }
+#available-favs li:hover label span.zoom {
+ display: block;
+ position: absolute;
+ bottom: 0;
+ left: 10em;
+ background-color: #f3f3f3;
+ border: 1px solid #dbdbdb;
+ padding: .2em;
+ border-radius: .5em; }
+
+#user-options label.ib {
+ display: inline-block;
+ width: 14em;
+ padding-right: 1em; }
+
+.blog-perm {
+ margin-top: 2em;
+ padding-top: 2em;
+ font-weight: bold; }
+
+.ul-perm {
+ list-style-type: square;
+ margin-left: 0;
+ padding-left: 3.5em;
+ margin-bottom: 0; }
+
+.add-perm {
+ padding-top: .5em;
+ padding-left: 2.5em;
+ margin-left: 0; }
+
+.guideline #content h2 {
+ color: #d33800;
+ padding: 2em 0 0 0;
+ margin: 1em 0;
+ font-size: 2em; }
+ .guideline #content h2:first-child {
+ margin-top: 0;
+ padding-top: .5em; }
+.guideline h3 {
+ margin-top: 2em; }
+.guideline .dc-update h3 {
+ margin-top: 0; }
+.guideline .one-box .box {
+ border: 1px solid #dbdbdb;
+ padding: 2px .5em; }
+.guideline #main-menu ul {
+ margin: 0;
+ padding: 0;
+ font-weight: normal; }
+.guideline #main-menu li {
+ padding-left: 1em; }
+
+/** --------------------------------------------------
+ Misc
+--------------------------------------------------- */
+/* jQuery Autocomplete plugin */
+.ac_results {
+ padding: 0px;
+ background-color: #fff;
+ border: 1px dotted #ffd478;
+ overflow: hidden;
+ z-index: 99999; }
+ .ac_results ul {
+ width: 100%;
+ list-style-position: outside;
+ list-style: none;
+ padding: 0;
+ margin: 0; }
+ .ac_results li {
+ margin: 0px;
+ padding: 2px 5px;
+ cursor: default;
+ display: block;
+ font-size: 1em;
+ line-height: 16px;
+ overflow: hidden; }
+
+.ac_loading {
+ background: transparent url("loader.gif") right center no-repeat; }
+
+.ac_odd {
+ background-color: #fff; }
+
+.ac_over {
+ color: #fff;
+ background-color: #2373a8; }
+
+/* password indicator */
+.pw-table {
+ display: table;
+ margin-bottom: 1em; }
+
+.pw-cell {
+ display: table-cell;
+ margin-bottom: 1em; }
+
+#pwindicator {
+ display: table-cell;
+ vertical-align: bottom;
+ padding-left: 1.5em;
+ height: 3.8em; }
+ #pwindicator .bar {
+ height: 6px;
+ margin-bottom: 4px; }
+
+.pw-very-weak .bar {
+ background: #c44d58;
+ width: 30px; }
+
+.pw-weak .bar {
+ background: #e1732c;
+ width: 60px; }
+
+.pw-mediocre .bar {
+ background: #ff9900;
+ width: 90px; }
+
+.pw-strong .bar {
+ background: #cdad12;
+ width: 120px; }
+
+.pw-very-strong .bar {
+ background: #9ac123;
+ width: 150px; }
+
+/* ------------------------------------------------------------------ navigation */
+/* selects accès rapide */
+.anchor-nav {
+ background: #fff;
+ padding: 4px 1em; }
+ .anchor-nav label {
+ vertical-align: bottom; }
+
+/* nav links */
+.nav_prevnext {
+ margin-bottom: 2em;
+ color: #fff; }
+
+.nav_prevnext a,
+a.back {
+ color: #2373a8;
+ border: 1px solid #dbdbdb;
+ padding: 2px 1.5em;
+ border-radius: .75em;
+ background-color: #f3f3f3; }
+
+a.back:before {
+ content: "\ab\a0"; }
+
+a.onblog_link {
+ color: #323232;
+ float: right;
+ border: 1px solid #ececec;
+ padding: 2px 1.5em;
+ border-radius: .75em;
+ background-color: #ececec;
+ box-shadow: 0 1px 1px rgba(50, 50, 50, 0.3); }
+
+/* Pagination */
+.pager {
+ margin: 2em 0 1em 0;
+ clear: left; }
+ .pager ul {
+ list-style-type: none;
+ margin: 0;
+ padding: 0; }
+ .pager li,
+ .pager input {
+ display: inline-block;
+ vertical-align: middle;
+ margin: 0 .33em 0 0;
+ padding: 0;
+ text-align: center; }
+ .pager .btn {
+ border: 1px solid #dbdbdb;
+ background-color: #f3f3f3;
+ color: #2373a8;
+ border-radius: 3px;
+ overflow: hidden; }
+ .pager .btn.no-link {
+ border-color: #dbdbdb;
+ background-color: #fff;
+ padding: 1px 3px 0; }
+ .pager .active {
+ padding: 4px 12px;
+ color: #676e78; }
+ .pager .direct-access {
+ margin-left: 2em; }
+ .pager .direct-access input[type=text] {
+ border: 1px solid #dbdbdb;
+ padding: 3px 8px;
+ margin-left: .25em;
+ background-color: #fff; }
+ .pager .direct-access input[type=submit] {
+ padding: 3px 6px; }
+ .pager a {
+ display: block;
+ padding: 1px 3px 0;
+ border: none; }
+ .pager a:hover, .pager a:focus {
+ background-color: #dbdbdb; }
+
+.index .btn.no-link,
+.index a {
+ padding: 2px 8px 3px;
+ font-variant: small-caps; }
+.index li {
+ margin-bottom: 3px; }
+.index a {
+ font-weight: bold; }
+.index .btn.no-link {
+ color: #c9c9c9; }
+.index .active {
+ padding: 4px 8px;
+ color: #fff;
+ background: #676e78;
+ border-radius: 3px;
+ font-variant: small-caps; }
+
+/* Etapes */
+.step {
+ display: inline-block;
+ float: left;
+ margin: 3px 10px 2px 0;
+ padding: 5px .5em;
+ color: #676e78;
+ background: #f3f3f3;
+ border: 1px solid #cbced1;
+ border-radius: 3px;
+ font-weight: bold; }
+
+/* ------------------------------------------------------------------------- indicateurs */
+.mark-attach {
+ display: inline-block;
+ box-sizing: border-box;
+ width: 12px;
+ height: 12px;
+ padding-left: 12px;
+ background: url("../images/attach.svg") no-repeat;
+ filter: hue-rotate(300deg) saturate(0.64); }
+
+/* ---------------------------------------------------------------- utilisables partout */
+.legible {
+ font-size: 1.16em;
+ max-width: 62em; }
+
+.fieldset {
+ background: #fff;
+ border: 1px solid #cbced1;
+ border-radius: 3px;
+ padding: 1em .7em .5em;
+ margin-bottom: 1em; }
+ .fieldset h3 {
+ margin-top: 0; }
+ .fieldset hr {
+ background-color: #cbced1;
+ border-width: 0;
+ margin: 1em 0; }
+ .fieldset:focus-within {
+ background-color: #ededed; }
+
+.right,
+.txt-right {
+ text-align: right; }
+
+.txt-center {
+ text-align: center; }
+
+.txt-left {
+ text-align: left; }
+
+.no-margin,
+label.no-margin {
+ margin-top: 0;
+ margin-bottom: 0; }
+
+.vertical-separator {
+ margin-top: 2em; }
+
+p.clear.vertical-separator {
+ padding-top: 2em; }
+
+.border-top {
+ border-top: 1px solid #868686;
+ padding-top: 1em;
+ margin-top: 1em; }
+
+.grid {
+ background: transparent repeat url("grid.png") 0 0; }
+
+ul.nice {
+ margin: 1em 0;
+ padding: 0 0 0 2em;
+ list-style: square; }
+ ul.nice li {
+ margin: 0;
+ padding: 0; }
+
+ul.from-left {
+ list-style-type: none;
+ padding-left: 0;
+ margin: 1em 0; }
+ ul.from-left > li {
+ margin-top: 1em;
+ margin-bottom: 1em; }
+ ul.from-left ul {
+ list-style-type: square; }
+
+.offline {
+ color: #676e78;
+ background: #f3f3f3; }
+
+/* caché pour tout le monde */
+.hide,
+.button.hide {
+ display: none !important; }
+
+/* Caché sauf pour les revues d'écran */
+.hidden,
+.with-js .out-of-screen-if-js {
+ position: absolute !important;
+ clip: rect(1px 1px 1px 1px);
+ /* IE6, IE7 */
+ clip: rect(1px, 1px, 1px, 1px);
+ padding: 0 !important;
+ border: 0 !important;
+ height: 1px !important;
+ width: 1px !important;
+ overflow: hidden; }
+
+/* caché si js est inactif */
+.no-js .hidden-if-no-js {
+ display: none; }
+
+/* caché si js est actif */
+.with-js .hidden-if-js {
+ display: none; }
+
+/* ---------------------------------------------- Couleurs ajoutées via javascript
+
+/* Sortable (jQuery UI) */
+.sortable-area {
+ border: 1px dashed currentColor;
+ background-color: #f3f3f3; }
+
+div.ui-sortable-handle {
+ border: 1px dashed currentColor !important;
+ border-radius: .75em !important; }
+
+/* color-picker.js */
+.color-color-picker {
+ border: 1px solid #000;
+ width: 195px;
+ background: #fff; }
+
+/* _media_item.js */
+.color-div {
+ border: 1px solid #c9c9c9; }
+
+/* Badges (common.js) */
+.badgeable {
+ /* class to set to badge parent's, not mandatory for menu items */
+ position: relative; }
+
+/* Badge design */
+.badge {
+ color: #fff;
+ background-color: #d54e21;
+ border: 1px solid transparent;
+ vertical-align: top;
+ border-radius: 1em;
+ padding: 0 .6em; }
+
+.badge-icon {
+ background-color: #607d8b; }
+
+/* Badge background override */
+.badge-std {
+ background-color: #d54e21; }
+
+.badge-info {
+ background-color: #3f51b5; }
+
+.badge-soft {
+ background-color: #607d8b; }
+
+/* Badge position */
+.badge-block {
+ /* Dashboard module → badge on top right */
+ position: absolute;
+ top: -10px;
+ right: -10px; }
+
+.badge-icon {
+ /* Dashboard icon → badge on top right */
+ right: 20px; }
+
+.badge-inline {
+ /* Menu item */
+ margin-left: .5em; }
+
+.badge-left {
+ right: auto;
+ left: -10px; }
+ .badge-left.badge-icon {
+ left: 20px; }
+
+/* Badge design option */
+.badge-noborder {
+ border: none; }
+
+.badge-small {
+ font-size: smaller; }
+ .badge-small.badge-icon {
+ right: 25px; }
+ .badge-small.badge-icon.badge-left {
+ right: auto;
+ left: 25px; }
+
+.badge-square {
+ border-radius: 0; }
+
+/* ------------------------------------------------------------------------------------
+ UN POIL DE MEDIA QUERIES
+------------------------------------------------------------------------------------ */
+@media screen and (max-width: 80em) {
+ #header,
+ h1 {
+ background: #323232; }
+
+ #top-info-user {
+ background: #676e78; }
+ #top-info-user a.active {
+ color: #fff;
+ background: #868686; }
+
+ #wrapper {
+ background: #fff; }
+
+ .modules th.module-desc,
+ .modules td.module-desc {
+ display: none; } }
+@media screen and (max-width: 44em) {
+ #help-button {
+ background-color: #a2cbe9;
+ padding: 0;
+ font-size: .83em;
+ line-height: 68px; } }
+@media screen and (max-width: 38em) {
+ h1 a:link {
+ border-right: 1px solid #c9c9c9; }
+ h1 a:hover, h1 a:focus {
+ border-right: 1px solid #c9c9c9; }
+
+ #dashboard-main {
+ padding: 0; }
+
+ #content,
+ .hide-mm #content {
+ padding: 0 .5em !important; }
+
+ #main #content > h2 {
+ padding: 6px 30px 4px .5em; }
+
+ .cell,
+ #filters-form .cell {
+ border: none;
+ padding: 1em; }
+
+ .pseudo-tabs li {
+ border-top: 1px solid #c9c9c9;
+ padding: .25em; }
+ .pseudo-tabs li:first-child, .pseudo-tabs li:nth-of-type(2) {
+ border-top: none; } }
+@media screen and (max-width: 26.5em) {
+ h1,
+ h1 a {
+ border-right: #323232 !important; }
+
+ #content.with-help #help {
+ font-size: 1.2rem; }
+
+ p.top-add {
+ text-align: center; }
+
+ .multi-part {
+ padding-left: 0; }
+
+ .part-tabs ul {
+ padding: 0 .5em; }
+
+ #icons p {
+ padding: 1em .25em; }
+
+ .box.current-theme {
+ padding: 10px; }
+
+ th,
+ td {
+ padding: 0.3em 1em 0.3em 0; }
+
+ .pseudo-tabs li {
+ border-top: 1px solid #c9c9c9 !important; }
+
+ .pseudo-tabs li:first-child {
+ border-top: none; } }
+/** --------------------------------------------------
+ Plugins
+--------------------------------------------------- */
+/* dcLegacyEditor */
+/* WYSIWYG Document */
+body.wysiwygDoc {
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; }
+
+.wysiwygDoc pre, .wysiwygDoc code, .wysiwygDoc kbd, .wysiwygDoc samp {
+ font-family: "Andale Mono", AndaleMono, Consolas, Monaco, "Courier New", monospace; }
+
+/* dcCKEditor */
+.cke textarea.cke_source {
+ font-family: "Andale Mono", AndaleMono, Consolas, Monaco, "Courier New", monospace;
+ font-size: 100%; }
+
+/** --------------------------------------------------
+ 3rd parties
+--------------------------------------------------- */
+/* Magnific Popup CSS */
+.mfp-bg {
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 1042;
+ overflow: hidden;
+ position: fixed;
+ background: #0b0b0b;
+ opacity: 0.8; }
+
+.mfp-wrap {
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 1043;
+ position: fixed;
+ outline: none !important;
+ -webkit-backface-visibility: hidden; }
+
+.mfp-container {
+ text-align: center;
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ left: 0;
+ top: 0;
+ padding: 0 8px;
+ box-sizing: border-box; }
+
+.mfp-container:before {
+ content: '';
+ display: inline-block;
+ height: 100%;
+ vertical-align: middle; }
+
+.mfp-align-top .mfp-container:before {
+ display: none; }
+
+.mfp-content {
+ position: relative;
+ display: inline-block;
+ vertical-align: middle;
+ margin: 0 auto;
+ text-align: left;
+ z-index: 1045; }
+
+.mfp-inline-holder .mfp-content,
+.mfp-ajax-holder .mfp-content {
+ width: 100%;
+ cursor: auto; }
+
+.mfp-ajax-cur {
+ cursor: progress; }
+
+.mfp-zoom-out-cur, .mfp-zoom-out-cur .mfp-image-holder .mfp-close {
+ cursor: -moz-zoom-out;
+ cursor: -webkit-zoom-out;
+ cursor: zoom-out; }
+
+.mfp-zoom {
+ cursor: pointer;
+ cursor: -webkit-zoom-in;
+ cursor: -moz-zoom-in;
+ cursor: zoom-in; }
+
+.mfp-auto-cursor .mfp-content {
+ cursor: auto; }
+
+.mfp-close,
+.mfp-arrow,
+.mfp-preloader,
+.mfp-counter {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none; }
+
+.mfp-loading.mfp-figure {
+ display: none; }
+
+.mfp-hide {
+ display: none !important; }
+
+.mfp-preloader {
+ color: #CCC;
+ position: absolute;
+ top: 50%;
+ width: auto;
+ text-align: center;
+ margin-top: -0.8em;
+ left: 8px;
+ right: 8px;
+ z-index: 1044; }
+ .mfp-preloader a {
+ color: #CCC; }
+ .mfp-preloader a:hover {
+ color: #FFF; }
+
+.mfp-s-ready .mfp-preloader {
+ display: none; }
+
+.mfp-s-error .mfp-content {
+ display: none; }
+
+button.mfp-close, button.mfp-arrow {
+ overflow: visible;
+ cursor: pointer;
+ background: transparent;
+ border: 0;
+ -webkit-appearance: none;
+ display: block;
+ outline: none;
+ padding: 0;
+ z-index: 1046;
+ box-shadow: none;
+ touch-action: manipulation; }
+button::-moz-focus-inner {
+ padding: 0;
+ border: 0; }
+
+.mfp-close {
+ width: 44px;
+ height: 44px;
+ line-height: 44px;
+ position: absolute;
+ right: 0;
+ top: 0;
+ text-decoration: none;
+ text-align: center;
+ opacity: 0.65;
+ padding: 0 0 18px 10px;
+ color: #FFF;
+ font-style: normal;
+ font-size: 28px;
+ font-family: Arial, Baskerville, monospace; }
+ .mfp-close:hover, .mfp-close:focus {
+ opacity: 1; }
+ .mfp-close:active {
+ top: 1px; }
+
+.mfp-close-btn-in .mfp-close {
+ color: #333; }
+
+.mfp-image-holder .mfp-close,
+.mfp-iframe-holder .mfp-close {
+ color: #FFF;
+ right: -6px;
+ text-align: right;
+ padding-right: 6px;
+ width: 100%; }
+
+.mfp-counter {
+ position: absolute;
+ top: 0;
+ right: 0;
+ color: #CCC;
+ font-size: 12px;
+ line-height: 18px;
+ white-space: nowrap; }
+
+.mfp-arrow {
+ position: absolute;
+ opacity: 0.65;
+ margin: 0;
+ top: 50%;
+ margin-top: -55px;
+ padding: 0;
+ width: 90px;
+ height: 110px;
+ -webkit-tap-highlight-color: transparent; }
+ .mfp-arrow:active {
+ margin-top: -54px; }
+ .mfp-arrow:hover, .mfp-arrow:focus {
+ opacity: 1; }
+ .mfp-arrow:before, .mfp-arrow:after {
+ content: '';
+ display: block;
+ width: 0;
+ height: 0;
+ position: absolute;
+ left: 0;
+ top: 0;
+ margin-top: 35px;
+ margin-left: 35px;
+ border: medium inset transparent; }
+ .mfp-arrow:after {
+ border-top-width: 13px;
+ border-bottom-width: 13px;
+ top: 8px; }
+ .mfp-arrow:before {
+ border-top-width: 21px;
+ border-bottom-width: 21px;
+ opacity: 0.7; }
+
+.mfp-arrow-left {
+ left: 0; }
+ .mfp-arrow-left:after {
+ border-right: 17px solid #FFF;
+ margin-left: 31px; }
+ .mfp-arrow-left:before {
+ margin-left: 25px;
+ border-right: 27px solid #3F3F3F; }
+
+.mfp-arrow-right {
+ right: 0; }
+ .mfp-arrow-right:after {
+ border-left: 17px solid #FFF;
+ margin-left: 39px; }
+ .mfp-arrow-right:before {
+ border-left: 27px solid #3F3F3F; }
+
+.mfp-iframe-holder {
+ padding-top: 40px;
+ padding-bottom: 40px; }
+ .mfp-iframe-holder .mfp-content {
+ line-height: 0;
+ width: 100%;
+ max-width: 98%; }
+ .mfp-iframe-holder .mfp-close {
+ top: -40px; }
+
+.mfp-iframe-scaler {
+ width: 100%;
+ height: 0;
+ overflow: hidden;
+ padding-top: 56.25%; }
+ .mfp-iframe-scaler iframe {
+ position: absolute;
+ display: block;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
+ background: #000; }
+
+/* Main image in popup */
+img.mfp-img {
+ width: auto;
+ max-width: 100%;
+ height: auto;
+ display: block;
+ line-height: 0;
+ box-sizing: border-box;
+ padding: 40px 0 40px;
+ margin: 0 auto; }
+
+/* The shadow behind the image */
+.mfp-figure {
+ line-height: 0; }
+ .mfp-figure:after {
+ content: '';
+ position: absolute;
+ left: 0;
+ top: 40px;
+ bottom: 40px;
+ display: block;
+ right: 0;
+ width: auto;
+ height: auto;
+ z-index: -1;
+ box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
+ background: #444; }
+ .mfp-figure small {
+ color: #BDBDBD;
+ display: block;
+ font-size: 12px;
+ line-height: 14px; }
+ .mfp-figure figure {
+ margin: 0; }
+
+.mfp-bottom-bar {
+ margin-top: -36px;
+ position: absolute;
+ top: 100%;
+ left: 0;
+ width: 100%;
+ cursor: auto; }
+
+.mfp-title {
+ text-align: left;
+ line-height: 18px;
+ color: #F3F3F3;
+ word-wrap: break-word;
+ padding-right: 36px; }
+
+.mfp-image-holder .mfp-content {
+ max-width: 100%; }
+
+.mfp-gallery .mfp-image-holder .mfp-figure {
+ cursor: pointer; }
+
+@media screen and (max-width: 800px) and (orientation: landscape), screen and (max-height: 300px) {
+ /**
+ * Remove all paddings around the image on small screen
+ */
+ .mfp-img-mobile .mfp-image-holder {
+ padding-left: 0;
+ padding-right: 0; }
+ .mfp-img-mobile img.mfp-img {
+ padding: 0; }
+ .mfp-img-mobile .mfp-figure:after {
+ top: 0;
+ bottom: 0; }
+ .mfp-img-mobile .mfp-figure small {
+ display: inline;
+ margin-left: 5px; }
+ .mfp-img-mobile .mfp-bottom-bar {
+ background: rgba(0, 0, 0, 0.6);
+ bottom: 0;
+ margin: 0;
+ top: auto;
+ padding: 3px 5px;
+ position: fixed;
+ box-sizing: border-box; }
+ .mfp-img-mobile .mfp-bottom-bar:empty {
+ padding: 0; }
+ .mfp-img-mobile .mfp-counter {
+ right: 5px;
+ top: 3px; }
+ .mfp-img-mobile .mfp-close {
+ top: 0;
+ right: 0;
+ width: 35px;
+ height: 35px;
+ line-height: 35px;
+ background: rgba(0, 0, 0, 0.6);
+ position: fixed;
+ text-align: center;
+ padding: 0; } }
+@media all and (max-width: 900px) {
+ .mfp-arrow {
+ -webkit-transform: scale(0.75);
+ transform: scale(0.75); }
+
+ .mfp-arrow-left {
+ -webkit-transform-origin: 0;
+ transform-origin: 0; }
+
+ .mfp-arrow-right {
+ -webkit-transform-origin: 100%;
+ transform-origin: 100%; }
+
+ .mfp-container {
+ padding-left: 6px;
+ padding-right: 6px; } }
+/* CodeMirror CSS */
+.CodeMirror {
+ /* Set height, width, borders, and global font properties here */
+ font-family: "Andale Mono", AndaleMono, Consolas, Monaco, "Courier New", monospace !important; }
+
+/** --------------------------------------------------
+ Debug
+--------------------------------------------------- */
+/* debug */
+#debug {
+ position: absolute;
+ top: 0;
+ width: 100%;
+ height: 4px;
+ background: #ffd478; }
+ #debug div {
+ display: none;
+ padding: 3px 0.5em 2px; }
+ #debug p {
+ margin: 0.5em 0; }
+ #debug:hover {
+ height: auto;
+ padding: 2px 1em;
+ z-index: 100; }
+ #debug:hover div {
+ display: block; }
+
+.debug {
+ color: #ae323b;
+ background: #ffd478;
+ padding: 3px 0.5em 2px; }
+
+input[type=submit].delete.debug,
+a.delete.debug {
+ border-color: #ffd478; }
+ input[type=submit].delete.debug:hover,
+ a.delete.debug:hover {
+ background: #ffd478;
+ color: #ae323b;
+ border-color: #ffd478; }
diff --git a/dotclear._no/admin/style/dotclear-logo.png b/dotclear._no/admin/style/dotclear-logo.png
new file mode 100644
index 0000000..e39a3ea
Binary files /dev/null and b/dotclear._no/admin/style/dotclear-logo.png differ
diff --git a/dotclear._no/admin/style/dotclear-logo2.png b/dotclear._no/admin/style/dotclear-logo2.png
new file mode 100755
index 0000000..7295297
Binary files /dev/null and b/dotclear._no/admin/style/dotclear-logo2.png differ
diff --git a/dotclear._no/admin/style/drag.png b/dotclear._no/admin/style/drag.png
new file mode 100644
index 0000000..05e001d
Binary files /dev/null and b/dotclear._no/admin/style/drag.png differ
diff --git a/dotclear._no/admin/style/grid.png b/dotclear._no/admin/style/grid.png
new file mode 100644
index 0000000..a62f8a7
Binary files /dev/null and b/dotclear._no/admin/style/grid.png differ
diff --git a/dotclear._no/admin/style/head-bg.png b/dotclear._no/admin/style/head-bg.png
new file mode 100644
index 0000000..d3f91a3
Binary files /dev/null and b/dotclear._no/admin/style/head-bg.png differ
diff --git a/dotclear._no/admin/style/help-mini.png b/dotclear._no/admin/style/help-mini.png
new file mode 100644
index 0000000..f824e2b
Binary files /dev/null and b/dotclear._no/admin/style/help-mini.png differ
diff --git a/dotclear._no/admin/style/help12.png b/dotclear._no/admin/style/help12.png
new file mode 100644
index 0000000..7add383
Binary files /dev/null and b/dotclear._no/admin/style/help12.png differ
diff --git a/dotclear._no/admin/style/info.png b/dotclear._no/admin/style/info.png
new file mode 100755
index 0000000..8851b99
Binary files /dev/null and b/dotclear._no/admin/style/info.png differ
diff --git a/dotclear._no/admin/style/install.css b/dotclear._no/admin/style/install.css
new file mode 100644
index 0000000..0aba7a3
--- /dev/null
+++ b/dotclear._no/admin/style/install.css
@@ -0,0 +1,192 @@
+html {
+ font-size: 62.5%;
+}
+body.install {
+ font: 1.2rem/1.5 Arial,Helvetica,sans-serif;
+ color: #333;
+ background: #fff;
+ margin: 0;
+ padding: 0;
+}
+body.install #content {
+ width:32em;
+ margin:1em auto;
+}
+body.install #main {
+ padding:1em 2em;
+ border:1px #ccc solid;
+ border-radius: 8px;
+}
+body.install h1,h2 {
+ font-family: arial,Helvetica,sans-serif;
+}
+body.install h1 {
+ background: url(install/w-logo.png) no-repeat top center;
+ font-size:1.8em;
+ font-weight:normal;
+ text-align:center;
+ color: #666;
+ padding-top: 70px;
+ margin: 0 0 0.75em 0;
+}
+body.install h2 {
+ color: #f90;
+}
+body.install h3 {
+ margin-top:0;
+}
+body.install label {
+ color: #555;
+}
+
+body.install .msg {
+ padding:10px 10px 10px 60px;
+ border-radius:8px;
+}
+body.install .warning {
+ background: #ffc url(install/important.png) no-repeat 10px 10px;
+}
+body.install .notice {
+ background: #eef url(install/note.png) no-repeat 10px 10px;
+}
+a, a:link, a:visited {
+ color: #2373A8;
+ text-decoration: none;
+ border-bottom: 1px dotted #f90;
+}
+a:hover, a:active, a:focus {
+ text-decoration: underline;
+}
+
+div.error, p.error, div.message, p.message, div.static-msg, p.static-msg {
+ padding: 0.5em 0.5em 0.5em 60px;
+ margin-bottom: 1em;
+ border-radius: 8px;
+}
+p.error, p.message, p.static-msg {
+ padding-top: 1em;
+ padding-bottom: 1em;
+}
+div.error, p.error {
+ background: #e5bfbf url(install/process_warning.png) no-repeat 5px 5px;
+ color: #600;
+}
+div.message, p.message, div.static-msg, p.static-msg {
+ background: #666 url(install/note.png) no-repeat 5px 5px;
+ color: #fff;
+}
+div.message a, p.message a, div.static-msg a, p.static-msg a {
+ color: #fff;
+}
+label {
+ display : block;
+}
+label input, label select, label span {
+ display : block;
+}
+
+label.required {
+ font-weight : bold;
+}
+label.required abbr {
+ color: #900;
+ font-size: 1.3em;
+}
+
+form {
+ display : block;
+ margin : 0;
+ padding : 0;
+}
+
+fieldset {
+ display : block;
+ margin : 0 0 1em 0;
+ padding : 1em 0.5em;
+ border-width : 1px 0;
+ border-style: solid;
+ border-color: #ccc;
+ background: #f5f5f5;
+}
+legend {
+ font-weight : bold;
+ padding: 0.2em 0.6em;
+ border-width: 1px;
+ border-style: solid;
+ border-color: #ccc;
+ background: #f5f5f5;
+ margin-bottom: 0.5em;
+}
+a#obfus {
+ color: #fff;
+ background-color: #666;
+ padding: 0 10px;
+ border-radius: 2px;
+}
+
+input[type=submit]{
+ font-size: 1.05em;
+ display: inline-block;
+ outline: none;
+ cursor: pointer;
+ text-align: center;
+ text-decoration: none;
+ padding: .1em .5em;
+ text-shadow: 0 1px 1px rgba(0,0,0,.3);
+ border-radius: .2em;
+ margin-bottom: .1em;
+ color: #fff;
+ border: 1px solid #2373A8;
+ background: #2373A8;
+ background: -webkit-gradient(linear, left top, left bottom, from(#2C8FD1), to(#2373A8));
+ background: -moz-linear-gradient(top, #2C8FD1, #2373A8);
+ width: 100%;
+}
+input[type=submit]:hover,
+input[type=submit]:focus {
+ background: #2373A8;
+ background: -webkit-gradient(linear, left top, left bottom, from(#2373A8), to(#2C8FD1));
+ background: -moz-linear-gradient(top, #2373A8, #2C8FD1);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#2373A8', endColorstr='#2C8FD1');
+ border: 1px solid #2C8FD1;
+}
+
+
+/* --------------------------------------------------------------- password indcator */
+.pw-table {
+ display: block;
+ margin-bottom: .25em;
+}
+.pw-cell {
+ display: block;
+ margin-bottom: .25em;
+}
+#pwindicator {
+ display: block;
+ vertical-align: bottom;
+ padding-left: 1.5em;
+}
+#pwindicator .bar {
+ height: 6px;
+ margin-bottom: 4px;
+}
+.pw-very-weak .bar {
+ background: #900;
+ width: 30px;
+}
+.pw-weak .bar {
+ background: #c00;
+ width: 60px;
+}
+.pw-mediocre .bar {
+ background: #f90;
+ width: 90px;
+}
+.pw-strong .bar {
+ background: #080;
+ width: 120px;
+}
+.pw-very-strong .bar {
+ background: #0c0;
+ width: 150px;
+}
diff --git a/dotclear._no/admin/style/install/important.png b/dotclear._no/admin/style/install/important.png
new file mode 100644
index 0000000..9f3cb4b
Binary files /dev/null and b/dotclear._no/admin/style/install/important.png differ
diff --git a/dotclear._no/admin/style/install/note.png b/dotclear._no/admin/style/install/note.png
new file mode 100644
index 0000000..a36ffc0
Binary files /dev/null and b/dotclear._no/admin/style/install/note.png differ
diff --git a/dotclear._no/admin/style/install/process_warning.png b/dotclear._no/admin/style/install/process_warning.png
new file mode 100644
index 0000000..7cbbec9
Binary files /dev/null and b/dotclear._no/admin/style/install/process_warning.png differ
diff --git a/dotclear._no/admin/style/install/w-logo.png b/dotclear._no/admin/style/install/w-logo.png
new file mode 100644
index 0000000..5bc8cee
Binary files /dev/null and b/dotclear._no/admin/style/install/w-logo.png differ
diff --git a/dotclear._no/admin/style/jsToolBar/bt_bquote.png b/dotclear._no/admin/style/jsToolBar/bt_bquote.png
new file mode 100755
index 0000000..5488446
Binary files /dev/null and b/dotclear._no/admin/style/jsToolBar/bt_bquote.png differ
diff --git a/dotclear._no/admin/style/jsToolBar/bt_br.png b/dotclear._no/admin/style/jsToolBar/bt_br.png
new file mode 100644
index 0000000..717ff6c
Binary files /dev/null and b/dotclear._no/admin/style/jsToolBar/bt_br.png differ
diff --git a/dotclear._no/admin/style/jsToolBar/bt_clean.png b/dotclear._no/admin/style/jsToolBar/bt_clean.png
new file mode 100755
index 0000000..0a2dc4c
Binary files /dev/null and b/dotclear._no/admin/style/jsToolBar/bt_clean.png differ
diff --git a/dotclear._no/admin/style/jsToolBar/bt_code.png b/dotclear._no/admin/style/jsToolBar/bt_code.png
new file mode 100644
index 0000000..40a149f
Binary files /dev/null and b/dotclear._no/admin/style/jsToolBar/bt_code.png differ
diff --git a/dotclear._no/admin/style/jsToolBar/bt_del.png b/dotclear._no/admin/style/jsToolBar/bt_del.png
new file mode 100644
index 0000000..87c3c66
Binary files /dev/null and b/dotclear._no/admin/style/jsToolBar/bt_del.png differ
diff --git a/dotclear._no/admin/style/jsToolBar/bt_em.png b/dotclear._no/admin/style/jsToolBar/bt_em.png
new file mode 100644
index 0000000..cddfe87
Binary files /dev/null and b/dotclear._no/admin/style/jsToolBar/bt_em.png differ
diff --git a/dotclear._no/admin/style/jsToolBar/bt_img.png b/dotclear._no/admin/style/jsToolBar/bt_img.png
new file mode 100755
index 0000000..10d34b1
Binary files /dev/null and b/dotclear._no/admin/style/jsToolBar/bt_img.png differ
diff --git a/dotclear._no/admin/style/jsToolBar/bt_img_select.png b/dotclear._no/admin/style/jsToolBar/bt_img_select.png
new file mode 100755
index 0000000..ff89988
Binary files /dev/null and b/dotclear._no/admin/style/jsToolBar/bt_img_select.png differ
diff --git a/dotclear._no/admin/style/jsToolBar/bt_ins.png b/dotclear._no/admin/style/jsToolBar/bt_ins.png
new file mode 100644
index 0000000..f690b90
Binary files /dev/null and b/dotclear._no/admin/style/jsToolBar/bt_ins.png differ
diff --git a/dotclear._no/admin/style/jsToolBar/bt_link.png b/dotclear._no/admin/style/jsToolBar/bt_link.png
new file mode 100755
index 0000000..7de22d1
Binary files /dev/null and b/dotclear._no/admin/style/jsToolBar/bt_link.png differ
diff --git a/dotclear._no/admin/style/jsToolBar/bt_ol.png b/dotclear._no/admin/style/jsToolBar/bt_ol.png
new file mode 100755
index 0000000..3cac215
Binary files /dev/null and b/dotclear._no/admin/style/jsToolBar/bt_ol.png differ
diff --git a/dotclear._no/admin/style/jsToolBar/bt_paragraph.png b/dotclear._no/admin/style/jsToolBar/bt_paragraph.png
new file mode 100644
index 0000000..5de086e
Binary files /dev/null and b/dotclear._no/admin/style/jsToolBar/bt_paragraph.png differ
diff --git a/dotclear._no/admin/style/jsToolBar/bt_post.png b/dotclear._no/admin/style/jsToolBar/bt_post.png
new file mode 100755
index 0000000..c1c0ab6
Binary files /dev/null and b/dotclear._no/admin/style/jsToolBar/bt_post.png differ
diff --git a/dotclear._no/admin/style/jsToolBar/bt_pre.png b/dotclear._no/admin/style/jsToolBar/bt_pre.png
new file mode 100644
index 0000000..b765759
Binary files /dev/null and b/dotclear._no/admin/style/jsToolBar/bt_pre.png differ
diff --git a/dotclear._no/admin/style/jsToolBar/bt_quote.png b/dotclear._no/admin/style/jsToolBar/bt_quote.png
new file mode 100644
index 0000000..6cd98e1
Binary files /dev/null and b/dotclear._no/admin/style/jsToolBar/bt_quote.png differ
diff --git a/dotclear._no/admin/style/jsToolBar/bt_strong.png b/dotclear._no/admin/style/jsToolBar/bt_strong.png
new file mode 100644
index 0000000..d2ac922
Binary files /dev/null and b/dotclear._no/admin/style/jsToolBar/bt_strong.png differ
diff --git a/dotclear._no/admin/style/jsToolBar/bt_ul.png b/dotclear._no/admin/style/jsToolBar/bt_ul.png
new file mode 100755
index 0000000..0e04bcb
Binary files /dev/null and b/dotclear._no/admin/style/jsToolBar/bt_ul.png differ
diff --git a/dotclear._no/admin/style/jsToolBar/jsToolBar.css b/dotclear._no/admin/style/jsToolBar/jsToolBar.css
new file mode 100755
index 0000000..fbe1589
--- /dev/null
+++ b/dotclear._no/admin/style/jsToolBar/jsToolBar.css
@@ -0,0 +1,184 @@
+.jstEditor {
+ border-width : 0 1px 1px 1px;
+ border-style : solid;
+ border-color : #ddd;
+ background : #f7f7f7;
+}
+.jstEditor textarea, .jstEditor iframe {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ margin: 0;
+ border: 1px solid #fbfbfb;
+ width: 100%;
+ padding: .5em 0 0 0;
+ min-height: 100px;
+}
+.jstHandle {
+ height: 8px;
+ background: #ccc url(resize.png) no-repeat center center;
+ font-size: 0.1em;
+ cursor: s-resize;
+ border-color: #ccc #ccc #ccc #000;
+ border-width: 0 1px 1px 1px;
+ border-style: solid;
+}
+.jstEditor textarea:focus, .jstEditor iframe:focus {
+ border-color: #bee74b;
+}
+.jstElements {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ padding: 0 32px 0 1rem;
+ border: 1px solid #dfdfdf;
+ overflow: hidden;
+ background: #f5f5f5;
+ border-radius: 4px 4px 0 0;
+ position: relative;
+ line-height: 30px;
+}
+.jstElements select, .jstElements button {
+ vertical-align: middle;
+}
+.jstElements select {
+ width: 12em;
+ margin-right: .6rem;
+ border: 1px solid #ddd;
+}
+.jstElements button {
+ width: 30px;
+ height: 28px;
+ border-style: solid;
+ border-width: 1px;
+ margin-left: 2px;
+ margin-bottom: 2px;
+ border-color: #e5e5e5 #ccc #ccc #e5e5e5;
+ background-color : #f7f7f7;
+ background-position : 50% 50%;
+ background-repeat: no-repeat;
+}
+.jstElements button:hover, .jstElements button:focus {
+ border-color : #ddd;
+ background-color: #fff;
+}
+.jstElements button span {
+ display: none;
+}
+.jstElements span {
+ display : inline-block;
+ vertical-align: top;
+}
+span.jstSpacer {
+ width : 8px;
+}
+.jstSwitcher {
+ display: block;
+ list-style: none;
+ margin: 0;
+ padding: 0 0 5px 0;
+ background: #dfdfdf;
+ border-top: 1px solid #999;
+}
+.jstSwitcher li {
+ display: inline;
+ margin: 0 0 0 5px;
+ padding: 2px 8px;
+}
+.jstSwitcher li.jstSwitcherCurrent {
+ color: #fff;
+ background: #677374;
+ border-radius: 0 0 2px 2px;
+}
+.jstSwitcher a {
+ font-weight: normal;
+ border-bottom: none !important;
+}
+
+/* Buttons
+-------------------------------------------------------- */
+button.jstb_strong {
+ background-image: url(bt_strong.png);
+}
+button.jstb_em {
+ background-image: url(bt_em.png);
+}
+button.jstb_ins {
+ background-image: url(bt_ins.png);
+}
+button.jstb_del {
+ background-image: url(bt_del.png);
+}
+button.jstb_quote {
+ background-image: url(bt_quote.png);
+}
+button.jstb_code {
+ background-image: url(bt_code.png);
+}
+button.jstb_paragraph {
+ background-image: url(bt_paragraph.png);
+}
+button.jstb_br {
+ background-image: url(bt_br.png);
+}
+button.jstb_blockquote {
+ background-image: url(bt_bquote.png);
+}
+button.jstb_pre {
+ background-image: url(bt_pre.png);
+}
+button.jstb_ul {
+ background-image: url(bt_ul.png);
+}
+button.jstb_ol {
+ background-image: url(bt_ol.png);
+}
+button.jstb_link {
+ background-image: url(bt_link.png);
+}
+button.jstb_img {
+ background-image: url(bt_img.png);
+}
+button.jstb_img_select {
+ background-image: url(bt_img_select.png);
+}
+button.jstb_post_link {
+ background-image: url(bt_post.png);
+}
+button.jstb_removeFormat {
+ background-color: #e5e5e5;
+ background-image: url(bt_clean.png);
+ position: absolute;
+ right: 2px;
+ top: 1px;
+ border-color: #ccc;
+}
+
+/* WYSIWYG Iframe */
+.wysiwygIframe {
+ border-width : 1px;
+ border-style : solid;
+ border-color : #000 #ccc #ccc #000;
+ width : 100%;
+}
+
+/* WYSIWYG Document */
+body.wysiwygDoc {
+ font: 12px "DejaVu Sans","Lucida Grande","Lucida Sans Unicode",Arial,sans-serif;
+ color : #000;
+ background: #f9f9f9;
+ margin: 0;
+ padding : 2px;
+ border: none;
+}
+.wysiwygDoc pre, .wysiwygDoc code, .wysiwygDoc kbd, .wysiwygDoc samp {
+ font-family:"Courier New",Courier,monospace;
+ font-size : 1.1em;
+}
+.wysiwygDoc code {
+ color : #666;
+ font-weight : bold;
+}
+body.wysiwygDoc > p:first-child {
+ margin-top: 0;
+}
diff --git a/dotclear._no/admin/style/jsToolBar/resize.png b/dotclear._no/admin/style/jsToolBar/resize.png
new file mode 100644
index 0000000..1b21270
Binary files /dev/null and b/dotclear._no/admin/style/jsToolBar/resize.png differ
diff --git a/dotclear._no/admin/style/loader.gif b/dotclear._no/admin/style/loader.gif
new file mode 100644
index 0000000..c3123ea
Binary files /dev/null and b/dotclear._no/admin/style/loader.gif differ
diff --git a/dotclear._no/admin/style/loader.png b/dotclear._no/admin/style/loader.png
new file mode 100644
index 0000000..0e84846
Binary files /dev/null and b/dotclear._no/admin/style/loader.png differ
diff --git a/dotclear._no/admin/style/magnifier.png b/dotclear._no/admin/style/magnifier.png
new file mode 100644
index 0000000..1578a8d
Binary files /dev/null and b/dotclear._no/admin/style/magnifier.png differ
diff --git a/dotclear._no/admin/style/msg-error.png b/dotclear._no/admin/style/msg-error.png
new file mode 100644
index 0000000..7cbbec9
Binary files /dev/null and b/dotclear._no/admin/style/msg-error.png differ
diff --git a/dotclear._no/admin/style/msg-info.png b/dotclear._no/admin/style/msg-info.png
new file mode 100644
index 0000000..a7933de
Binary files /dev/null and b/dotclear._no/admin/style/msg-info.png differ
diff --git a/dotclear._no/admin/style/msg-std.png b/dotclear._no/admin/style/msg-std.png
new file mode 100644
index 0000000..a36ffc0
Binary files /dev/null and b/dotclear._no/admin/style/msg-std.png differ
diff --git a/dotclear._no/admin/style/msg-success.png b/dotclear._no/admin/style/msg-success.png
new file mode 100644
index 0000000..e847d10
Binary files /dev/null and b/dotclear._no/admin/style/msg-success.png differ
diff --git a/dotclear._no/admin/style/msg-warning.png b/dotclear._no/admin/style/msg-warning.png
new file mode 100644
index 0000000..9f3cb4b
Binary files /dev/null and b/dotclear._no/admin/style/msg-warning.png differ
diff --git a/dotclear._no/admin/style/package.png b/dotclear._no/admin/style/package.png
new file mode 100755
index 0000000..f138df4
Binary files /dev/null and b/dotclear._no/admin/style/package.png differ
diff --git a/dotclear._no/admin/style/page-bg.png b/dotclear._no/admin/style/page-bg.png
new file mode 100644
index 0000000..a048e81
Binary files /dev/null and b/dotclear._no/admin/style/page-bg.png differ
diff --git a/dotclear._no/admin/style/scss/default-dark.scss b/dotclear._no/admin/style/scss/default-dark.scss
new file mode 100644
index 0000000..b1dffd8
--- /dev/null
+++ b/dotclear._no/admin/style/scss/default-dark.scss
@@ -0,0 +1,84 @@
+@charset "UTF-8";
+
+/** --------------------------------------------------
+ Start
+--------------------------------------------------- */
+@import "init/mixins-functions"; // mixins, fonctions
+@import "init/rebase"; // init html tags
+@import "init/config"; // variables
+
+/** --------------------------------------------------
+ Colors
+--------------------------------------------------- */
+@import "themes/dark";
+
+/** --------------------------------------------------
+ Common rules
+--------------------------------------------------- */
+@import "partials/common"; // headings, links
+
+/** --------------------------------------------------
+ Layout
+--------------------------------------------------- */
+@import "partials/layout";
+
+/** --------------------------------------------------
+ Elements
+--------------------------------------------------- */
+@import "partials/markup";
+@import "partials/forms";
+@import "partials/buttons";
+@import "partials/messages";
+
+/** --------------------------------------------------
+ Components
+--------------------------------------------------- */
+@import "partials/header";
+@import "partials/main-menu";
+@import "partials/content";
+@import "partials/footer";
+
+/** --------------------------------------------------
+ Tables and Filters
+--------------------------------------------------- */
+@import "partials/tables";
+@import "partials/filters";
+
+/** --------------------------------------------------
+ Pages
+--------------------------------------------------- */
+@import "partials/auth";
+@import "partials/index";
+@import "partials/blog_pref";
+@import "partials/blog_theme";
+@import "partials/categories";
+@import "partials/media";
+@import "partials/media_item";
+@import "partials/plugins";
+@import "partials/entry";
+@import "partials/preferences";
+@import "partials/user";
+@import "partials/charte";
+
+/** --------------------------------------------------
+ Misc
+--------------------------------------------------- */
+@import "partials/classes";
+@import "partials/utils";
+@import "partials/mediaqueries";
+
+/** --------------------------------------------------
+ Plugins
+--------------------------------------------------- */
+@import "partials/editors";
+
+/** --------------------------------------------------
+ 3rd parties
+--------------------------------------------------- */
+@import "vendor/magnific-popup";
+@import "vendor/codemirror";
+
+/** --------------------------------------------------
+ Debug
+--------------------------------------------------- */
+@import "partials/debug";
diff --git a/dotclear._no/admin/style/scss/default.scss b/dotclear._no/admin/style/scss/default.scss
new file mode 100644
index 0000000..4a6e907
--- /dev/null
+++ b/dotclear._no/admin/style/scss/default.scss
@@ -0,0 +1,84 @@
+@charset "UTF-8";
+
+/** --------------------------------------------------
+ Start
+--------------------------------------------------- */
+@import "init/mixins-functions"; // mixins, fonctions
+@import "init/rebase"; // init html tags
+@import "init/config"; // variables
+
+/** --------------------------------------------------
+ Colors
+--------------------------------------------------- */
+@import "themes/light";
+
+/** --------------------------------------------------
+ Common rules
+--------------------------------------------------- */
+@import "partials/common"; // headings, links
+
+/** --------------------------------------------------
+ Layout
+--------------------------------------------------- */
+@import "partials/layout";
+
+/** --------------------------------------------------
+ Elements
+--------------------------------------------------- */
+@import "partials/markup";
+@import "partials/forms";
+@import "partials/buttons";
+@import "partials/messages";
+
+/** --------------------------------------------------
+ Components
+--------------------------------------------------- */
+@import "partials/header";
+@import "partials/main-menu";
+@import "partials/content";
+@import "partials/footer";
+
+/** --------------------------------------------------
+ Tables and Filters
+--------------------------------------------------- */
+@import "partials/tables";
+@import "partials/filters";
+
+/** --------------------------------------------------
+ Pages
+--------------------------------------------------- */
+@import "partials/auth";
+@import "partials/index";
+@import "partials/blog_pref";
+@import "partials/blog_theme";
+@import "partials/categories";
+@import "partials/media";
+@import "partials/media_item";
+@import "partials/plugins";
+@import "partials/entry";
+@import "partials/preferences";
+@import "partials/user";
+@import "partials/charte";
+
+/** --------------------------------------------------
+ Misc
+--------------------------------------------------- */
+@import "partials/classes";
+@import "partials/utils";
+@import "partials/mediaqueries";
+
+/** --------------------------------------------------
+ Plugins
+--------------------------------------------------- */
+@import "partials/editors";
+
+/** --------------------------------------------------
+ 3rd parties
+--------------------------------------------------- */
+@import "vendor/magnific-popup";
+@import "vendor/codemirror";
+
+/** --------------------------------------------------
+ Debug
+--------------------------------------------------- */
+@import "partials/debug";
diff --git a/dotclear._no/admin/style/scss/init/_config.scss b/dotclear._no/admin/style/scss/init/_config.scss
new file mode 100644
index 0000000..eedeeec
--- /dev/null
+++ b/dotclear._no/admin/style/scss/init/_config.scss
@@ -0,0 +1,55 @@
+// ----------------- LIBRAIRIES ------------------- //
+
+// ----------------- Compass config --------------- //
+// Do not support IE less than IE11
+$browser-minimum-versions: (
+ "ie": "11"
+);
+$graceful-usage-threshold: 100;
+$critical-usage-threshold: 100;
+@import "compass"; // gem
+
+// ========================================================================== //
+// =Typographie
+// ========================================================================== //
+
+$sans-serif: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
+
+$sans-serif-input: inherit; // Use OS default font for input fields
+
+$monospace: "Andale Mono",AndaleMono,Consolas,Monaco,"Courier New",monospace;
+
+$html-font-size: 62.5%; // Basically 10px (16px ⁄x 0.625) — Must be in %
+
+$body-font-size-small: 1.2rem; // For screen smaller than $small-screen (see below)
+$body-font-size: 1.4rem;
+$body-font-size-large: 1.6rem; // For screen larger or equal than $large-screen (see below)
+
+// Divers
+
+$css-img-path: 'img'; // utile pour le mixin de fallback svg
+
+// ========================================================================== //
+// =Breakpoints
+// ========================================================================== //
+
+$xxs-screen: 26.5em; // 424 px
+$xs-screen: 38em; // 608 px
+$s-screen: 44em; // 704 px
+$m-screen: 48em; // 768 px
+$l-screen: 61em; // 976 px
+$xl-screen: 80em; // 1280 px
+$xxl-screen: 120em; // 1920 px
+
+// =========================================================================== //
+// =Various dimensions
+// =========================================================================== //
+
+// screens sizes
+
+$small-screen: $xxs-screen;
+$large-screen: $xxl-screen;
+
+// collapser width
+
+$collapser-width: 10px;
diff --git a/dotclear._no/admin/style/scss/init/_mixins-functions.scss b/dotclear._no/admin/style/scss/init/_mixins-functions.scss
new file mode 100644
index 0000000..40d6679
--- /dev/null
+++ b/dotclear._no/admin/style/scss/init/_mixins-functions.scss
@@ -0,0 +1,58 @@
+// mixin svg fallback
+@mixin svg(
+ $file-name,
+ $css-img-path: $css-img-path)
+{
+ background-image: inline-image($file-name+'.png');
+ background-image: inline-image($file-name+'.svg'), none;
+}
+
+@mixin bg-with-svg(
+ $file-name,
+ $css-img-path: $css-img-path,
+ $repeat: no-repeat,
+ $position: 50% 50%,
+ $bg-color: transparent)
+{
+ background: inline-image($file-name+'.png') $repeat, $position, $bg-color;
+ background-image: inline-image($file-name+'.svg'), none;
+}
+
+// Mix from Nico3333 (https://github.com/nico3333fr/ROCSSTI/blob/master/src/css/rocssti-fr.css#L637)
+// and ffood (http://www.ffoodd.fr/cache-cache-css/)
+@mixin visually-hidden {
+ border: 0;
+ clip: rect(0 0 0 0);
+ clip-path: inset(50%);
+ height: 1px;
+ margin: -1px;
+ overflow: hidden;
+ padding: 0;
+ position: absolute;
+ width: 1px;
+ white-space: nowrap;
+}
+@mixin visually-hidden-focus {
+ clip: auto;
+ clip-path: none;
+ height: auto;
+ overflow: visible;
+ position: static;
+ width: auto;
+ white-space: normal;
+}
+
+// Remove any unit from a value
+@function strip-unit(
+ $value)
+{
+ @return $value / ($value * 0 + 1);
+}
+
+// Compute absolute value (in em or rem) depending on font-size of the html element (given in %)
+@function relative-to-screen(
+ $value, // em or rem value
+ $html-font-size) // in %
+{
+ @return $value / (strip-unit($html-font-size) / 100);
+}
diff --git a/dotclear._no/admin/style/scss/init/_rebase.scss b/dotclear._no/admin/style/scss/init/_rebase.scss
new file mode 100644
index 0000000..626b355
--- /dev/null
+++ b/dotclear._no/admin/style/scss/init/_rebase.scss
@@ -0,0 +1,261 @@
+/* largeur des paddings et border compris dans "width" */
+
+*,
+*:before,
+*:after {
+ box-sizing: border-box;
+}
+
+html {
+ font-size: 100%;
+ -ms-text-size-adjust: 100%;
+ -webkit-text-size-adjust: 100%;
+ margin: 0;
+ padding: 0;
+}
+
+body {
+ margin: 0;
+ padding: 0;
+}
+
+a {
+ background: transparent;
+ &:focus {
+ outline: thin dotted;
+ }
+ &:active,
+ &:hover {
+ outline: none;
+ }
+ img {
+ border: none;
+ }
+}
+
+q,
+cite {
+ font-style: italic;
+}
+
+q:before,
+q:after {
+ content: "";
+}
+
+sup,
+sub {
+ font-size: .75em;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline;
+}
+
+sup {
+ top: -0.5em;
+}
+
+sub {
+ bottom: -0.25em;
+}
+
+abbr[title] {
+ border-bottom: 1px dotted;
+ cursor: help;
+}
+
+b,
+strong {
+ font-weight: bold;
+ font-size: 0.9375em;
+}
+
+small {
+ font-size: 80%;
+}
+
+dfn {
+ font-style: italic;
+}
+
+hr {
+ box-sizing: content-box;
+ height: 0;
+}
+
+mark {
+ background: #ff0;
+ color: #000;
+}
+
+code,
+kbd,
+pre,
+samp {
+ font-family: monospace, serif;
+ font-size: 1em;
+}
+
+pre {
+ white-space: pre-wrap;
+}
+
+fieldset {
+ margin: 0;
+ padding: 0;
+ border: none;
+}
+
+input,
+button,
+select {
+ vertical-align: middle;
+}
+
+button,
+input,
+select,
+textarea {
+ font-family: inherit;
+ font-size: 100%;
+ margin: 0;
+}
+
+button,
+input {
+ line-height: normal;
+}
+
+button,
+html input[type="button"],
+input[type="reset"],
+input[type="submit"] {
+ -webkit-appearance: button;
+ cursor: pointer;
+}
+
+button[disabled],
+html input[disabled] {
+ cursor: default;
+}
+
+input[type="checkbox"],
+input[type="radio"] {
+ padding: 0;
+ border: none;
+}
+
+input[type="search"] {
+ -webkit-appearance: textfield;
+}
+
+input[type="search"]::-webkit-search-cancel-button,
+input[type="search"]::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+ border: 0;
+ padding: 0;
+}
+
+textarea {
+ overflow: auto;
+ vertical-align: top;
+}
+
+table {
+ border-collapse: collapse;
+ margin-bottom: 1.5em;
+}
+
+td,
+th {
+ padding: 1px;
+ vertical-align: top;
+ text-align: left;
+}
+
+td:first-child,
+th:first-child {
+ empty-cells: hide;
+}
+
+
+/* scripts */
+
+body > script {
+ display: none !important;
+}
+
+
+/* HTML5 for old browsers */
+
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+main,
+nav,
+section,
+summary {
+ display: block;
+}
+
+audio,
+canvas,
+video {
+ display: inline-block;
+}
+
+audio:not([controls]) {
+ display: none;
+ height: 0;
+}
+
+figure {
+ margin: 0;
+}
+
+[hidden],
+template {
+ display: none;
+}
+
+svg:not(:root) {
+ overflow: hidden;
+}
+
+
+/* Headings reset */
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-size: 1em;
+ font-weight: normal;
+ margin: 0;
+}
+
+
+/* Screen-reader only */
+
+.visually-hidden {
+ @include visually-hidden;
+ &:focus,
+ &:active {
+ @include visually-hidden-focus;
+ }
+}
+
+.sr-only {
+ @include visually-hidden;
+}
diff --git a/dotclear._no/admin/style/scss/partials/_auth.scss b/dotclear._no/admin/style/scss/partials/_auth.scss
new file mode 100644
index 0000000..004a0a4
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_auth.scss
@@ -0,0 +1,63 @@
+#login-screen {
+ display: block;
+ width: 20em;
+ margin: 1.5em auto 0;
+ font-size: 1.16em;
+ h1 {
+ text-indent: -2000px;
+ background: transparent url(dc_logos/w-dotclear240.png) no-repeat top left;
+ height: 66px;
+ width: 20em;
+ margin-bottom: .5em;
+ margin-left: 0;
+ }
+ .fieldset {
+ border: 1px solid $login-fieldset-border;
+ padding: 1em 1em 0 1em;
+ background: $login-fieldset-background;
+ margin-bottom: 0;
+ margin-top: 1em;
+ }
+ input[type=text],
+ input[type=color],
+ input[type=email],
+ input[type=url],
+ input[type=datetime-local],
+ input[type=date],
+ input[type=time],
+ input[type=file],
+ input[type=number],
+ input[type=password],
+ input[type=submit],
+ input[type=text]:focus,
+ input[type=color]:focus,
+ input[type=email]:focus,
+ input[type=url]:focus,
+ input[type=datetime-local]:focus,
+ input[type=date]:focus,
+ input[type=time]:focus,
+ input[type=file]:focus,
+ input[type=number]:focus,
+ input[type=password]:focus,
+ input[type=submit]:focus {
+ width: 100%;
+ margin: 0;
+ padding: 5px 3px;
+ }
+ input.login,
+ input.login:focus {
+ padding-top: 6px;
+ padding-bottom: 6px;
+ font-size: 1em;
+ }
+ #issue {
+ margin-left: 1.33em;
+ font-size: .91em;
+ p:first-child {
+ text-align: right;
+ }
+ strong {
+ font-weight: normal;
+ }
+ }
+}
diff --git a/dotclear._no/admin/style/scss/partials/_blog_pref.scss b/dotclear._no/admin/style/scss/partials/_blog_pref.scss
new file mode 100644
index 0000000..b3a9c54
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_blog_pref.scss
@@ -0,0 +1,52 @@
+#media_img_title_pattern {
+ margin-right: 1em;
+}
+
+#part-users > div {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: left;
+}
+
+.user-perm {
+ margin: 0 1em 1em 0;
+ background: transparent url(user.png) no-repeat .5em .5em;
+ width: 20em;
+ border: 1px solid $blog-user-border;
+ border-radius: .5em;
+ padding: 0 1em .5em;
+ h4,
+ h5,
+ p,
+ ul,
+ li {
+ margin: .5em 0 .33em;
+ padding: 0;
+ }
+ h4 {
+ padding-left: 28px;
+ }
+ h5 {
+ margin: 1em 0 0 0;
+ }
+ li {
+ margin-left: 1em;
+ padding-left: 0;
+ }
+ &.user_super {
+ border-color: $blog-super-border;
+ background-color: $blog-super-background;
+ }
+}
+
+li.user_super,
+li.user_admin {
+ margin-left: 0;
+ padding-left: 20px;
+ list-style: none;
+ background: transparent url(../images/superadmin.png) no-repeat 0 .3em;
+}
+
+li.user_admin {
+ background-image: url(../images/admin.png);
+}
diff --git a/dotclear._no/admin/style/scss/partials/_blog_theme.scss b/dotclear._no/admin/style/scss/partials/_blog_theme.scss
new file mode 100644
index 0000000..dbfac9a
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_blog_theme.scss
@@ -0,0 +1,178 @@
+/* pour les alignements verticaux */
+
+#theme-new,
+#theme-activate,
+#theme-deactivate,
+#theme-update {
+ margin-left: -10px;
+ margin-right: -10px;
+}
+
+.box.theme {
+ margin: 5px;
+ padding: 10px 10px 5px 10px;
+ border: 1px solid $theme-box-border;
+ position: relative;
+ &:hover {
+ background: $theme-box-over;
+ }
+ input {
+ margin-bottom: 1em;
+ }
+}
+
+.module-name,
+.module-name label {
+ margin-bottom: .5em;
+ color: $theme-name-color;
+}
+
+.module-sshot {
+ text-align: center;
+ img {
+ padding: 5px;
+ background: $theme-img-background;
+ box-shadow: 1px 1px 2px $theme-img-shadow;
+ border: 3px solid $theme-img-border;
+ max-width: 100%;
+ }
+}
+
+.module-actions {
+ margin-top: 1em;
+}
+
+.bloc-toggler {
+ text-align: right;
+ img {
+ opacity: .4;
+ &:hover {
+ opacity: 1;
+ }
+ }
+ a:focus img {
+ opacity: 1;
+ }
+}
+
+span.module-version:before {
+ content: "- ";
+}
+
+.toggle-bloc .mod-more {
+ display: block;
+ margin-left: 0;
+}
+
+.module-name input[type="checkbox"] {
+ margin-bottom: 0;
+}
+
+
+/**
+Les screenshots des thèmes ont deux tailles possibles :
+- dans Ajouter des thèmes : 240px (+ 10 padding image + 20 padding boîte + 6 bordure + 2 ombrage = 278)
+- dans Thèmes installés : 280px (+ 10 padding-image + 20 padding-boîte + 2 ombrage = 318)
+On adapte largeur et hauteur en fonction
+*/
+
+#theme-new .box.theme,
+#theme-update .box.theme {
+ /* Ajouter un thème */
+ width: 278px;
+ min-height: 275px;
+}
+
+#theme-new .module-sshot img {
+ /* Pour ceux qui n'ont pas de miniature on contraint l'image */
+ max-width: 240px;
+ max-height: 210px;
+ overflow: hidden;
+}
+
+#theme-deactivate .box.theme {
+ /* Thèmes désactivés */
+ width: 278px;
+ &:hover {
+ background: transparent url(dc_logos/sq-logo-32.png) no-repeat top right;
+ }
+}
+
+#theme-activate .box.theme {
+ /* Thèmes installés */
+ width: 318px;
+ min-height: 304px;
+ max-width: 100%;
+}
+
+
+/* si js est là, les infos viennent par dessus le screenshot */
+
+.with-js #theme-new {
+ .module-infos.toggle-bloc,
+ .module-actions.toggle-bloc {
+ position: absolute;
+ left: 10px;
+ width: 239px;
+ margin: 0;
+ padding: 10px;
+ background: $theme-action-background;
+ }
+ .module-infos.toggle-bloc {
+ top: 128px;
+ height: 80px;
+ border-top: 1px solid $theme-action-border;
+ }
+ .module-actions.toggle-bloc {
+ top: 208px;
+ height: 40px;
+ border-bottom: 1px solid $theme-action-border;
+ }
+}
+
+.with-js .module-sshot:hover {
+ cursor: pointer;
+}
+
+
+/* mise en forme pour la boîte du thème courant */
+
+.box.current-theme {
+ /* Thème courant */
+ width: 646px;
+ margin: 5px;
+ padding: 20px 18px 6px;
+ background: $theme-current-background;
+ border: 1px solid $theme-current-border;
+ border-radius: .5em;
+ min-height: 326px;
+ box-shadow: 1px 1px 2px $theme-current-shadow;
+ position: relative;
+ .module-sshot:hover {
+ cursor: auto;
+ }
+ .module-sshot img {
+ float: left;
+ margin-right: 2em;
+ border: 9px solid $theme-current-img-border;
+ padding: 5px;
+ max-width: 308px;
+ max-height: 273px;
+ }
+ .module-name {
+ color: $theme-current-name-color;
+ font-size: 1.5em;
+ margin-bottom: 1em;
+ }
+ .module-actions {
+ display: flex;
+ flex-wrap: wrap;
+ }
+}
+
+.current-actions {
+ width: auto;
+ overflow: hidden;
+ padding-top: 2em;
+ background: transparent url(../images/minus-theme.png) no-repeat left top;
+}
diff --git a/dotclear._no/admin/style/scss/partials/_buttons.scss b/dotclear._no/admin/style/scss/partials/_buttons.scss
new file mode 100644
index 0000000..d626210
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_buttons.scss
@@ -0,0 +1,289 @@
+/* Removes inner padding and border in FF3+ - Knacss */
+
+button::-moz-focus-inner,
+input[type=button]::-moz-focus-inner,
+input[type=reset]::-moz-focus-inner,
+input[type=submit]::-moz-focus-inner {
+ border: 0;
+ padding: 0;
+}
+
+
+/* tous les boutons */
+
+button,
+a.button,
+input[type=button],
+input[type=reset],
+input[type=submit] {
+ border: 1px solid $button-border;
+ font-family: $sans-serif-input;
+ padding: 3px 10px;
+ line-height: normal !important;
+ display: inline-block;
+ font-size: 100%;
+ text-align: center;
+ text-decoration: none;
+ cursor: pointer;
+ position: relative;
+ box-shadow: 0 1px 3px $button-shadow;
+ border-style: solid;
+ border-width: 1px;
+}
+
+a.button {
+ vertical-align: middle;
+}
+
+/* validation */
+
+input[type=submit],
+button[type=submit],
+a.button.submit,
+button.submit,
+input.button.start {
+ color: $submit-color;
+ background-color: $submit-background;
+ @include background-image(linear-gradient(to bottom, $submit-background, $submit-background-alt));
+ border-color: $submit-background;
+}
+
+input[type=submit]:hover,
+input[type=submit]:focus,
+button[type=submit]:hover,
+button[type=submit]:focus,
+input.button.start:hover,
+input.button.start:focus,
+button.submit:hover,
+button.submit:focus,
+a.button.submit:hover,
+a.button.submit:focus {
+ background-color: $submit-background-alt;
+ @include background-image(linear-gradient(to bottom, $submit-background-alt, $submit-background));
+ border-color: $submit-background-alt;
+}
+
+
+/* suppression, reset, "neutres" fond gris */
+
+button,
+input[type=button],
+input.button,
+input[type=reset],
+input[type=submit].reset,
+input.reset,
+input[type=submit].delete,
+input.delete,
+a.button,
+a.button.delete,
+a.button.reset {
+ color: $reset-color;
+ background-color: $reset-background;
+ @include background-image(linear-gradient(to bottom, $reset-background-alt, $reset-background));
+ background-repeat: repeat-x;
+ border-color: $reset-border;
+}
+
+button:hover,
+input[type=button]:hover,
+input.button:hover,
+button:focus,
+input[type=button]:focus,
+input.button:focus,
+input[type=reset]:hover,
+input[type=submit].reset:hover,
+input.reset:hover,
+input[type=reset]:focus,
+input[type=submit].reset:focus,
+input.reset:focus,
+input[type=submit].delete:hover,
+input.delete:hover,
+input[type=submit].delete:focus,
+input.delete:focus,
+a.button.delete:hover,
+a.button.reset:hover,
+a.button:hover,
+a.button.delete:focus,
+a.button.reset:focus,
+a.button:focus {
+ background-color: $reset-background-ter;
+ @include background-image(linear-gradient(to bottom, $reset-background, $reset-background-ter));
+ background-repeat: repeat-x;
+ border-color: $reset-border;
+}
+
+
+/* suppression */
+
+input[type=submit].delete,
+input.delete,
+button[type=submit].delete,
+button.delete,
+a.button.delete {
+ color: $delete-color;
+}
+
+input[type=submit].delete:hover,
+input.delete:hover,
+button[type=submit].delete:hover,
+button.delete:hover,
+a.button.delete:hover,
+input[type=submit].delete:focus,
+input.delete:focus,
+button[type=submit].delete:focus,
+button.delete:focus,
+a.button.delete:focus {
+ color: $delete-hover-color;
+ background-color: $delete-hover-background;
+ @include background-image(linear-gradient(to bottom, $delete-hover-background-alt, $delete-hover-background));
+ background-repeat: repeat-x;
+ border-color: $delete-hover-border;
+}
+
+#info-box a.button,
+#info-box button {
+ padding: 0 .5em;
+ margin-left: 2em;
+}
+
+.button.add,
+button.add {
+ color: $add-color;
+ background-color: $add-background;
+ @include background-image(linear-gradient(to bottom, $add-background, $add-background-alt));
+ border-color: $add-border;
+ padding: .33em 1.33em .5em;
+}
+
+.button.add:hover,
+.button.add:active,
+.button.add:focus,
+button.add:hover,
+button.add:active,
+button.add:focus {
+ background-color: $add-background-alt;
+ @include background-image(linear-gradient(to bottom, $add-background-alt, $add-background));
+ border-color: $add-border-alt;
+}
+
+.button-add:focus {
+ outline: dotted 1px;
+}
+
+
+/* paragraphe pour bouton Nouveau bidule */
+
+p.top-add {
+ text-align: right;
+ margin: 0;
+}
+
+
+/* disabled */
+
+input.disabled,
+input[type=submit].disabled,
+button.disabled,
+button[type=submit].disabled {
+ color: $button-disabled-color;
+ background: $button-disabled-background;
+ border: 1px solid $button-disabled-border;
+}
+
+input.disabled:hover,
+input[type=submit].disabled:hover,
+button.disabled:hover,
+button[type=submit].disabled:hover {
+ color: $button-disabled-color;
+ background: $button-disabled-background-alt;
+ border: 1px solid $button-disabled-border;
+}
+
+/* Boutons javascript (dépliage/repliage, …) */
+
+.void-btn {
+ border: none;
+ border-radius: 0;
+ padding: 0;
+}
+
+button.details-cmd {
+ font-size: 0.9em;
+ border: none;
+ border-radius: 0;
+ padding: 0;
+ margin: 0 5px 0 0;
+ color: currentColor;
+ background: transparent;
+ box-shadow: none;
+ &:hover,
+ &:focus {
+ background: transparent;
+ color: $input-focus;
+ }
+}
+
+/* specific buttons */
+
+// Ajax or js buttons (action do not reload current page)
+.checkbox-helper,
+#gototop,
+.metaGetList,
+.metaGetMore,
+a.checkbox-helper,
+a#gototop,
+a.metaGetList,
+a.metaGetMore {
+ font-size: 0.825em;
+ color: $ajax-color;
+ background: $ajax-background;
+ box-shadow: none;
+ border: 1px solid $ajax-border;
+ margin-bottom: .25em;
+ text-align: center;
+ &:hover {
+ background: $ajax-background-alt;
+ box-shadow: none;
+ border: 1px solid $ajax-border;
+ }
+}
+
+#gototop {
+ display: none;
+ z-index: 1000;
+ position: fixed;
+ bottom: 0;
+ right: .5em;
+ width: 10em;
+ padding: .25em;
+ border-radius: .25em;
+ a,
+ a:link,
+ a:hover,
+ a:active {
+ color: $gotop-color;
+ background: $gotop-background;
+ border: $gotop-border;
+ }
+}
+
+.metaRemove,
+.addMeta button:not(.metaGetMore),
+.addMeta a:not(.metaGetMore) {
+ border: none;
+ border-radius: 0;
+ padding: 0;
+ color: $addmeta-color;
+ background: $addmeta-background;
+}
+
+.addMeta button:not(.metaGetMore),
+.addMeta a:not(.metaGetMore) {
+ box-shadow: initial;
+ margin-bottom: 2px;
+ &:hover,
+ &:focus {
+ color: $addmeta-focus-color;
+ background: $addmeta-focus-background;
+ }
+}
diff --git a/dotclear._no/admin/style/scss/partials/_categories.scss b/dotclear._no/admin/style/scss/partials/_categories.scss
new file mode 100644
index 0000000..897721f
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_categories.scss
@@ -0,0 +1,72 @@
+#categories {
+ margin: 1em 0;
+ ul {
+ list-style: none;
+ margin-top: 2em;
+ padding: 0;
+ ul {
+ margin-right: 2em;
+ margin-left: 2em;
+ }
+ }
+ .placeholder {
+ outline: 1px dashed $cat-placeholder-outline;
+ min-height: 2.5em;
+ }
+}
+
+.cat-line {
+ position: relative;
+ margin: .66em 0;
+ padding: .66em 1em;
+ border: 1px solid $cat-line-border;
+ border-radius: 3px;
+ label {
+ margin-right: .25em;
+ a {
+ font-weight: bold;
+ }
+ }
+ p,
+ label {
+ margin: 0;
+ display: inline-block;
+ }
+ .cat-line {
+ border: 1px solid $subcat-line-border;
+ }
+}
+
+p.cat-title {
+ margin-right: 1em;
+}
+
+.cat-nb-posts a {
+ color: $cat-post-counter;
+}
+
+.cat-url {
+ padding-left: 1em;
+}
+
+.cat-buttons {
+ float: right;
+ margin-top: -.2em;
+ font-size: .91em;
+ select {
+ padding: 1px 2px 3px 2px;
+ margin-right: .25em;
+ }
+ .reset {
+ padding-left: 4px;
+ padding-right: 4px;
+ }
+}
+
+.cat-actions {
+ line-height: 2;
+}
+
+#del_cat {
+ width: 100%;
+}
diff --git a/dotclear._no/admin/style/scss/partials/_charte.scss b/dotclear._no/admin/style/scss/partials/_charte.scss
new file mode 100644
index 0000000..9969e13
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_charte.scss
@@ -0,0 +1,30 @@
+.guideline {
+ #content h2 {
+ color: $charte-title;
+ padding: 2em 0 0 0;
+ margin: 1em 0;
+ font-size: 2em;
+ &:first-child {
+ margin-top: 0;
+ padding-top: .5em;
+ }
+ }
+ h3 {
+ margin-top: 2em;
+ }
+ .dc-update h3 {
+ margin-top: 0;
+ }
+ .one-box .box {
+ border: 1px solid $charte-one-box-border;
+ padding: 2px .5em;
+ }
+ #main-menu ul {
+ margin: 0;
+ padding: 0;
+ font-weight: normal;
+ }
+ #main-menu li {
+ padding-left: 1em;
+ }
+}
diff --git a/dotclear._no/admin/style/scss/partials/_classes.scss b/dotclear._no/admin/style/scss/partials/_classes.scss
new file mode 100644
index 0000000..089a0e2
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_classes.scss
@@ -0,0 +1,379 @@
+/* jQuery Autocomplete plugin */
+
+.ac_results {
+ padding: 0px;
+ background-color: $ac-results-background;
+ border: 1px dotted $ac-results-border;
+ overflow: hidden;
+ z-index: 99999;
+ ul {
+ width: 100%;
+ list-style-position: outside;
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ }
+ li {
+ margin: 0px;
+ padding: 2px 5px;
+ cursor: default;
+ display: block;
+ font-size: 1em;
+ line-height: 16px;
+ overflow: hidden;
+ }
+}
+
+.ac_loading {
+ background: transparent url('loader.gif') right center no-repeat;
+}
+
+.ac_odd {
+ background-color: $ac-results-background;
+}
+
+.ac_over {
+ color: $ac-results-over;
+ background-color: $ac-results-over-background;
+}
+
+
+/* password indicator */
+
+.pw-table {
+ display: table;
+ margin-bottom: 1em;
+}
+
+.pw-cell {
+ display: table-cell;
+ margin-bottom: 1em;
+}
+
+#pwindicator {
+ display: table-cell;
+ vertical-align: bottom;
+ padding-left: 1.5em;
+ height: 3.8em;
+ .bar {
+ height: 6px;
+ margin-bottom: 4px;
+ }
+}
+
+.pw-very-weak .bar {
+ background: $pw-very-weak;
+ width: 30px;
+}
+
+.pw-weak .bar {
+ background: $pw-weak;
+ width: 60px;
+}
+
+.pw-mediocre .bar {
+ background: $pw-mediocre;
+ width: 90px;
+}
+
+.pw-strong .bar {
+ background: $pw-strong;
+ width: 120px;
+}
+
+.pw-very-strong .bar {
+ background: $pw-very-strong;
+ width: 150px;
+}
+
+
+/* ------------------------------------------------------------------ navigation */
+
+
+/* selects accès rapide */
+
+.anchor-nav {
+ background: $nav-background;
+ padding: 4px 1em;
+ label {
+ vertical-align: bottom;
+ }
+}
+
+
+/* nav links */
+
+.nav_prevnext {
+ margin-bottom: 2em;
+ color: $nav-background;
+}
+
+.nav_prevnext a,
+a.back {
+ color: $nav-prevnext;
+ border: 1px solid $nav-prevnext-border;
+ padding: 2px 1.5em;
+ border-radius: .75em;
+ background-color: $nav-prevnext-background;
+}
+
+a.back:before {
+ content: "\ab\a0";
+}
+
+a.onblog_link {
+ color: $onblog-link;
+ float: right;
+ border: 1px solid $onblog-link-border;
+ padding: 2px 1.5em;
+ border-radius: .75em;
+ background-color: $onblog-link-background;
+ box-shadow: 0 1px 1px $onblog-link-shadow;
+}
+
+
+/* Pagination */
+
+.pager {
+ margin: 2em 0 1em 0;
+ clear: left;
+ ul {
+ list-style-type: none;
+ margin: 0;
+ padding: 0;
+ }
+ li,
+ input {
+ display: inline-block;
+ vertical-align: middle;
+ margin: 0 .33em 0 0;
+ padding: 0;
+ text-align: center;
+ }
+ .btn {
+ border: 1px solid $pager-border;
+ background-color: $pager-background;
+ color: $pager-link;
+ border-radius: 3px;
+ overflow: hidden;
+ &.no-link {
+ border-color: $pager-border;
+ background-color: $pager-off-background;
+ padding: 1px 3px 0;
+ }
+ }
+ .active {
+ padding: 4px 12px;
+ color: $pager-active;
+ }
+ .direct-access {
+ margin-left: 2em;
+ input[type=text] {
+ border: 1px solid $pager-border;
+ padding: 3px 8px;
+ margin-left: .25em;
+ background-color: $pager-input-background;
+ }
+ input[type=submit] {
+ padding: 3px 6px;
+ }
+ }
+ a {
+ display: block;
+ padding: 1px 3px 0;
+ border: none;
+ &:hover,
+ &:focus {
+ background-color: $pager-background-over;
+ }
+ }
+}
+
+.index {
+ .btn.no-link,
+ a {
+ padding: 2px 8px 3px;
+ font-variant: small-caps;
+ }
+ li {
+ margin-bottom: 3px;
+ }
+ a {
+ font-weight: bold;
+ }
+ .btn.no-link {
+ color: $index-no-link;
+ }
+ .active {
+ padding: 4px 8px;
+ color: $index-active;
+ background: $index-active-background;
+ border-radius: 3px;
+ font-variant: small-caps;
+ }
+}
+
+
+/* Etapes */
+
+.step {
+ display: inline-block;
+ float: left;
+ margin: 3px 10px 2px 0;
+ padding: 5px .5em;
+ color: $step;
+ background: $step-background;
+ border: 1px solid $step-border;
+ border-radius: 3px;
+ font-weight: bold;
+}
+
+/* ------------------------------------------------------------------------- indicateurs */
+
+.mark {}
+
+.mark-published {}
+.mark-unpublished {}
+.mark-scheduled {}
+.mark-pending {}
+.mark-locked {}
+.mark-selected {}
+.mark-hidden {}
+.mark-attach {
+ display: inline-block;
+ box-sizing: border-box;
+ width: 12px;
+ height: 12px;
+ padding-left: 12px;
+ background: $mark-attach-img no-repeat;
+ filter: $mark-attach-filter;
+}
+
+/* ---------------------------------------------------------------- utilisables partout */
+
+.legible {
+ font-size: 1.16em;
+ max-width: 62em;
+}
+
+.fieldset {
+ background: $fieldset-background;
+ border: 1px solid $fieldset-border;
+ border-radius: 3px;
+ padding: 1em .7em .5em;
+ margin-bottom: 1em;
+ h3 {
+ margin-top: 0;
+ }
+ hr {
+ background-color: $fieldset-border;
+ border-width: 0;
+ margin: 1em 0;
+ }
+ &:focus-within {
+ background-color: $fieldset-focus-background;
+ }
+}
+
+.right,
+.txt-right {
+ text-align: right;
+}
+
+.txt-center {
+ text-align: center;
+}
+
+.txt-left {
+ text-align: left;
+}
+
+.no-margin,
+label.no-margin {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+.vertical-separator {
+ margin-top: 2em;
+}
+
+p.clear.vertical-separator {
+ padding-top: 2em;
+}
+
+.border-top {
+ border-top: 1px solid $link-border;
+ padding-top: 1em;
+ margin-top: 1em;
+}
+
+.grid {
+ background: transparent repeat url('grid.png') 0 0;
+}
+
+ul.nice {
+ margin: 1em 0;
+ padding: 0 0 0 2em;
+ list-style: square;
+ li {
+ margin: 0;
+ padding: 0;
+ }
+}
+
+ul.from-left {
+ list-style-type: none;
+ padding-left: 0;
+ margin: 1em 0;
+ > li {
+ margin-top: 1em;
+ margin-bottom: 1em;
+ }
+ ul {
+ list-style-type: square;
+ }
+}
+
+.offline {
+ color: $offline;
+ background: $offline-background;
+}
+
+
+/* caché pour tout le monde */
+
+.hide,
+.button.hide {
+ display: none !important;
+}
+
+
+/* Caché sauf pour les revues d'écran */
+
+.hidden,
+.with-js .out-of-screen-if-js {
+ position: absolute !important;
+ clip: rect(1px 1px 1px 1px);
+ /* IE6, IE7 */
+ clip: rect(1px, 1px, 1px, 1px);
+ padding: 0 !important;
+ border: 0 !important;
+ height: 1px !important;
+ width: 1px !important;
+ overflow: hidden;
+}
+
+
+/* caché si js est inactif */
+
+.no-js .hidden-if-no-js {
+ display: none;
+}
+
+
+/* caché si js est actif */
+
+.with-js .hidden-if-js {
+ display: none;
+}
diff --git a/dotclear._no/admin/style/scss/partials/_colors.scss b/dotclear._no/admin/style/scss/partials/_colors.scss
new file mode 100755
index 0000000..42ffcbc
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_colors.scss
@@ -0,0 +1,144 @@
+// Colors (text and border colors and backgrounds)
+body {
+ color: $primary-color;
+ background: $primary-background;
+}
+
+h2 {
+ color: $ternary-color;
+}
+
+h3,
+.as_h3 {
+ color: $secondary-color;
+}
+
+h4,
+.as_h4 {
+ color: $ternary-color;
+}
+
+h5 {
+ color: $ternary-color;
+}
+
+h6 {
+ color: $ternary-color;
+}
+
+
+/* ---------------------------------------------------------------- layout: main */
+
+#header {
+ color: $white;
+ background: $ternary-background;
+ border-bottom: 4px solid $light-blue;
+}
+
+#wrapper {
+ background: $secondary-background url(bg_wrapper.png) repeat-y 14em;
+}
+
+#content {
+ background: $white;
+}
+
+#main-menu {
+ background: $secondary-background;
+}
+
+#footer {
+ background-color: $white;
+ border-top: 1px solid $gray-light; // #ccc;
+}
+
+
+/* to hide main-menu */
+
+#collapser {
+ // background: $light-blue;
+ background: $gray-light;
+ // background: $light-blue url(../images/collapser-hide.png) no-repeat center bottom;
+ &:hover,
+ &:focus {
+ // background: $gray-dark;
+ background: $light-blue;
+ }
+ .hide-mm & {
+ // background: $gray-dark;
+ background: $light-blue;
+ // background: $light-blue url(../images/collapser-show.png) no-repeat center bottom;
+ &:hover,
+ &:focus {
+ background: $gray-light;
+ }
+ }
+}
+
+
+/* if main-menu is hidden */
+
+#wrapper.hide-mm {
+ background: $white;
+}
+
+
+/* ---------------------------------------------------------------- layout: popups */
+
+.popup h1 {
+ color: $white;
+ background: $ternary-background;
+}
+
+
+/* ------------------------------------------------------------------------------------
+ UN POIL DE MEDIA QUERIES
+------------------------------------------------------------------------------------ */
+
+@media screen and (max-width: $xl-screen) {
+ #header,
+ h1 {
+ background: $gray-very-dark;
+ }
+ #top-info-user {
+ background: $ternary-background;
+ a.active {
+ color: $white;
+ background: $gray-semi-dark;
+ }
+ }
+ #wrapper {
+ background: $secondary-background url(bg_wrapper.png) repeat-y 17em;
+ }
+}
+
+@media screen and (max-width: $s-screen) {
+ #help-button {
+ background-color: $light-blue;
+ }
+}
+
+@media screen and (max-width: $xs-screen) {
+ h1 a {
+ &:link {
+ border-right: 1px solid $gray-light;
+ }
+ &:hover,
+ &:focus {
+ border-right: 1px solid $light-blue;
+ }
+ }
+ .pseudo-tabs li {
+ border-top: 1px solid $gray-light;
+ }
+}
+
+@media screen and (max-width: $xxs-screen) {
+ h1,
+ h1 a {
+ border-right: $gray-very-dark !important;
+ }
+ .pseudo-tabs li {
+ border-top: 1px solid $gray-light !important;
+ }
+}
diff --git a/dotclear._no/admin/style/scss/partials/_common.scss b/dotclear._no/admin/style/scss/partials/_common.scss
new file mode 100644
index 0000000..f9c7a9d
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_common.scss
@@ -0,0 +1,92 @@
+/* Typographie */
+
+:root {
+ // May be superseed by user pref (typically from 50% to 75%, default 62.5%)
+ --html-font-size: #{$html-font-size};
+ // Main colors
+ --body-color: #{$body-color};
+ --body-background: #{$body-background};
+}
+
+// base //
+html {
+ font-size: $html-font-size; // html font-size for IE and Edge
+ font-size: var(--html-font-size); // html font-size for modern browsers
+}
+
+body {
+ color: $body-color;
+ background: $body-background;
+ font-size: $body-font-size;
+ line-height: 1.5;
+ font-family: $sans-serif;
+ &.responsive-font {
+ @media screen and(max-width: $small-screen) {
+ font-size: $body-font-size-small;
+ line-height: 1.3;
+ }
+ @media screen and(min-width: $large-screen) {
+ font-size: $body-font-size-large;
+ line-height: 1.5;
+ }
+ @media screen and(min-width: $small-screen) and (max-width: $large-screen) {
+ font-size: calc( #{$body-font-size-small} + #{strip-unit($body-font-size-large - $body-font-size-small)} * (100vw - #{strip-unit(relative-to-screen($small-screen,$html-font-size))}rem) / (#{strip-unit(relative-to-screen($large-screen,$html-font-size))} - #{strip-unit(relative-to-screen($small-screen,$html-font-size))}));
+ line-height: calc( 1.3em + (1.5 - 1.3) * (100vw - #{strip-unit(relative-to-screen($small-screen,$html-font-size))}rem) / (#{strip-unit(relative-to-screen($large-screen,$html-font-size))} - #{strip-unit(relative-to-screen($small-screen,$html-font-size))}));
+ }
+ }
+}
+
+#wrapper {
+ @media screen and (max-width: $small-screen) {
+ font-size: 1.2em;
+ }
+}
+
+// titres //
+h2,
+h3,
+.as_h3,
+h4,
+.as_h4,
+h5,
+h6 {
+ margin-top: 0;
+ margin-bottom: 1em;
+}
+
+h2 {
+ color: $title-color;
+ font-size: 1.5em;
+ font-weight: normal;
+ line-height: 1.25;
+ padding: 0 0 1.5em;
+}
+
+h3,
+.as_h3 {
+ color: $title-alt-color;
+ font-size: 1.34em;
+ font-weight: normal;
+ line-height: 1.5;
+ margin-top: 1em;
+}
+
+h4,
+.as_h4 {
+ color: $title-color;
+ font-size: 1.16em;
+ line-height: 1.5;
+}
+
+h5 {
+ color: $title-color;
+ font-size: 1em;
+ line-height: 1.5;
+ font-weight: bold;
+}
+
+h6 {
+ color: $title-color;
+ font-size: 1em;
+ line-height: 1.5;
+}
diff --git a/dotclear._no/admin/style/scss/partials/_content.scss b/dotclear._no/admin/style/scss/partials/_content.scss
new file mode 100644
index 0000000..5c303ec
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_content.scss
@@ -0,0 +1,173 @@
+.part-tabs {
+ ul {
+ padding: .5em 0 0 1em;
+ border-bottom: 1px solid $tabs-border;
+ line-height: 1.8;
+ }
+ li {
+ list-style: none;
+ margin: 0;
+ display: inline;
+ &:first-child a {
+ border-top-left-radius: 3px;
+ }
+ &:last-child a {
+ border-top-right-radius: 3px;
+ }
+ a {
+ padding: .33em 1.5em;
+ margin-right: -1px;
+ border: 1px solid $tabs-border;
+ border-bottom: none;
+ text-decoration: none;
+ color: $tabs-color;
+ background-color: $tabs-background;
+ display: inline-block;
+ &:hover,
+ &:focus {
+ color: $tabs-color;
+ background: $tabs-active-background;
+ border-bottom-color: $tabs-active-background;
+ }
+ &:link {
+ transition: unset;
+ }
+ }
+ &.part-tabs-active a {
+ color: $tabs-active-color;
+ background: $tabs-active-background;
+ font-weight: bold;
+ border-bottom-color: $tabs-active-background;
+ }
+ }
+}
+
+.multi-part {
+ padding-left: 1em;
+}
+
+.pseudo-tabs {
+ margin: -.75em 0 2em 0;
+ border-bottom: 1px solid $tabs-border;
+ display: table;
+ width: 100%;
+ padding: 0;
+ line-height: 24px;
+ border-collapse: collapse;
+ li {
+ display: table-cell;
+ border-width: 0 1px;
+ border-style: solid;
+ border-color: $tabs-border;
+ padding: 0;
+ margin: 0;
+ text-align: center;
+ }
+ a {
+ display: block;
+ font-weight: bold;
+ padding: 0 24px;
+ border-bottom: none;
+ &:hover,
+ &:focus {
+ background-color: $tabs-background;
+ color: $tabs-color;
+ }
+ &.active {
+ background-color: $tabs-active-background;
+ color: $tabs-active-color;
+ }
+ }
+}
+
+
+/* contextual help */
+
+#help {
+ margin-top: 4em;
+ background: $help-background;
+ z-index: 100;
+ clear: both;
+ padding: 0 1em;
+ #content.with-help & {
+ display: block;
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: 32rem; // 24em;
+ border-left: 2px solid $help-border;
+ border-top: 2px solid $help-border;
+ margin-top: 0;
+ padding: .5em 0 0 0;
+ overflow: auto;
+ }
+}
+
+#help-button {
+ background: transparent url(help-mini.png) no-repeat 6px center;
+ position: absolute;
+ top: 0;
+ right: 0;
+ padding: 0 1.5em 0 30px;
+ cursor: pointer;
+ color: $link;
+ line-height: 3;
+ &.floatable {
+ border-top: 2px solid $help-button-border;
+ border-left: 2px solid $help-button-border;
+ border-bottom: 2px solid $help-button-border;
+ border-bottom-left-radius: 1em;
+ border-top-left-radius: 1em;
+ background-color: $help-button-background;
+ position: fixed;
+ top: 10px;
+ -webkit-transform: translateZ(0); // Let GPU doing his job
+ }
+ .no-js & {
+ top: 1em;
+ }
+ span {
+ padding: .5em 0 .1em 0;
+ }
+ #content.with-help & {
+ right: 32rem; // 24em;
+ background-color: $help-background;
+ position: fixed;
+ top: 6em; // 50px;
+ z-index: 100;
+ border-top: 2px solid $help-border;
+ border-left: 2px solid $help-border;
+ border-bottom: 2px solid $help-border;
+ border-bottom-left-radius: 1em;
+ border-top-left-radius: 1em;
+ }
+}
+
+.help-box {
+ display: none;
+ ul {
+ padding-left: 20px;
+ margin-left: 0;
+ }
+}
+
+.help-content {
+ #content.with-help & {
+ padding: 0 1em 1em;
+ }
+ dt {
+ font-weight: bold;
+ color: $help-title-color;
+ margin: 0;
+ }
+ dd {
+ margin: 0.3em 0 1.5em 0;
+ }
+}
+
+
+/* lien d'aide générale dans le help content */
+
+#helplink p {
+ padding: 0 0 0 .5em;
+}
diff --git a/dotclear._no/admin/style/scss/partials/_debug.scss b/dotclear._no/admin/style/scss/partials/_debug.scss
new file mode 100644
index 0000000..8b698c3
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_debug.scss
@@ -0,0 +1,40 @@
+/* debug */
+
+#debug {
+ position: absolute;
+ top: 0;
+ width: 100%;
+ height: 4px;
+ background: $debug-background;
+ div {
+ display: none;
+ padding: 3px 0.5em 2px;
+ }
+ p {
+ margin: 0.5em 0;
+ }
+ &:hover {
+ height: auto;
+ padding: 2px 1em;
+ z-index: 100;
+ div {
+ display: block;
+ }
+ }
+}
+
+.debug {
+ color: $debug-color;
+ background: $debug-background;
+ padding: 3px 0.5em 2px;
+}
+
+input[type=submit].delete.debug,
+a.delete.debug {
+ border-color: $debug-border;
+ &:hover {
+ background: $debug-background;
+ color: $debug-color;
+ border-color: $debug-border;
+ }
+}
diff --git a/dotclear._no/admin/style/scss/partials/_editors.scss b/dotclear._no/admin/style/scss/partials/_editors.scss
new file mode 100644
index 0000000..3b87337
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_editors.scss
@@ -0,0 +1,16 @@
+/* dcLegacyEditor */
+
+/* WYSIWYG Document */
+body.wysiwygDoc {
+ font-family: $sans-serif;
+}
+.wysiwygDoc pre, .wysiwygDoc code, .wysiwygDoc kbd, .wysiwygDoc samp {
+ font-family: $monospace;
+}
+
+/* dcCKEditor */
+
+.cke textarea.cke_source {
+ font-family: $monospace;
+ font-size: 100%;
+}
diff --git a/dotclear._no/admin/style/scss/partials/_entry.scss b/dotclear._no/admin/style/scss/partials/_entry.scss
new file mode 100644
index 0000000..5fb0604
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_entry.scss
@@ -0,0 +1,67 @@
+#entry-form {
+ display: flex;
+ flex-wrap: wrap;
+}
+
+#entry-wrapper {
+ flex-grow: 1;
+ width: calc(100% - 19em);
+ @media screen and (max-width: $xl-screen) {
+ width: 100%;
+ }
+}
+
+#entry-content {
+ margin-left: 0;
+ @media screen and (min-width: $xl-screen + 0.01em ) {
+ // Large padding on big screens
+ padding-right: 3em;
+ }
+ @media screen and (max-width: $xs-screen) {
+ // Small padding on medium screens
+ padding-right: 1em;
+ }
+ @media screen and (max-width: $xxs-screen) {
+ // No padding on small screens
+ padding-right: 0;
+ }
+}
+
+#entry-sidebar {
+ display: flex;
+ flex-wrap: wrap;
+ flex-direction: column;
+ @media screen and (max-width: $xl-screen) {
+ flex-direction: row;
+ }
+ @media screen and (max-width: $xs-screen) {
+ padding-right: 1em;
+ }
+
+ h4 {
+ font-size: 1.08em;
+ margin-top: .3em;
+ border-bottom: 1px solid $entry-sb-title-border;
+ }
+ select {
+ width: 100%;
+ }
+ input#post_position {
+ width: 4em;
+ }
+}
+
+.sb-box {
+ width: 18em;
+ margin-bottom: 1em;
+ margin-right: 1em;
+ padding: .5em 1em;
+ background-color: $entry-sb-background;
+ &:focus-within {
+ background-color: $entry-sb-focus-background;
+ }
+}
+
+#tb_excerpt {
+ width: 100%;
+}
diff --git a/dotclear._no/admin/style/scss/partials/_filters.scss b/dotclear._no/admin/style/scss/partials/_filters.scss
new file mode 100644
index 0000000..967bea4
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_filters.scss
@@ -0,0 +1,65 @@
+a.form-control {
+ display: none;
+ color: $form-control-color;
+ &::before {
+ content: "►";
+ margin-right: 5px;
+ }
+ &.open {
+ &::before {
+ content: "▼";
+ }
+ }
+}
+
+#filters-form {
+ border: 1px solid $filter-border;
+ border-radius: .3em;
+ margin-bottom: 2em;
+ padding: .5em 1em 0;
+ .table {
+ width: 100%;
+ padding: 0;
+ margin-bottom: 1em;
+ margin-top: .5em;
+ }
+ .cell {
+ padding: 0 2em 0 0;
+ }
+ .filters-sibling-cell {
+ padding-top: 3.8em;
+ }
+ .filters-options {
+ padding-left: 2em;
+ border-left: 1px solid $filter-options-border;
+ }
+ select {
+ width: 14em;
+ vertical-align: middle;
+ }
+ h4 {
+ margin-top: 0;
+ margin-bottom: 2em;
+ }
+ label.ib,
+ span.ib {
+ width: 7em;
+ }
+ label.ibw,
+ span.ibw {
+ width: 9em;
+ display: inline-block;
+ }
+ &:focus-within {
+ background-color: $filter-focus-background;
+ }
+}
+
+span.ib {
+ width: 7em;
+}
+
+span.ibw {
+ width: 9em;
+ display: inline-block;
+}
diff --git a/dotclear._no/admin/style/scss/partials/_footer.scss b/dotclear._no/admin/style/scss/partials/_footer.scss
new file mode 100644
index 0000000..50dae70
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_footer.scss
@@ -0,0 +1,36 @@
+#footer {
+ p {
+ margin: 0;
+ padding: 0 1em;
+ font-size: 1em;
+ }
+ a:hover span.tooltip {
+ padding: 10px;
+ color: $tooltip-color;
+ height: auto;
+ width: auto;
+ left: 0;
+ bottom: 0;
+ background: $tooltip-background;
+ z-index: 99;
+ font-family: monospace;
+ text-align: left;
+ border-top: 1px solid $tooltip-color;
+ border-right: 1px solid $tooltip-color;
+ border-radius: 0 2em 0 0;
+ }
+}
+
+span.credit {
+ font-size: 1em;
+ font-weight: normal;
+}
+
+span.tooltip {
+ position: absolute;
+ padding: 0;
+ border: 0;
+ height: 1px;
+ width: 1px;
+ overflow: hidden;
+}
diff --git a/dotclear._no/admin/style/scss/partials/_forms.scss b/dotclear._no/admin/style/scss/partials/_forms.scss
new file mode 100644
index 0000000..789e256
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_forms.scss
@@ -0,0 +1,277 @@
+input[type=text],
+input[type=color],
+input[type=email],
+input[type=url],
+input[type=datetime-local],
+input[type=date],
+input[type=time],
+input[type=file],
+input[type=number],
+input[type=password],
+input[type=submit],
+input[type=button],
+input[type=reset],
+a.button, button, textarea, select, legend {
+ max-width: 100%;
+}
+input[type=text],
+input[type=color],
+input[type=email],
+input[type=url],
+input[type=datetime-local],
+input[type=date],
+input[type=time],
+input[type=file],
+input[type=number],
+input[type=password],
+input[type=submit],
+input[type=button],
+input[type=reset],
+a.button, button, textarea, legend {
+ border-radius: 3px;
+}
+form {
+ display: block;
+ margin: 0;
+ padding: 0;
+}
+fieldset {
+ display: block;
+ margin: 1em 0;
+ padding: 1em 0.5em;
+ border-width: 1px 0;
+ border-style: solid;
+ border-color: $fieldset-border;
+ background: $fieldset-background;
+ hr {
+ background-color: $fieldset-border;
+ border-width: 0;
+ margin: 1em 0;
+ }
+ &:focus-within {
+ background-color: $fieldset-focus-background;
+ }
+}
+input[type=text],
+input[type=color],
+input[type=email],
+input[type=url],
+input[type=datetime-local],
+input[type=date],
+input[type=time],
+input[type=file],
+input[type=number],
+textarea {
+ font-family: $sans-serif-input;
+ font-size: 100%;
+}
+legend {
+ padding: 0.2em 0.6em;
+ border-width: 1px;
+ border-style: solid;
+ border-color: $legend-border;
+ background: $legend-background;
+ margin-bottom: 0.5em;
+}
+label .maximal, textarea.maximal, input.maximal, select.maximal {
+ width: 99%;
+}
+input[type=text],
+input[type=password],
+input[type=color],
+input[type=email],
+input[type=url],
+input[type=datetime-local],
+input[type=date],
+input[type=time],
+input[type=file],
+input[type=number],
+textarea, select, input:not([type=file]):invalid, input:not([type=file]):invalid:placeholder-shown {
+// color: $input-color;
+ box-shadow: 1px 1px 2px $input-shadow inset;
+ padding: 3px;
+ vertical-align: top;
+}
+input[type=text],
+input[type=password],
+input[type=color],
+input[type=email],
+input[type=url],
+input[type=datetime-local],
+input[type=date],
+input[type=time],
+input[type=file],
+input[type=number],
+textarea, input:not([type=file]):invalid, input:not([type=file]):invalid:placeholder-shown {
+// background: $input-background;
+ border-width: 1px;
+ border-style: solid;
+ border-color: $input-border;
+}
+input[type=file] {
+// color: $input-file-color;
+// background: $input-file-background;
+}
+// Special for invalid but not required fields
+input:invalid:not(:required), textarea:invalid:not(:required), select:invalid:not(:required), input:not([type=file]):invalid:not(:focus):not(:required) {
+ color: $invalid-input-color;
+ box-shadow: 0 0 0 3px $invalid-input-shadow;
+}
+input:invalid:not(:required), textarea:invalid:not(:required), input:not([type=file]):invalid:not(:focus):not(:required) {
+ border: 1px solid $invalid-input-border;
+ background: $invalid-input-background;
+}
+//
+input:focus, textarea:focus {
+ border-color: $input-focus;
+}
+textarea {
+ padding: 2px 0;
+ &.maximal {
+ resize: vertical;
+ }
+ .area & {
+ display: block;
+ width: 100%;
+ resize: vertical;
+ }
+}
+select {
+ padding: 2px 0;
+ vertical-align: middle;
+}
+@media not all and (min-resolution:.001dpcm) {
+ @supports (-webkit-appearance: none) {
+ /* Safari 10.1+ only (https://browserstrangeness.bitbucket.io/css_hacks.html#webkit) */
+ select {
+ font-size: initial;
+ }
+ }
+}
+select.l10n option {
+ padding-left: 16px;
+}
+option.avail10n {
+ background: transparent url(../images/check-on.png) no-repeat 0 50%;
+}
+input[type=text],
+input[type=color],
+input[type=email],
+input[type=url],
+input[type=datetime-local],
+input[type=date],
+input[type=time],
+input[type=number],
+input[type=password],
+textarea {
+ margin-right: .3em;
+}
+input[type=checkbox], input[type=radio], input[type=file] {
+ border: none;
+ margin: 0 .33em 0 0;
+ padding: 0;
+// background: transparent;
+}
+input+input[type=checkbox] {
+ // Used for js expand/hide (lists, options, ...)
+ margin-left: .33em;
+}
+a input {
+ // Used for js expand/hide (lists, options, ...)
+ margin-right: .33em;
+}
+input[type=file] {
+ margin-top: .3em;
+ margin-bottom: .3em;
+}
+input[type=color] {
+ width: 4em;
+ height: 3em;
+}
+optgroup {
+ font-weight: bold;
+ font-style: normal;
+}
+option {
+ font-weight: normal;
+}
+label, label span {
+ display: block;
+}
+label.ib, input.ib {
+ display: inline-block;
+}
+label.classic {
+ display: inline;
+}
+label.classic input, label span input, label.classic select, label span select {
+ display: inline;
+}
+label.required {
+ font-weight: bold;
+}
+label.required abbr {
+ color: $required;
+ font-size: 1.3em;
+ text-decoration: none;
+}
+label.bold {
+ text-transform: uppercase;
+ font-weight: bold;
+ margin-top: 2em;
+}
+label.area, p.area, div.area {
+ width: inherit !important;
+}
+div.area {
+ margin-bottom: 1em;
+}
+p.field {
+ position: relative;
+ label {
+ display: inline-block;
+ width: 14em;
+ }
+ &.wide label {
+ width: 21em;
+ }
+ input, select {
+ display: inline-block;
+ }
+}
+.form-note, .form-stats {
+ font-style: italic;
+ font-weight: normal;
+ color: $form-note-color;
+}
+p.form-note, p.form-stats {
+ margin-top: -.7em;
+}
+span.form-note, span.form-stats {
+ text-transform: none;
+}
+.missing {
+ background-color: inherit;
+ animation-name: kf-missing;
+ animation-duration: 1s;
+}
+@keyframes kf-missing {
+ 50% {
+ background-color: $error-background;
+ }
+}
+.focus {
+ background-color: inherit;
+ animation-name: kf-focus;
+ animation-duration: 1s;
+}
+@keyframes kf-focus {
+ 50% {
+ background-color: $input-focus;
+ }
+}
+// .more-info Additional information on fieldset, field, … (may be hide from user-prefs)
+.more-info {}
+.no-more-info {
+ display: none;
+}
diff --git a/dotclear._no/admin/style/scss/partials/_header.scss b/dotclear._no/admin/style/scss/partials/_header.scss
new file mode 100644
index 0000000..1d7848f
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_header.scss
@@ -0,0 +1,152 @@
+/* prelude */
+
+#prelude {
+ line-height: 1.5;
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ background: $prelude-background;
+ width: 100%;
+ li {
+ list-style-type: none;
+ margin: 0;
+ background: transparent;
+ display: inline;
+ a {
+ padding: 3px 16px 3px 8px;
+ color: $prelude-color;
+ background: $prelude-background;
+ text-decoration: underline;
+ &:hover,
+ &:focus {
+ background: $prelude-background-alt;
+ }
+ }
+ }
+}
+
+
+/* si le prélude est affiché on repousse les trucs dessous */
+
+#wrapper.with-prelude {
+ padding-top: 1em;
+}
+
+#help-button.with-prelude,
+#collapser.with-prelude {
+ top: 1em;
+}
+
+
+/* header global h1, form#top-info-blog, ul#top-info-user */
+
+#header {
+ a {
+ color: $header-color;
+ }
+ img {
+ vertical-align: middle;
+ padding-left: .5em;
+ }
+}
+
+
+/* h1 */
+
+h1 {
+ text-indent: 100%;
+ width: 16.5em;
+ a {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 150px;
+ height: 36px; // was 3em;
+ color: $header-color;
+ background: transparent url(dc_logos/b-dotclear120.png) no-repeat 0 6px;
+ transition: none;
+ &:hover,
+ &:focus {
+ background-position: 0 -94px;
+ background-color: transparent;
+ transition: none;
+ }
+ &:link {
+ transition-timing-function: ease-in-out;
+ }
+ }
+}
+
+
+/* top-info-blog */
+
+#top-info-blog {
+ select {
+ max-width: 20em;
+ }
+ a {
+ margin-left: 1.5em;
+ }
+ input[type=submit] {
+ background: $submit-background;
+ border-color: $submit-border;
+ margin-left: .33em;
+ }
+ input[type=submit]:hover {
+ color: $submit-color;
+ background: $submit-background-alt;
+ }
+ p {
+ display: inline-block;
+ margin: 0;
+ }
+}
+
+
+/* top-info-user */
+
+#top-info-user {
+ padding-right: .5em;
+ list-style-type: none;
+ text-align: right;
+ li {
+ display: inline-block;
+ margin-left: .5em;
+ padding-left: .5em;
+ border-left: 1px solid $header-link-border;
+ &:first-child {
+ border-left: none;
+ }
+ }
+ a.active {
+ border-width: 0;
+ border-radius: 4px;
+ margin: 0;
+ padding: 2px 8px 3px;
+ color: $header-active-color;
+ background-color: $header-active-background;
+ font-weight: bold;
+ }
+}
+
+
+/* ------------------------------------------------------------------------------------
+ UN POIL DE MEDIA QUERIES
+------------------------------------------------------------------------------------ */
+
+@media screen and (max-width: $xxs-screen) {
+ h1,
+ h1 a {
+ padding: 0;
+ }
+}
+
+@media screen and (max-width: $xs-screen) {
+ h1 a:link {
+ background: transparent url(dc_logos/b-dotclear120.png) no-repeat -270px 6px;
+ }
+ h1 a:hover,
+ h1 a:focus {
+ background: url(dc_logos/b-dotclear120.png) no-repeat -270px -94px;
+ }
+}
diff --git a/dotclear._no/admin/style/scss/partials/_index.scss b/dotclear._no/admin/style/scss/partials/_index.scss
new file mode 100644
index 0000000..8e4eed6
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_index.scss
@@ -0,0 +1,163 @@
+#dashboard-main {
+ text-align: center;
+ &>*:last-child {
+ margin-bottom: 1em;
+ }
+}
+/* raccourcis */
+
+#icons {
+ margin: 1em auto 2em;
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ p {
+ width: 13em;
+ margin: 1em 0 2em;
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+ align-items: center;
+ }
+ a {
+ &, &:link, &:visited, &:hover, &:focus {
+ border-bottom-width: 0;
+ text-decoration: none;
+ }
+ span {
+ color: $index-link-color;
+ border-bottom: 1px dotted $index-link-border;
+ }
+ img {
+ padding: 1.5em;
+ background-color: $index-icon-background;
+ border-radius: 8px;
+ border: 1px solid $index-icon-border;
+ display: inline-block;
+ filter: contrast($index-icon-contrast);
+ }
+ &:focus {
+ outline: 0;
+ span {
+ border: 2px solid $index-icon-outline;
+ }
+ }
+ &:focus, &:hover {
+ img {
+ background: $index-icon-outline;
+ outline: 0;
+ border-color: $index-icon-border;
+ }
+ span {
+ border-bottom-style: solid;
+ }
+ }
+ }
+}
+/* billet rapide */
+
+#quick {
+ max-width: 72em;
+ margin: 1em auto 2em;
+ padding: 1em;
+ background: $quick-background;
+ border: 1px solid $quick-border;
+ text-align: left;
+ h3 {
+ margin-bottom: 0.2em;
+ font-size: 1.2em;
+ }
+ p.qinfo {
+ margin: -.7em -1em 1em;
+ background: $quick-info-background url(msg-info.png) no-repeat .2em .2em;
+ border: 1px solid $quick-info-border;
+ padding: .2em 1em .1em 24px;
+ color: $quick-info-color;
+ }
+ #new_cat, .q-cat, .q-cat label {
+ display: inline-block;
+ vertical-align: top;
+ margin-right: 1em;
+ margin-top: 0;
+ }
+ .q-cat label {
+ margin-right: .3em;
+ }
+ #new_cat {
+ margin-bottom: 2em;
+ }
+}
+/* modules additionnels */
+
+#dashboard-boxes {
+ margin: 1em auto 2em;
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ .box {
+ padding: 10px;
+ border: 1px solid $index-box-border;
+ border-radius: 3px;
+ min-height: 200px;
+ margin: 10px;
+ text-align: left;
+ }
+}
+.db-items, .db-contents {
+ // display: inline-block;
+ // text-align: center;
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ flex: 1 1 auto;
+ img {
+ vertical-align: middle;
+ }
+ ul {
+ display: block;
+ padding-left: 1.5em;
+ list-style: square;
+ }
+ li {
+ margin: 0.25em 0 0 0;
+ }
+}
+.no-js .outgoing img {
+ display: none;
+}
+.dc-box {
+ background: transparent url(dc_logos/sq-logo-32.png) no-repeat top right;
+}
+#news {
+ dt {
+ font-weight: bold;
+ margin: 0 0 0.4em 0;
+ }
+ dd {
+ margin: 0 0 1em 0;
+ p {
+ margin: 0.2em 0 0 0;
+ }
+ }
+}
+// Drag'n'drop of dashboard areas
+#dragndrop {
+ .no-js & {
+ display: none;
+ }
+ position: absolute;
+ &+label {
+ position: absolute;
+ display: inline-block;
+ line-height: 1;
+ }
+ &+label .dragndrop-svg {
+ width: 2em;
+ height: 1.5em;
+ fill: $drag-n-drop-off;
+ }
+ &:checked+label .dragndrop-svg {
+ fill: $drag-n-drop-on;
+ background-color: var(--body-background);
+ }
+}
diff --git a/dotclear._no/admin/style/scss/partials/_layout.scss b/dotclear._no/admin/style/scss/partials/_layout.scss
new file mode 100644
index 0000000..4fa16f3
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_layout.scss
@@ -0,0 +1,544 @@
+// Admin layout
+#dotclear-admin {
+ display: flex;
+ min-height: 100vh;
+ flex-direction: column;
+}
+
+#header {
+ color: $header-color;
+ background: $header-background;
+ border-bottom: 4px solid $header-border;
+ width: 99.99%;
+ /* Bugfix Chrome >= 49.0.2623.108 */
+ display: table;
+ position: relative;
+}
+
+h1,
+#top-info-blog,
+#top-info-user {
+ display: table-cell;
+ padding: 8px 0;
+ margin: 0;
+ font-size: 1em;
+ vertical-align: top;
+}
+
+#wrapper {
+ background: $wrapper-background;
+ position: relative;
+ padding-top: 1.5em;
+ float: left;
+ width: 100%;
+ z-index: 10;
+ flex: 1;
+}
+
+.with-js #wrapper {
+ padding-top: 0;
+}
+
+#main {
+ width: 100%;
+ float: right;
+ margin-left: -14em;
+ margin-top: 0;
+}
+
+#content {
+ background: $content-background;
+ margin: 0 0 0 14em;
+ padding: .5em 1.5em .75em 2.5em;
+}
+
+#main-menu {
+ background: $main-menu-background;
+ width: 14em;
+ float: left;
+ margin: 0;
+ padding-top: .5em;
+ padding-bottom: 1em;
+ overflow: hidden;
+}
+
+#footer {
+ background-color: $footer-background;
+ border-top: 1px solid $footer-border;
+ clear: both;
+ position: relative;
+ padding: .5em 0 .5em 1em;
+ text-align: left;
+}
+
+
+/* to hide main-menu */
+
+#collapser {
+ background: $collapser-background;
+ position: absolute;
+ top: 0;
+ left: 14em;
+ width: $collapser-width;
+ height: 100%;
+ overflow: hidden;
+ display: block;
+ border-right: 0;
+ border-bottom: 0;
+ z-index: 1;
+ transition: none;
+ &:hover,
+ &:focus {
+ background: $collapser-focus;
+ }
+ .hide-mm & {
+ background: $collapser-focus;
+ &:hover,
+ &:focus {
+ background: $collapser-background;
+ }
+ }
+}
+
+.expand-mm {
+ display: none;
+}
+
+
+/* if main-menu is hidden */
+
+.hide-mm {
+ #main {
+ margin-left: 0;
+ }
+ #content {
+ margin-left: $collapser-width;
+ > h2 {
+ margin-left: calc(-1em - #{$collapser-width});
+ }
+ }
+ #main-menu {
+ display: none;
+ }
+ #collapser {
+ left: 0;
+ }
+ .collapse-mm {
+ display: none;
+ }
+ .expand-mm {
+ display: block;
+ }
+}
+
+#wrapper.hide-mm {
+ background: $content-background;
+}
+
+
+/* -------------------------------------------------------------- layout: two-cols */
+
+.two-cols {
+ position: static;
+ .col {
+ width: 48%;
+ margin-left: 2%;
+ float: left;
+ &:first-child {
+ margin-left: 0;
+ margin-right: 2%;
+ }
+ &:last-child {
+ margin-left: 2%;
+ margin-right: 0;
+ }
+ }
+ .col70 {
+ width: 68%;
+ margin-left: 0;
+ float: left;
+ &.last-col {
+ margin-left: 2%;
+ margin-right: 0;
+ }
+ }
+ .col30 {
+ width: 28%;
+ margin-left: 2%;
+ float: left;
+ &.first-col {
+ margin-left: 0;
+ margin-right: 2%;
+ }
+ }
+ table {
+ width: 90%;
+ }
+}
+
+
+/* -------------------------------------------------------------- layout: three-cols */
+
+.three-cols {
+ position: static;
+ .col {
+ width: 32.3%;
+ float: left;
+ margin-left: 1%;
+ &:first-child {
+ margin-left: 0;
+ }
+ }
+}
+
+
+/* ------------------------------------------------- layout: optionnal one/two/three-boxes */
+
+.one-box {
+ text-align: justify;
+}
+
+.two-boxes {
+ width: 48.5%;
+}
+
+.three-boxes {
+ width: 30%;
+}
+
+.two-boxes,
+.three-boxes {
+ display: inline-block;
+ vertical-align: top;
+ margin: 0 1.5% 1em;
+ text-align: left;
+}
+
+.two-boxes:nth-of-type(odd),
+.three-boxes:nth-of-type(3n+1) {
+ margin-left: 0;
+}
+
+.two-boxes:nth-of-type(even),
+.three-boxes:nth-of-type(3n) {
+ margin-right: 0;
+}
+
+
+/* ---------------------------------------------------------------- layout: popups */
+
+.popup {
+ h1 {
+ color: $popup-title-color;
+ background: $popup-title-background;
+ display: block;
+ width: 100%;
+ margin: 0;
+ font-size: 1.5em;
+ text-indent: 1em;
+ line-height: 1.5em;
+ font-weight: normal;
+ }
+ #wrapper {
+ display: block;
+ float: none;
+ width: 100%;
+ margin: 0;
+ padding: 0;
+ background-position: 0 0;
+ }
+ #main {
+ margin: 0;
+ padding: 0;
+ }
+ #content {
+ margin: 0;
+ padding: 1em;
+ h2 {
+ margin: 0 0 1em 0;
+ padding: 0;
+ }
+ }
+ #footer p {
+ border: none;
+ }
+}
+
+
+/* -------------------------------------------------------- layout: classes de complément */
+
+.constrained {
+ margin: 0;
+ padding: 0;
+ border: none;
+ background: transparent;
+}
+
+.table {
+ display: table;
+}
+
+.cell {
+ display: table-cell;
+ vertical-align: top;
+}
+
+.clear {
+ clear: both;
+}
+
+.lclear {
+ clear: left;
+}
+
+.clearer {
+ height: 1px;
+ font-size: 1px;
+}
+
+
+/* Micro clearfix thx to Nicolas Gallagher */
+
+.clearfix:before,
+.clearfix:after {
+ content: " ";
+ display: table;
+}
+
+.clearfix:after {
+ clear: both;
+}
+
+.box {
+ display: inline-block;
+ vertical-align: top;
+ margin: 0 10px 10px;
+ text-align: left;
+ flex: 1 1 auto;
+ &.small {
+ // width: 312px;
+ flex-basis: 312px;
+ }
+ &.medium {
+ // width: 644px;
+ flex-basis: 644px;
+ }
+ &.large {
+ width: 100%;
+ }
+}
+
+.odd {
+ margin-left: 0;
+}
+
+.even {
+ margin-right: 0;
+}
+
+
+/* ------------------------------------------------------------------------------------
+ UN POIL DE MEDIA QUERIES
+------------------------------------------------------------------------------------ */
+
+@media screen and (max-width: $xl-screen) {
+ #header {
+ display: block;
+ width: 100%;
+ text-align: right;
+ h1,
+ h1 a {
+ width: 120px;
+ margin: 0;
+ }
+ }
+ h1 {
+ width: 19.5em;
+ display: inline-block;
+ vertical-align: top;
+ margin-right: 1em;
+ }
+ #top-info-blog {
+ display: inline-block;
+ vertical-align: top;
+ margin-right: 1em;
+ #switchblog {
+ max-width: 16em;
+ }
+ a {
+ margin-left: 2em;
+ }
+ }
+ #top-info-user {
+ display: block;
+ width: 100%;
+ }
+ #collapser {
+ left: 17em;
+ }
+ #main {
+ margin-left: -17em;
+ }
+ #content {
+ margin: 0 0 0 17em;
+ }
+ #main-menu {
+ width: 17em;
+ }
+ .three-boxes,
+ .three-boxes .box,
+ .two-cols .col70,
+ .two-cols .col30 {
+ width: 100%;
+ margin-left: 0;
+ margin-right: 0;
+ }
+}
+
+@media screen and (max-width: $m-screen) {
+ #dashboard-boxes .box.medium,
+ .box.medium,
+ #dashboard-boxes .box.small,
+ .box.small,
+ #dashboard-boxes .box.large,
+ .box.large {
+ width: 95%;
+ margin: 10px auto;
+ }
+}
+
+@media screen and (max-width: $s-screen) {
+ #help-button {
+ height: 26px;
+ width: 26px;
+ margin: 0;
+ overflow: hidden;
+ }
+ #content.with-help #help-button {
+ top: 10em; // 77px;
+ }
+ .one-box,
+ .two-boxes,
+ .box,
+ .two-cols .col {
+ width: 96%;
+ margin-left: 0;
+ margin-right: 0;
+ }
+}
+
+@media screen and (max-width: $xs-screen) {
+ #header h1,
+ #header h1 a {
+ width: 42px !important;
+ height: 42px;
+ }
+ #wrapper,
+ #main,
+ #main-menu {
+ display: block;
+ float: none;
+ width: 100%;
+ margin: 0;
+ }
+ #main-menu {
+ a {
+ display: block;
+ width: 100%;
+ }
+ h3 a {
+ display: inline;
+ }
+ }
+ #content,
+ .hide-mm #content {
+ margin: 0;
+ }
+ #collapser {
+ display: none;
+ }
+ #main #content > h2 {
+ margin: 0 -.25em 1em;
+ }
+ #dashboard-boxes .box.medium,
+ .box.medium,
+ #dashboard-boxes .box.small,
+ .box.small,
+ #dashboard-boxes .box.large,
+ .box.large {
+ width: 95%;
+ margin: 10px auto;
+ }
+ .cell,
+ #filters-form .cell {
+ display: inline-block;
+ vertical-align: bottom;
+ }
+ .pseudo-tabs li {
+ display: block;
+ float: left;
+ width: 50%;
+ }
+}
+
+@media screen and (max-width: $xxs-screen) {
+ #top-info-blog label,
+ .nomobile {
+ display: none;
+ }
+ #top-info-blog {
+ margin-bottom: .5em;
+ max-width: 75%;
+ select {
+ margin-bottom: .5em;
+ }
+ }
+ #content.with-help {
+ #help-button {
+ top: 10em; // 120px;
+ right: 28rem; // 20.5em;
+ }
+ #help {
+ width: 28rem;
+ }
+ }
+ p.top-add {
+ margin-bottom: .5em;
+ }
+ .part-tabs ul {
+ margin: 1em 0;
+ }
+ .part-tabs li a {
+ display: block;
+ width: 100%;
+ }
+ #icons p {
+ width: 9em;
+ }
+ .media-item {
+ width: 90%;
+ }
+ #theme-new,
+ #theme-activate,
+ #theme-deactivate {
+ margin-left: 0;
+ margin-right: 0;
+ }
+ .box.current-theme {
+ margin: 5px;
+ width: 100%;
+ }
+ .current-theme .module-sshot img {
+ margin: 0;
+ float: none;
+ max-width: 100%;
+ }
+ table .maximal {
+ min-width: 14em;
+ }
+ .pseudo-tabs li {
+ display: block;
+ width: 100%;
+ float: none;
+ }
+}
diff --git a/dotclear._no/admin/style/scss/partials/_main-menu.scss b/dotclear._no/admin/style/scss/partials/_main-menu.scss
new file mode 100644
index 0000000..e9b9fe9
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_main-menu.scss
@@ -0,0 +1,82 @@
+#main-menu {
+ div:last-child {
+ border-bottom: none;
+ }
+ h3 {
+ margin: 0;
+ padding: 10px 0 10px 8px;
+ color: $main-menu-title-color;
+ font-size: 1.15em;
+ }
+ a {
+ color: $main-menu-item-color;
+ border-bottom-color: $main-menu-item-border;
+ }
+ ul {
+ margin: 0 0 1.5em 0;
+ padding: 0;
+ list-style: none;
+ li {
+ display: block;
+ margin: 0.5em 0 0;
+ padding: 4px 0 1px 32px;
+ background-repeat: no-repeat;
+ background-position: 8px .3em;
+ position: relative;
+ a::after {
+ position: absolute;
+ content: "";
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ }
+ &:hover {
+ background-color: $main-menu-active-background;
+ }
+ &:first-child {
+ margin-top: 0;
+ }
+ }
+ }
+ .active {
+ background-color: $main-menu-active-background;
+ font-weight: bold;
+ a {
+ border-bottom: none;
+ color: $main-menu-active-color;
+ }
+ }
+}
+
+#favorites-menu,
+#blog-menu,
+#system-menu,
+#plugins-menu {
+ border-bottom: 1px dashed $main-menu-border;
+}
+
+#favorites-menu {
+ h3 {
+ font-variant: small-caps;
+ padding-top: .2em;
+ }
+}
+
+#search-menu {
+ padding: 4px 0 0 4px;
+ font-size: .91em;
+ p {
+ width: 95%;
+ margin: 0 0 .5em 0;
+ }
+ input[type="submit"] {
+ float: right;
+ }
+}
+
+#qx {
+ width: 75%;
+ background: transparent url(search.svg) no-repeat 0 center;
+ text-indent: 20px;
+}
diff --git a/dotclear._no/admin/style/scss/partials/_markup.scss b/dotclear._no/admin/style/scss/partials/_markup.scss
new file mode 100644
index 0000000..64ba678
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_markup.scss
@@ -0,0 +1,222 @@
+/* ------------------------------------------------------------------ titres */
+
+
+/* fil d'ariane */
+
+#content > h2 {
+ padding: 0 1em .5em 1em;
+ margin: 0 -1em 1em -1em;
+ background: $breadcrumb-background;
+ border-bottom: 1px solid $breadcrumb-border;
+}
+
+/* page courante dans le fil d'ariane */
+
+.page-title {
+ color: $breadcrumb-current;
+ img {
+ padding-left: .5em;
+ vertical-align: middle;
+ }
+}
+
+
+/* autres titres */
+
+#main-menu h3 {
+ font-weight: bold;
+}
+
+.fieldset h3,
+.fieldset h4,
+.pretty-title {
+ color: $fieldset-pretty-title-color;
+ font-size: 1em;
+ font-weight: bold;
+}
+
+.fieldset h3 {
+ font-size: 1.17em;
+}
+
+.fieldset h3.smart-title,
+.fieldset h4.smart-title,
+.smart-title {
+ font-size: 1em;
+ text-transform: uppercase;
+ font-weight: bold;
+ color: $fieldset-smart-title-color;
+}
+
+#entry-sidebar h5 {
+ font-weight: normal;
+ color: $entry-sidebar-title-color;
+}
+
+.entry-status img.img_select_option {
+ padding-left: 4px;
+ vertical-align: -1px;
+}
+
+h4 label,
+h5 label {
+ color: $title-label-color;
+}
+
+h2:first-child,
+h3:first-child,
+h4:first-child,
+h5:first-child,
+ul:first-child,
+p:first-child {
+ margin-top: 0;
+}
+
+
+/* ---------------------------------------------------------------- tableaux */
+
+
+/* Pour autoriser le scroll sur les petites largeurs
+ envelopper les tableaux dans une div.table-outer */
+
+.table-outer {
+ width: 100%;
+ overflow: auto;
+}
+
+table {
+ font-size: 1em;
+ border-collapse: collapse;
+ margin: 0 0 1em 0;
+ width: 100%;
+}
+
+caption {
+ color: $table-caption-color;
+ font-weight: bold;
+ text-align: left;
+ margin-bottom: .5em;
+}
+
+th:not(.module-name) {
+ border-width: 1px 0 1px 0;
+ border-style: solid;
+ border-color: $head-border;
+ background: $head-background;
+ padding: .4em 1em .4em .5em;
+ vertical-align: top;
+ text-align: left;
+}
+
+td, th.module-name {
+ font-weight: normal;
+ border-width: 0 0 1px 0;
+ border-style: solid;
+ border-color: $cell-border;
+ padding: .4em 1em .4em .5em;
+ vertical-align: top;
+}
+
+
+/* ---------------------------------------------------------- autres balises */
+
+p {
+ margin: 0 0 1em 0;
+}
+
+hr {
+ height: 1px;
+ border-width: 1px 0 0;
+ border-color: $hr-color;
+ background: $hr-color;
+ border-style: solid;
+ &.clearer {
+ clear: both;
+ }
+}
+
+pre,
+code,
+#debug {
+ font: 100% $monospace;
+}
+
+code {
+ color: $code-color;
+ background: $code-background;
+}
+
+pre {
+ white-space: pre;
+ white-space: -moz-pre-wrap;
+ white-space: pre-wrap;
+ white-space: pre-line;
+ word-wrap: break-word;
+}
+
+abbr {
+ cursor: help;
+}
+
+input,
+textarea,
+select,
+option,
+optgroup,
+legend,
+label {
+ font-size: 1em;
+}
+
+
+/* ------------------------------------------------------------------ liens */
+
+a,
+a:link,
+a:visited {
+ color: $link-color;
+ text-decoration: none;
+ border-bottom: 1px dotted $link-border;
+ background-color: inherit;
+ outline: 0;
+ img {
+ border: none;
+ background: initial;
+ }
+}
+
+a:hover,
+a:active {
+ border-bottom-style: solid;
+}
+
+h1 a:link,
+h1 a:visited {
+ border: none;
+}
+
+.discrete a {
+ color: $discrete-link-color;
+}
+
+a:link {
+ transition: .5s;
+}
+
+a:focus,
+a:focus img {
+ outline: 2px solid $link-outline;
+ border-bottom: none;
+ text-decoration: none;
+}
+
+a.outgoing img, .outgoing-js {
+ width: .75em;
+ filter: contrast($outgoing-link-filter);
+ #header & {
+ width: 1.25em;
+ padding: 0 0 0 .5em;
+ vertical-align: initial;
+ filter: contrast(100%);
+ }
+}
diff --git a/dotclear._no/admin/style/scss/partials/_media.scss b/dotclear._no/admin/style/scss/partials/_media.scss
new file mode 100644
index 0000000..36a5a18
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_media.scss
@@ -0,0 +1,287 @@
+.media-file-mode {
+ a {
+ border-bottom: none;
+ }
+ img {
+ margin-right: 1em;
+ }
+}
+
+span.media-file-mode {
+ margin-right: 1em;
+}
+
+.media-item {
+ position: relative;
+ border: 1px solid $media-item-border;
+ margin: 9px;
+ padding: 10px 12px 6px;
+ width: 320px;
+ display: inline-block;
+ vertical-align: top;
+ min-height: 140px;
+ word-wrap: break-word;
+ p {
+ margin: 0 0 .5em;
+ }
+ object {
+ margin-top: .5em;
+ }
+ ul {
+ display: block;
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ }
+ audio {
+ width: 100%;
+ margin-top: .5em;
+ }
+ &.media-private {
+ border-color: $media-item-border-private;
+ & img.media-private {
+ padding-right: .5em;
+ }
+ }
+}
+tr.media-private img.media-private {
+ padding-right: .5em;
+}
+
+a.media-icon {
+ display: block;
+ border-bottom: none;
+ margin: 0 auto;
+}
+
+.media-icon img {
+ display: block;
+}
+
+a.media-flag {
+ border-bottom: none;
+}
+
+.media-flag img {
+ float: left;
+ margin-right: .5em;
+}
+
+.media-link {
+ font-size: 1.1em;
+}
+
+.media-action-box {
+ position: relative;
+ margin: 3em 3em 1em 1em;
+ display: inline-block;
+ vertical-align: top;
+}
+
+li.media-action {
+ display: block;
+ position: absolute;
+ top: 4px;
+ right: 8px;
+ height: 16px;
+ a {
+ border: none;
+ &.attach-media {
+ margin-right: 5px;
+ }
+ }
+ form {
+ display: inline;
+ }
+ input {
+ border: none;
+ }
+}
+
+#entry-sidebar .media-item {
+ width: 100%;
+ min-height: 0;
+ padding: 4px;
+ margin: .33em 0;
+}
+
+.folders-group .media-item {
+ min-height: 70px;
+ p {
+ margin-bottom: 0;
+ }
+}
+
+.media-folder {
+ background: $media-folder-background;
+ border-color: $media-folder-border;
+ border-left-width: 8px;
+ .media-link {
+ font-size: 1.125em;
+ margin-left: 2em;
+ color: $media-folder-link;
+ border-bottom: none;
+ }
+}
+
+tr.media-folder {
+ background: $media-folder-background;
+ .media-link {
+ margin-left: 0;
+ }
+}
+
+.media-folder-up {
+ border-color: $media-folderup-border;
+ padding-bottom: 6px;
+}
+
+.medias-delete,
+.medias-select {
+ text-align: right;
+}
+
+.media-recent {
+ float: left;
+ margin-right: 2em;
+}
+
+#media-fav-dir {
+ border-bottom: none;
+ img {
+ vertical-align: middle;
+ }
+}
+
+
+/* upload multiple */
+
+.enhanced_uploader {
+ .choose_files,
+ .cancel,
+ .clean,
+ .start {
+ margin-right: .4em;
+ }
+ #upfile {
+ visibility: hidden;
+ width: 0;
+ height: 0;
+ margin: 0;
+ opacity: 0;
+ filter: alpha(opacity=0);
+ cursor: pointer;
+ }
+ .button.choose_files {
+ display: inline-block;
+ }
+ .max-size {
+ display: block;
+ }
+ .one-file {
+ display: none;
+ }
+ p.clear {
+ padding-top: 1em;
+ margin-bottom: 1em;
+ }
+}
+
+.button.clean,
+.button.cancel,
+.button.choose_files {
+ display: none;
+}
+
+label span.one-file {
+ display: inline;
+}
+
+#add-file-f p.clear {
+ margin-top: 1em;
+ margin-bottom: 0;
+ clear: both;
+}
+
+.files {
+ list-style-type: none;
+ margin-left: 0;
+ padding-left: 0;
+ border-bottom: 1px solid $media-files-border;
+ li {
+ margin-left: 0;
+ padding-left: 0;
+ }
+}
+
+.upload-msg {
+ font-weight: bold;
+ &.upload-error {
+ color: $upload-error-color;
+ }
+}
+
+.upload-files {
+ padding: 0 0.5em;
+ margin: 1em 0;
+}
+
+.upload-file {
+ margin: 0;
+ padding: .3em 0;
+ border-top: 1px solid $upload-file-border;
+ position: relative;
+}
+
+.upload-fileinfo {
+ margin-left: 0;
+ input {
+ position: absolute;
+ top: .5em;
+ right: .5em;
+ }
+ span {
+ padding-right: 8px;
+ }
+ .upload-filecancel {
+ display: block;
+ padding-right: 0;
+ margin-top: 3px;
+ width: 20px;
+ height: 20px;
+ background: transparent url("cancel.png") no-repeat left top;
+ text-indent: -1000px;
+ cursor: pointer;
+ float: left;
+ }
+}
+
+.upload-filemsg {
+ font-weight: bold;
+ color: $upload-filemsg-color;
+ &.upload-error {
+ color: $upload-error-color;
+ }
+}
+
+.upload-progress {
+ padding: .3em 0;
+ div {
+ width: 0;
+ height: 1.2em;
+ font-weight: bold;
+ line-height: 1.2em;
+ text-align: right;
+ background: $upload-progress-background url(loader.png) repeat-x left top;
+ color: $upload-progress-color;
+ border-radius: 3px;
+ }
+}
+
+div.template-upload {
+ clear: both;
+}
+
+.queue-message {
+ font-weight: bold;
+}
diff --git a/dotclear._no/admin/style/scss/partials/_media_item.scss b/dotclear._no/admin/style/scss/partials/_media_item.scss
new file mode 100644
index 0000000..f3ba56e
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_media_item.scss
@@ -0,0 +1,41 @@
+#media-icon {
+ float: left;
+}
+
+.near-icon {
+ margin-left: 70px;
+ margin-bottom: 3em;
+}
+
+#media-details {
+ ul {
+ display: block;
+ margin-left: 0;
+ padding: 0;
+ }
+ li {
+ list-style: square inside;
+ margin: 0;
+ padding: 0;
+ }
+}
+
+#media-original-image {
+ overflow: auto;
+ &.overheight {
+ height: 500px;
+ }
+}
+
+#media-attribute {
+ margin-left: .5em;
+ padding: .5em;
+ border-left: 1px solid currentColor;
+ overflow-wrap: break-word;
+ .media-title {
+ font-weight: bolder;
+ }
+ .media-desc {
+ font-style: italic;
+ }
+}
diff --git a/dotclear._no/admin/style/scss/partials/_mediaqueries.scss b/dotclear._no/admin/style/scss/partials/_mediaqueries.scss
new file mode 100644
index 0000000..f6863f6
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_mediaqueries.scss
@@ -0,0 +1,105 @@
+/* ------------------------------------------------------------------------------------
+ UN POIL DE MEDIA QUERIES
+------------------------------------------------------------------------------------ */
+
+@media screen and (max-width: $xl-screen) {
+ #header,
+ h1 {
+ background: $header-background-alt;
+ }
+ #top-info-user {
+ background: $header-background;
+ a.active {
+ color: $header-active-color-alt;
+ background: $header-active-background-alt;
+ }
+ }
+ #wrapper {
+ background: $wrapper-background;
+ }
+ .modules {
+ th.module-desc,
+ td.module-desc {
+ display: none; // Module description is also in title of the raw
+ }
+ }
+}
+
+@media screen and (max-width: $s-screen) {
+ #help-button {
+ background-color: $header-border;
+ padding: 0;
+ font-size: .83em;
+ line-height: 68px;
+ }
+}
+
+@media screen and (max-width: $xs-screen) {
+ h1 a {
+ &:link {
+ border-right: 1px solid $tabs-border;
+ }
+ &:hover,
+ &:focus {
+ border-right: 1px solid $tabs-border;
+ }
+ }
+ #dashboard-main {
+ padding: 0;
+ }
+ #content,
+ .hide-mm #content {
+ padding: 0 .5em !important;
+ }
+ #main #content > h2 {
+ padding: 6px 30px 4px .5em;
+ }
+ .cell,
+ #filters-form .cell {
+ border: none;
+ padding: 1em;
+ }
+ .pseudo-tabs li {
+ border-top: 1px solid $tabs-border;
+ padding: .25em;
+ &:first-child,
+ &:nth-of-type(2) {
+ border-top: none;
+ }
+ }
+}
+
+@media screen and (max-width: $xxs-screen) {
+ h1,
+ h1 a {
+ border-right: $header-border-alt !important;
+ }
+ #content.with-help #help {
+ font-size: 1.2rem // 12px;
+ }
+ p.top-add {
+ text-align: center;
+ }
+ .multi-part {
+ padding-left: 0;
+ }
+ .part-tabs ul {
+ padding: 0 .5em;
+ }
+ #icons p {
+ padding: 1em .25em;
+ }
+ .box.current-theme {
+ padding: 10px;
+ }
+ th,
+ td {
+ padding: 0.3em 1em 0.3em 0;
+ }
+ .pseudo-tabs li {
+ border-top: 1px solid $tabs-border !important;
+ }
+ .pseudo-tabs li:first-child {
+ border-top: none;
+ }
+}
diff --git a/dotclear._no/admin/style/scss/partials/_messages.scss b/dotclear._no/admin/style/scss/partials/_messages.scss
new file mode 100644
index 0000000..820873b
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_messages.scss
@@ -0,0 +1,202 @@
+.warn,
+.warning,
+.info {
+ font-style: normal;
+ padding: .2em .66em .2em;
+ text-indent: 24px;
+ display: inline-block;
+ line-height: 1.5em;
+ border-radius: 3px;
+}
+
+.info {
+ color: $msg-info-color;
+ background: $msg-info-background url(msg-info.png) no-repeat .3em .3em;
+ border: 1px solid $msg-info-border;
+}
+
+.warn,
+.warning {
+ color: $msg-warn-color;
+ background: $msg-warn-background url(msg-warning.png) no-repeat .3em .3em;
+ border: 1px solid $msg-warn-border;
+}
+
+div.warn,
+div.warning,
+div.info {
+ display: block;
+ padding: 1em 1em .33em 1em;
+ margin-bottom: 1em;
+}
+
+span.warn,
+span.warning,
+span.info {
+ padding-top: 1px;
+ padding-bottom: 1px;
+ background-position: .3em .2em;
+}
+
+.error,
+.message,
+.static-msg,
+.success,
+.warning-msg {
+ padding: 1em 0.5em 0.5em 48px;
+ margin-bottom: 1em;
+ border-radius: 8px;
+ box-shadow: 1px 1px 2px $msg-shadow;
+}
+
+p.error,
+p.message,
+p.static-msg,
+p.success,
+p.warning-msg {
+ padding-top: 1em;
+ padding-bottom: 1em;
+ margin-top: .5em;
+}
+
+.error {
+ background: $msg-error-background url(msg-error.png) no-repeat .7em .7em;
+ color: $msg-error-color;
+ animation-name: kf-error;
+ animation-duration: .5s;
+}
+@keyframes kf-error {
+ 0% {
+ background-color: $msg-error-background-alt;
+ }
+ 100% {
+ background-color: $msg-error-background;
+ }
+}
+
+.message,
+.static-msg {
+ color: $msg-color;
+ background: $msg-background url(msg-std.png) no-repeat .7em .7em;
+}
+.message {
+ animation-name: kf-message;
+ animation-duration: .5s;
+}
+@keyframes kf-message {
+ 0% {
+ background-color: $msg-background-alt;
+ }
+ 100% {
+ background-color: $msg-background;
+ }
+}
+
+.message a,
+.static-msg a,
+.message h3,
+.static-msg h3 {
+ color: $msg-color;
+}
+
+.success {
+ color: $msg-success-color;
+}
+
+.success {
+ background: $msg-success-background url(msg-success.png) no-repeat .7em .7em;
+ animation-name: kf-success;
+ animation-duration: .5s;
+}
+@keyframes kf-success {
+ 0% {
+ background-color: $msg-success-background-alt;
+ }
+ 100% {
+ background-color: $msg-success-background;
+ }
+}
+
+.warning-msg {
+ color: $msg-warn-color;
+ background: $msg-warn-background-alt url(msg-warning.png) no-repeat .7em .7em;
+ border: 1px solid $msg-warn-border;
+ animation-name: kf-warning;
+ animation-duration: .5s;
+}
+@keyframes kf-warning {
+ 0% {
+ background-color: $msg-warn-background;
+ }
+ 100% {
+ background-color: $msg-warn-background-alt;
+ }
+}
+
+.success a,
+.warning-msg a,
+.info a {
+ color: $msg-info-color;
+}
+
+.close-notice-parent {
+ display: flex;
+ justify-content: space-between;
+ ul, p + p {
+ flex: 1;
+ }
+ p + p {
+ padding-left: .25em;
+ }
+}
+.close-notice {
+ background: none;
+ border: none;
+ border-radius: 0;
+ box-shadow: none;
+ padding-left: 1em;
+ &:hover, &:focus {
+ background: none;
+ }
+}
+
+.dc-update {
+ padding: 1em 48px 0.5em 48px;
+ margin-bottom: 1em;
+ border-radius: 8px;
+ color: $dc-update-color;
+ background: $dc-update-background url(msg-success.png) no-repeat .7em .7em;
+ box-shadow: 1px 1px 2px $msg-shadow;
+ h3 {
+ margin-top: 0;
+ color: $dc-update-color;
+ }
+ p {
+ display: inline-block;
+ vertical-align: middle;
+ }
+ a {
+ color: $dc-update-color;
+ margin-right: 1em;
+ &.button {
+ padding: .5em 1em;
+ }
+ }
+}
+
+.updt-info a {
+ margin-left: 2em;
+ border-color: $dc-update-color;
+ font-weight: bold;
+}
+
+body.ajax-loader {
+ #header {
+ border-bottom-color: $ajax-loader;
+ transition: border-bottom-color .3s ease;
+ }
+ #collapser {
+ background-color: $ajax-loader;
+ transition: background-color .3s ease;
+ }
+}
diff --git a/dotclear._no/admin/style/scss/partials/_plugins.scss b/dotclear._no/admin/style/scss/partials/_plugins.scss
new file mode 100644
index 0000000..c357417
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_plugins.scss
@@ -0,0 +1,78 @@
+.modules td {
+ &.module-actions,
+ &.module-icon {
+ vertical-align: middle;
+ }
+ &.module-icon img {
+ &:last-child {
+ width: 16px;
+ height: 16px;
+ }
+ &.expand {
+ margin-bottom: 3px;
+ }
+ }
+ &.module-distrib img {
+ display: block;
+ float: right;
+ }
+}
+
+.modules tr.expand,
+.modules td.expand {
+ background: $modules-background;
+ border-color: $modules-border;
+}
+
+.modules tr.expand td:first-child {
+ font-weight: bold;
+ background: $modules-background;
+}
+
+.modules td.expand {
+ padding: 0 0 1em;
+ div {
+ display: inline-block;
+ vertical-align: top;
+ margin-right: 3em;
+ }
+}
+
+.modules dt {
+ font-weight: bold;
+}
+
+.modules a {
+ &.module-details {
+ background: transparent url(search.svg) no-repeat 0 center;
+ padding: 4px 4px 0 20px;
+ }
+ &.module-support {
+ background: transparent url(help12.png) no-repeat 2px center;
+ padding: 4px 4px 0 20px;
+ }
+ &.module-config {
+ background: transparent url(settings.png) no-repeat 2px 6px;
+ padding: 4px 4px 0 18px;
+ }
+}
+
+#m_search {
+ color: $search-color;
+ background: transparent url(search.svg) no-repeat 0 center;
+ padding-left: 20px;
+}
+
+.mod-more {
+ padding-top: .5em;
+ &,
+ li {
+ margin: .25em 0 0 1em;
+ padding: 0;
+ list-style-type: none;
+ }
+}
+
+#plugin-update td {
+ vertical-align: baseline;
+}
diff --git a/dotclear._no/admin/style/scss/partials/_preferences.scss b/dotclear._no/admin/style/scss/partials/_preferences.scss
new file mode 100644
index 0000000..404c757
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_preferences.scss
@@ -0,0 +1,78 @@
+.fav-list {
+ list-style-type: none;
+ margin-left: 0;
+ padding-left: 0;
+ #my-favs & {
+ border-top: 1px solid $fav-list-border;
+ }
+ li {
+ margin-left: 0;
+ padding-left: 0;
+ padding-top: 3px;
+ padding-bottom: 3px;
+ position: relative;
+ #my-favs & {
+ line-height: 2;
+ border-bottom: 1px solid $fav-list-border;
+ padding-top: 3px;
+ padding-bottom: 3px;
+ position: relative;
+ }
+ #my-favs:focus-within & {
+ border-bottom-color: $fav-list-border-alt;
+ }
+ span.zoom {
+ display: none;
+ }
+ &:hover span.zoom {
+ display: block;
+ position: absolute;
+ bottom: 0;
+ left: 10em;
+ background-color: $fav-list-background-over;
+ border: 1px solid $fav-list-border-alt;
+ padding: .2em;
+ border-radius: .5em;
+ }
+ }
+ img {
+ vertical-align: middle;
+ margin-right: .2em;
+ }
+}
+
+#my-favs {
+ border-color: $my-favs-border;
+ input.position {
+ margin: 0 0 .4em .2em;
+ }
+
+}
+
+#available-favs {
+ input,
+ label,
+ label span {
+ white-space: normal;
+ display: inline;
+ }
+ label span.zoom {
+ display: none;
+ }
+ li:hover label span.zoom {
+ display: block;
+ position: absolute;
+ bottom: 0;
+ left: 10em;
+ background-color: $fav-list-background;
+ border: 1px solid $fav-list-border-alt;
+ padding: .2em;
+ border-radius: .5em;
+ }
+}
+
+#user-options label.ib {
+ display: inline-block;
+ width: 14em;
+ padding-right: 1em;
+}
diff --git a/dotclear._no/admin/style/scss/partials/_tables.scss b/dotclear._no/admin/style/scss/partials/_tables.scss
new file mode 100644
index 0000000..a3547a9
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_tables.scss
@@ -0,0 +1,158 @@
+table {
+ .maximal {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ max-width: 1px;
+ }
+ .maximal, &.maximal {
+ width: 100%;
+ }
+ .minimal {
+ width: 1px;
+ }
+ .nowrap {
+ white-space: nowrap;
+ vertical-align: top;
+ }
+ .count {
+ text-align: right;
+ padding-right: 1.5em;
+ }
+}
+th.first input {
+ padding-right: 34px;
+}
+th, tr.line {
+ img {
+ vertical-align: middle;
+ &.expand {
+ margin-right: 6px;
+ margin-bottom: -2px;
+ }
+ }
+}
+tr.line {
+ p {
+ margin: 0;
+ }
+ input, select {
+ vertical-align: middle;
+ box-shadow: none;
+ }
+ select {
+ width: 6em;
+ }
+ input[type=text] {
+ // background: $line-input-background;
+ }
+ &:hover {
+ background: $line-background-over;
+ }
+ &:focus-within {
+ background-color: $line-focus-background;
+ }
+}
+td.status {
+ vertical-align: middle;
+ a {
+ border: none;
+ }
+}
+.noborder td, td.noborder, .noborder th, th.noborder {
+ border-width: 0 0 1px 0;
+ border-color: $cell-noborder-color;
+ line-height: 2em;
+ padding-bottom: 0;
+}
+.noborder p {
+ margin-bottom: 0;
+}
+table.posts-list {
+ min-width: 50%;
+}
+table.settings, table.prefs {
+ margin-bottom: 3em;
+ th:first-child {
+ width: 20%;
+ }
+ th+th {
+ width: 30%;
+ +th {
+ width: 10%;
+ }
+ }
+ th:last-child {
+ width: 40%;
+ }
+}
+/* js */
+
+td.expand {
+ padding: 1em;
+ td {
+ border-bottom: none;
+ }
+}
+.handle {
+ padding: 0;
+}
+.handler {
+ cursor: move;
+ background: transparent url(drag.png) no-repeat 0 50%;
+ padding-left: 15px;
+}
+/* Responsive Cell Header */
+
+.rch td::before {
+ display: none;
+}
+@media screen and (max-width: $s-screen), print and (max-width: 5in) {
+ table.rch {
+ display: block;
+ caption, tbody, tr, td {
+ display: block;
+ }
+ th, tr:first-of-type {
+ display: none;
+ }
+ td:first-of-type {
+ border-top: 1px solid $gray-lighter;
+ color: $gray-light;
+ background: $gray-darker;
+ }
+ td::before {
+ display: inline;
+ font-weight: bold;
+ }
+ td {
+ display: grid;
+ grid-template-columns: 10em auto;
+ grid-gap: 1em 0.5em;
+ text-align: left;
+ border: none;
+ }
+ .maximal {
+ max-width: inherit;
+ }
+ .nowrap {
+ white-space: inherit;
+ }
+ td.expand {
+ grid-template-columns: auto !important;
+ color: $body-color;
+ background-color: $body-background;
+ border-top: 1px dashed $gray-lighter;
+ }
+ input, select {
+ align-self: center;
+ }
+ }
+ table.rch-thead {
+ thead {
+ display: none;
+ }
+ tr:first-of-type {
+ display: block;
+ }
+ }
+}
diff --git a/dotclear._no/admin/style/scss/partials/_user.scss b/dotclear._no/admin/style/scss/partials/_user.scss
new file mode 100644
index 0000000..b85a46b
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_user.scss
@@ -0,0 +1,18 @@
+.blog-perm {
+ margin-top: 2em;
+ padding-top: 2em;
+ font-weight: bold;
+}
+
+.ul-perm {
+ list-style-type: square;
+ margin-left: 0;
+ padding-left: 3.5em;
+ margin-bottom: 0
+}
+
+.add-perm {
+ padding-top: .5em;
+ padding-left: 2.5em;
+ margin-left: 0;
+}
diff --git a/dotclear._no/admin/style/scss/partials/_utils.scss b/dotclear._no/admin/style/scss/partials/_utils.scss
new file mode 100644
index 0000000..0b2d4f9
--- /dev/null
+++ b/dotclear._no/admin/style/scss/partials/_utils.scss
@@ -0,0 +1,103 @@
+/* ---------------------------------------------- Couleurs ajoutées via javascript
+
+/* Sortable (jQuery UI) */
+
+.sortable-area {
+ border: 1px dashed currentColor;
+ background-color: $main-menu-background;
+}
+
+div.ui-sortable-handle {
+ border: 1px dashed currentColor !important;
+ border-radius: .75em !important;
+ & > .ui-sortable {
+// border-color: $main-menu-border !important;
+ }
+}
+
+/* color-picker.js */
+
+.color-color-picker {
+ border: 1px solid $color-picker-border;
+ width: 195px;
+ background: $color-picker-background;
+}
+
+/* _media_item.js */
+
+.color-div {
+ border: 1px solid $color-div-border;
+}
+
+/* Badges (common.js) */
+
+.badgeable {
+ /* class to set to badge parent's, not mandatory for menu items */
+ position: relative;
+}
+
+/* Badge design */
+.badge {
+ color: $badge-color;
+ background-color: $badge-std-background;
+ border: 1px solid $badge-border;
+ vertical-align: top;
+ border-radius: 1em;
+ padding: 0 .6em;
+}
+.badge-icon {
+ background-color: $badge-soft-background;
+}
+
+/* Badge background override */
+.badge-std {
+ background-color: $badge-std-background;
+}
+.badge-info {
+ background-color: $badge-info-background;
+}
+.badge-soft {
+ background-color: $badge-soft-background;
+}
+
+/* Badge position */
+.badge-block {
+ /* Dashboard module → badge on top right */
+ position: absolute;
+ top: -10px;
+ right: -10px;
+}
+.badge-icon {
+ /* Dashboard icon → badge on top right */
+ right: 20px;
+}
+.badge-inline {
+ /* Menu item */
+ margin-left: .5em;
+}
+.badge-left {
+ right: auto;
+ left: -10px;
+ &.badge-icon {
+ left: 20px;
+ }
+}
+
+/* Badge design option */
+.badge-noborder {
+ border: none;
+}
+.badge-small {
+ font-size: smaller;
+ &.badge-icon {
+ right: 25px;
+ &.badge-left {
+ right: auto;
+ left: 25px;
+ }
+ }
+}
+.badge-square {
+ border-radius: 0;
+}
+
diff --git a/dotclear._no/admin/style/scss/themes/_dark.scss b/dotclear._no/admin/style/scss/themes/_dark.scss
new file mode 100644
index 0000000..4ee014a
--- /dev/null
+++ b/dotclear._no/admin/style/scss/themes/_dark.scss
@@ -0,0 +1,452 @@
+// ========================================================================== //
+// =Couleurs
+// ========================================================================== //
+
+// Palette de base
+
+$blue: #76c2f1;
+$green: #9ac123;
+$red: #c44d58;
+$light-blue: #a2cbe9;
+$orange: #ff6e3a;
+
+$gray: #b2b5ba;
+$gray-dark: #676e78;
+
+$gray-very-dark: shade($gray, 72%); // #323232
+$gray-darker: shade($gray, 50%); // #595b5d
+$gray-semi-dark: shade($gray, 25%); // #868686
+$gray-light: tint($gray, 30%); // #c9c9c9
+$gray-lighter: tint($gray, 75%); // #ececec
+$gray-very-light: tint($gray, 85%); // #f3f3f3
+
+$white: #fff;
+$black: #000;
+
+// Palette secondaire
+
+$warning: #fefacd;
+$warning-alt: #ffd478;
+
+$info: #d9edf7;
+$info-alt: #bce8f1;
+
+$error: #ffbaba;
+$error-alt: #ae323b;
+
+$required: #f783b3;
+
+$success: #bee74b;
+$success-alt: #9bca1c;
+
+$link: #76c2f1;
+$link-outline: #bee74b;
+
+$current-page: #f783b3;
+
+$legacy: #eef1ec;
+$code: #fefacd;
+$tooltip: #910ed3;
+
+$pw-very-weak: #c44d58;
+$pw-weak: #e1732c;
+$pw-mediocre: #ff9900;
+$pw-strong: #cdad12;
+$pw-very-strong: #9ac123;
+
+$submit: #25a6e1;
+$submit-alt: #188bc0;
+
+$reset: #eaeaea;
+$reset-alt: #f9f9f9;
+$reset-ter: #dadada;
+
+$delete: #ae323b;
+$delete-alt: #b33630;
+$delete-ter: #dc5f59;
+
+$add: #bee74b;
+$add-alt: #9bca1c;
+
+$badge: #d54e21; /* bright red */
+$badge-soft: #607d8b; /* slategray */
+$badge-info: #3f51b5; /* soft dark blue */
+
+// Common
+
+$body-color: #dcdee0;
+$body-background: #272b30;
+
+$title-color: $gray;
+$title-alt-color: $orange;
+
+// Layout
+
+$header-color: $body-color;
+$header-background: $body-background;
+$header-background-alt: $gray-darker;
+$header-border: $green;
+$header-border-alt: $gray-very-dark;
+$header-link-border: $gray-semi-dark;
+$header-active-color: $white;
+$header-active-background: $gray-semi-dark;
+$header-active-color-alt: $white;
+$header-active-background-alt: $gray-semi-dark;
+
+$wrapper-background: $body-background;
+
+$content-background: $body-background;
+
+$tabs-color: $gray-very-dark;
+$tabs-border: $gray-lighter;
+$tabs-background: $gray-light;
+$tabs-active-color: $white;
+$tabs-active-background: $gray-semi-dark;
+
+$main-menu-background: shade($gray-darker, 15%);
+$main-menu-title-color: $gray-light;
+$main-menu-item-color: $body-color;
+$main-menu-item-border: tint($gray-dark, 66%);
+$main-menu-active-color: $white;
+$main-menu-active-background: $gray-semi-dark;
+$main-menu-border: $light-blue;
+
+$search-color: $white;
+$search-border: $gray-light;
+$search-background: $submit;
+$search-submit-color: $white;
+$search-submit-background: $submit-alt;
+$search-outline: $link-outline;
+
+$footer-background: $body-background;
+$footer-border: tint($gray-dark, 66%);
+
+$collapser-focus: $green;
+$collapser-background: $gray-darker;
+
+$popup-title-color: $body-color;
+$popup-title-background: $gray-darker;
+
+// Markup
+
+$breadcrumb-background: $body-background;
+$breadcrumb-border: $gray-semi-dark;
+$breadcrumb-current: $current-page;
+
+$fieldset-pretty-title-color: $orange;
+$fieldset-smart-title-color: $gray-very-light;
+$entry-sidebar-title-color: $gray-lighter;
+$title-label-color: $gray-lighter;
+
+$table-caption-color: $body-color;
+$head-border: shade($gray-very-light, 10%);
+$head-background: $gray-darker;
+$line-input-background: $gray-light;
+$line-background-over: shade($gray-darker, 15%);
+$line-focus-background: shade($gray-darker, 15%);
+$cell-border: $gray-lighter;
+$cell-noborder-color: shade($gray-very-light, 10%);
+
+$hr-color: $gray-light;
+
+$code-color: $body-background;
+$code-background: $code;
+
+$link-color: $link;
+$link-border: $gray-semi-dark;
+$discrete-link-color: $body-color;
+
+$outgoing-link-filter: 100%;
+
+// Forms
+
+$fieldset-border: tint($gray-dark, 66%);
+$fieldset-background: $body-background;
+$fieldset-focus-background: tint($body-background, 7%);
+
+$legend-border: tint($gray-dark, 66%);
+$legend-background: $gray-darker;
+
+$invalid-input-color: $black;
+$invalid-input-border: $error-alt;
+$invalid-input-background: $error;
+$invalid-input-shadow: rgba($error-alt, 0.3);
+
+$input-color: $black;
+$input-background: $gray-lighter;
+$input-border: tint($gray-dark, 10%);
+$input-shadow: $gray-lighter;
+$input-focus: $link-outline;
+
+$input-file-color: $body-color;
+$input-file-background: $body-background;
+
+$form-note-color: $info-alt;
+
+$error-background: $error;
+
+// Buttons
+
+$button-color: $black;
+$button-border: $gray-semi-dark;
+$button-shadow: rgba($black, 0.05);
+
+$submit-color: $white;
+$submit-background: $submit;
+$submit-background-alt: $submit-alt;
+$submit-border: $gray-semi-dark;
+
+$reset-color: $black;
+$reset-background: $reset;
+$reset-background-alt: $reset-alt;
+$reset-background-ter: $reset-ter;
+$reset-border: $gray-semi-dark;
+
+$delete-color: $delete;
+$delete-hover-color: $white;
+$delete-hover-background: $delete-alt;
+$delete-hover-background-alt: $delete-ter;
+$delete-hover-border: $delete-ter;
+
+$add-color: $black;
+$add-background: $add;
+$add-background-alt: $add-alt;
+$add-border: $add;
+$add-border-alt: $add-alt;
+
+$button-disabled-color: $gray-dark;
+$button-disabled-background: $gray-very-light;
+$button-disabled-background-alt: $gray-lighter;
+$button-disabled-border: $gray-semi-dark;
+
+$ajax-color: $gray-very-dark;
+$ajax-background: $white;
+$ajax-background-alt: $light-blue;
+$ajax-border: $gray-dark;
+
+$gotop-color: $gray-very-dark;
+$gotop-background: transparent;
+$gotop-border: none;
+
+$addmeta-color: $white;
+$addmeta-background: transparent;
+$addmeta-focus-color: $black;
+$addmeta-focus-background: tint($light-blue, 10%);
+
+// Messages
+
+$msg-color: $white;
+$msg-background: $gray-dark;
+$msg-background-alt: tint($gray-dark, 66%);
+$msg-shadow: rgba($gray-very-dark, 0.1);
+
+$msg-info-color: $gray-very-dark;
+$msg-info-background: $info;
+$msg-info-border: $info-alt;
+
+$msg-warn-color: $gray-very-dark;
+$msg-warn-background: $warning;
+$msg-warn-background-alt: $warning-alt;
+$msg-warn-border: $warning-alt;
+
+$msg-error-color: $black;
+$msg-error-background: $error;
+$msg-error-background-alt: $warning;
+
+$msg-success-color: $black;
+$msg-success-background: $success;
+$msg-success-background-alt: $success-alt;
+
+$dc-update-color: $black;
+$dc-update-background: $light-blue;
+
+$ajax-loader: shade($light-blue, 10%);
+
+// Filters
+
+$form-control-color: $white;
+
+$filter-border: $light-blue;
+$filter-options-border: $gray-light;
+$filter-focus-background: shade($gray-darker, 15%);
+
+// Prelude
+
+$prelude-color: $gray-very-dark;
+$prelude-background: $light-blue;
+$prelude-background-alt: $white;
+
+// Help
+
+$help-background: $gray-very-dark;
+$help-border: $warning-alt;
+$help-button-border: $gray-dark;
+$help-button-background: $gray-very-dark;
+$help-title-color: $light-blue;
+
+// Tooltip (in footer)
+
+$tooltip-color: $tooltip;
+$tooltip-background: $gray-very-light;
+
+// Application pages:
+// - Auth (white background page)
+
+$login-fieldset-background: $white;
+$login-fieldset-border: $success-alt;
+
+// - Index
+
+$index-link-color: $gray-lighter;
+$index-link-border: $gray-semi-dark;
+$index-icon-background: $gray-darker;
+$index-icon-border: $gray-lighter;
+$index-icon-outline: $link-outline;
+$index-icon-contrast: 130%;
+
+$quick-background: $gray-very-dark;
+$quick-border: shade($gray-very-light, 10%);
+$quick-info-background: $info;
+$quick-info-border: $info-alt;
+$quick-info-color: $gray-very-dark;
+
+$index-box-border: tint($gray-dark, 66%);
+
+$drag-n-drop-off: $gray-semi-dark;
+$drag-n-drop-on: $success;
+
+// - Blog params
+
+$blog-user-border: tint($gray-dark, 66%);
+$blog-super-background: rgba($info, 0.15);
+$blog-super-border: $link-outline;
+
+// - Blog theme
+
+$theme-box-border: shade($gray-very-light, 10%);
+$theme-box-over: $gray-darker;
+
+$theme-name-color: $gray-light;
+
+$theme-img-background: $gray-very-light;
+$theme-img-border: $white;
+$theme-img-shadow: rgba($gray-very-dark, 0.1);
+
+$theme-action-background: $gray-darker;
+$theme-action-border: transparent;
+
+$theme-current-background: $gray-darker;
+$theme-current-border: $gray-lighter;
+$theme-current-shadow: rgba($gray-very-dark, 0.1);
+
+$theme-current-img-border: $white;
+$theme-current-name-color: $orange;
+
+// - Categories
+
+$cat-placeholder-outline: $link;
+
+$cat-line-border: tint($gray-dark, 66%);
+$subcat-line-border: shade($gray-very-light, 10%);
+
+$cat-post-counter: $gray-very-light;
+
+// - Media
+
+$media-folder-background: transparent;
+$media-folder-border: $gray-lighter;
+$media-folderup-border: transparent;
+$media-folder-link: $gray-light;
+$media-item-border: shade($gray-very-light, 10%);
+$media-item-border-private: $red;
+$media-files-border: shade($gray-very-light, 10%);
+
+$upload-color: shade($success-alt, 45%);
+$upload-filemsg-color: $white;
+$upload-progress-color: $white;
+$upload-progress-background: shade($success-alt, 45%);
+$upload-file-border: shade($gray-very-light, 10%);
+$upload-error-color: $error-alt;
+
+// - Plugins
+
+$modules-background: $gray-very-dark;
+$modules-border: $light-blue;
+
+// - Entry
+
+$entry-sb-title-border: $white;
+$entry-sb-background: shade($gray-darker, 15%);
+$entry-sb-focus-background: $gray-darker;
+
+// - Preferences
+
+$fav-list-background: $gray-very-dark;
+$fav-list-background-over: $gray-darker;
+$fav-list-border: $gray-lighter;
+$fav-list-border-alt: shade($gray-very-light, 10%);
+
+$my-favs-border: $green;
+
+// - Charte
+
+$charte-title: $orange;
+$charte-one-box-border: shade($gray-very-light, 10%);
+
+// Classes
+
+$ac-results-background: $gray-very-dark;
+$ac-results-border: $warning-alt;
+$ac-results-over: $white;
+$ac-results-over-background: shade($link, 20%);
+
+$nav-background: $body-background;
+$nav-prevnext: $link;
+$nav-prevnext-background: $gray-very-dark;
+$nav-prevnext-border: shade($gray-very-light, 10%);
+
+$onblog-link: $gray-very-dark;
+$onblog-link-background: $gray-lighter;
+$onblog-link-border: $gray-lighter;
+$onblog-link-shadow: rgba($gray-very-dark, 0.3);
+
+$pager-link: $link;
+$pager-active: $gray-light;
+$pager-background: $gray-very-light;
+$pager-background-over: shade($gray-very-light, 10%);
+$pager-off-background: $white;
+$pager-input-background: $white;
+$pager-border: shade($gray-very-light, 10%);
+
+$index-no-link: $gray-light;
+$index-active: $white;
+$index-active-background: $gray-dark;
+
+$step: $gray-dark;
+$step-background: $gray-very-light;
+$step-border: tint($gray-dark, 66%);
+
+$offline: $gray-semi-dark;
+$offline-background: $gray-very-dark;
+
+$mark-attach-img: url('../images/attach.svg');
+$mark-attach-filter: hue-rotate(260deg) saturate(100) saturate(.2) brightness(220%);
+
+// Utils
+
+$color-picker-border: $black;
+$color-picker-background: $white;
+
+$color-div-border: $gray-light;
+
+$badge-color: $white; // #ffffff
+$badge-border: $body-color;
+$badge-std-background: $badge; // #d54e21
+$badge-soft-background: $badge-soft;
+$badge-info-background: $badge-info;
+
+// Debug
+
+$debug-color: $error-alt;
+$debug-background: $warning-alt;
+$debug-border: $warning-alt;
diff --git a/dotclear._no/admin/style/scss/themes/_light.scss b/dotclear._no/admin/style/scss/themes/_light.scss
new file mode 100644
index 0000000..d201af0
--- /dev/null
+++ b/dotclear._no/admin/style/scss/themes/_light.scss
@@ -0,0 +1,452 @@
+// ========================================================================== //
+// =Couleurs
+// ========================================================================== //
+
+// Palette de base
+
+$blue: #137bbb;
+$green: #9ac123;
+$red: #c44d58;
+$light-blue: #a2cbe9;
+$orange: #d33800;
+
+$gray: #b2b2b2;
+$gray-dark: #676e78;
+
+$gray-very-dark: shade($gray, 72%); // #323232 (near to #333 used before)
+$gray-darker: shade($gray, 50%); // #595b5d
+$gray-semi-dark: shade($gray, 25%); // #868686 (near to #999 used before)
+$gray-light: tint($gray, 30%); // #c9c9c9 (near to #ccc used before)
+$gray-lighter: tint($gray, 75%); // #ececec (near to #eee used before)
+$gray-very-light: tint($gray, 85%); // #f3f3f3 (near to #f7f7f7 used before)
+
+$white: #fff;
+$black: #000;
+
+// Palette secondaire
+
+$warning: #fefacd;
+$warning-alt: #ffd478;
+
+$info: #d9edf7;
+$info-alt: #bce8f1;
+
+$error: #ffbaba;
+$error-alt: #ae323b;
+
+$required: #ae323b;
+
+$success: #bee74b;
+$success-alt: #9bca1c;
+
+$link: #2373a8;
+$link-outline: #bee74b;
+
+$current-page: #d30e60;
+
+$legacy: #eef1ec;
+$code: #fefacd;
+$tooltip: #910ed3;
+
+$pw-very-weak: #c44d58;
+$pw-weak: #e1732c;
+$pw-mediocre: #ff9900;
+$pw-strong: #cdad12;
+$pw-very-strong: #9ac123;
+
+$submit: #25a6e1;
+$submit-alt: #188bc0;
+
+$reset: #eaeaea;
+$reset-alt: #f9f9f9;
+$reset-ter: #dadada;
+
+$delete: #ae323b;
+$delete-alt: #b33630;
+$delete-ter: #dc5f59;
+
+$add: #bee74b;
+$add-alt: #9bca1c;
+
+$badge: #d54e21; /* bright red */
+$badge-soft: #607d8b; /* slategray */
+$badge-info: #3f51b5; /* soft dark blue */
+
+// Common
+
+$body-color: $gray-very-dark; // #323232
+$body-background: $white; // #ffffff
+
+$title-color: $gray-dark; // #676e78 : h2, h4, h5, h6
+$title-alt-color: $orange; // #d33800 : h3
+
+// Layout
+
+$header-color: $white; // #ffffff
+$header-background: $gray-dark; // #676e78
+$header-background-alt: $gray-very-dark; // #323232
+$header-border: $light-blue; // #a2cbe9
+$header-border-alt: $gray-very-dark; // #323232
+$header-link-border: $gray-semi-dark; // #868686
+$header-active-color: $orange; // #d33800
+$header-active-background: $gray-very-light; // #f3f3f3
+$header-active-color-alt: $white; // #ffffff
+$header-active-background-alt: $gray-semi-dark; // #868686
+
+$wrapper-background: $white; // #ffffff
+
+$content-background: $white; // #ffffff
+
+$tabs-color: $gray-very-dark; // #323232
+$tabs-border: $gray-light; // #c9c9c9
+$tabs-background: $gray-very-light; // #f3f3f3
+$tabs-active-color: $orange; // #d33800
+$tabs-active-background: $white; // #ffffff
+
+$main-menu-background: $gray-very-light; // #f3f3f3
+$main-menu-title-color: $gray-dark; // #676e78
+$main-menu-item-color: $gray-very-dark; // #323232
+$main-menu-item-border: tint($gray-dark, 66%); // #c5c5c5
+$main-menu-active-color: $current-page; // #d30e60
+$main-menu-active-background: $white; // #ffffff
+$main-menu-border: $light-blue; // #a2cbe9
+
+$search-color: $gray-very-dark; // #323232
+$search-border: $gray-semi-dark; // #868686
+$search-background: $gray-light; // #c9c9c9
+$search-submit-color: $white; // #ffffff
+$search-submit-background: $gray-semi-dark; // #868686
+$search-outline: $link-outline; // #bee74b
+
+$footer-background: $white; // #ffffff
+$footer-border: tint($gray-dark, 66%); // #c5c5c5
+
+$collapser-focus: $light-blue; // #a2cbe9
+$collapser-background: $gray-light; // #c9c9c9
+
+$popup-title-color: $white; // #ffffff
+$popup-title-background: $gray-dark; // #676e78
+
+// Markup
+
+$breadcrumb-background: $white; // #ffffff
+$breadcrumb-border: $gray-semi-dark; // #868686
+$breadcrumb-current: $current-page; // #d30e60
+
+$fieldset-pretty-title-color: $orange; // #d33800
+$fieldset-smart-title-color: $gray-very-dark; // #323232
+$entry-sidebar-title-color: $gray-very-dark; // #323232
+$title-label-color: $gray-very-dark; // #323232
+
+$table-caption-color: $gray-very-dark; // #323232
+$head-border: shade($gray-very-light, 10%); // #dbdbdb
+$head-background: $gray-very-light; // #f3f3f3
+$line-input-background: $white; // #ffffff
+$line-background-over: $gray-very-light; // #f3f3f3 tr:hover
+$line-focus-background: shade($white, 7%); // #ededed
+$cell-border: $gray-lighter; // #ececec
+$cell-noborder-color: shade($gray-very-light, 10%); // #dbdbdb td/th .noborder
+
+$hr-color: shade($gray-very-light, 10%); // #dbdbdb
+
+$code-color: $body-color;
+$code-background: $code; // #fefacd
+
+$link-color: $link; // #2373a8
+$link-border: $gray-semi-dark; // #868686
+$discrete-link-color: $gray-very-dark; // #323232
+
+$outgoing-link-filter: 50%;
+
+// Forms
+
+$fieldset-border: tint($gray-dark, 66%); // #c5c5c5
+$fieldset-background: $white; // #ffffff
+$fieldset-focus-background: shade($white, 7%); // #ededed
+
+$legend-border: $gray-dark; // #676e78
+$legend-background: $white; // #ffffff
+
+$invalid-input-color: $black; // #000000
+$invalid-input-border: $error-alt; // #ae323b
+$invalid-input-background: $error; // #ffbaba
+$invalid-input-shadow: rgba($error-alt, 0.3);
+
+$input-color: $black; // #000000
+$input-background: tint($gray-very-light, 50%); // #f9f9f9
+$input-border: shade($gray-very-light, 10%); // #dbdbdb
+$input-shadow: $gray-very-light; // #f3f3f3
+$input-focus: $link-outline; // #bee74b
+
+$input-file-color: $body-color;
+$input-file-background: $body-background;
+
+$form-note-color: $gray-dark; // #676e78
+
+$error-background: $error; // #ffbaba
+
+// Buttons
+
+$button-color: $black; // #000000
+$button-border: tint($gray-dark, 66%); // #c5c5c5
+$button-shadow: rgba($black, 0.05);
+
+$submit-color: $white; // #ffffff
+$submit-background: $submit; // #25a6e1
+$submit-background-alt: $submit-alt; // #188bc0
+$submit-border: tint($gray-dark, 66%); // #c5c5c5
+
+$reset-color: $black; // #000000
+$reset-background: $reset; // #eaeaea
+$reset-background-alt: $reset-alt; // #f9f9f9
+$reset-background-ter: $reset-ter; // #dadada
+$reset-border: tint($gray-dark, 66%); // #c5c5c5
+
+$delete-color: $delete; // #ae323b
+$delete-hover-color: $white; // #ffffff
+$delete-hover-background: $delete-alt; // #b33630
+$delete-hover-background-alt: $delete-ter; // #dc5f59
+$delete-hover-border: $delete-ter; // #dc5f59
+
+$add-color: $black; // #000000
+$add-background: $add; // #bee74b
+$add-background-alt: $add-alt; // #9bca1c
+$add-border: $add; // #bee74b
+$add-border-alt: $add-alt; // #9bca1c
+
+$button-disabled-color: $gray-dark; // #676e78
+$button-disabled-background: $gray-very-light; // #f3f3f3
+$button-disabled-background-alt: $gray-lighter; // #ececec
+$button-disabled-border: tint($gray-dark, 66%); // #c5c5c5
+
+$ajax-color: $gray-very-dark; // #323232
+$ajax-background: $white; // #ffffff
+$ajax-background-alt: $light-blue; // #a2cbe9
+$ajax-border: $gray-dark; // #676e78
+
+$gotop-color: $gray-very-dark; // #323232
+$gotop-background: transparent;
+$gotop-border: none;
+
+$addmeta-color: $black; // #000000
+$addmeta-background: transparent;
+$addmeta-focus-color: $black; // #000000
+$addmeta-focus-background: tint($light-blue, 10%); // #abd0eb
+
+// Messages
+
+$msg-color: $white; // #ffffff
+$msg-background: $gray-dark; // #676e78
+$msg-background-alt: tint($gray-dark, 66%); // #c5c5c5
+$msg-shadow: rgba($gray-very-dark, 0.1);
+
+$msg-info-color: $gray-very-dark; // #323232
+$msg-info-background: $info; // #d9edf7
+$msg-info-border: $info-alt; // #bce8f1
+
+$msg-warn-color: $gray-very-dark; // #323232
+$msg-warn-background: $warning; // #fefacd
+$msg-warn-background-alt: $warning-alt; // #ffd478
+$msg-warn-border: $warning-alt; // #ffd478
+
+$msg-error-color: $black; // #000000
+$msg-error-background: $error; // #ffbaba
+$msg-error-background-alt: $warning; // #fefacd
+
+$msg-success-color: $black; // #000000
+$msg-success-background: $success; // #bee74b
+$msg-success-background-alt: $success-alt; // #9bca1c
+
+$dc-update-color: $black; // #000000
+$dc-update-background: $light-blue; // #a2cbe9
+
+$ajax-loader: shade($light-blue, 10%); // #92b7d2
+
+// Filters
+
+$form-control-color: $black; // #000000
+
+$filter-border: $light-blue; // #a2cbe9
+$filter-options-border: $gray-light; // #c9c9c9
+$filter-focus-background: shade($white, 7%); // #ededed
+
+// Prelude
+
+$prelude-color: $gray-very-dark; // #323232
+$prelude-background: $light-blue; // #a2cbe9
+$prelude-background-alt: $white; // #ffffff
+
+// Help
+
+$help-background: $gray-very-light; // #f3f3f3
+$help-border: $warning-alt; // #ffd478
+$help-button-border: $gray-light; // #c9c9c9
+$help-button-background: $gray-very-light; // #f3f3f3
+$help-title-color: $gray-dark; // #676e78
+
+// Tooltip (in footer)
+
+$tooltip-color: $tooltip; // #910ed3
+$tooltip-background: rgba($white, 0.9);
+
+// Application pages:
+// - Auth (white background page)
+
+$login-fieldset-background: $white; // #ffffff
+$login-fieldset-border: $success-alt; // #9bca1c
+
+// - Index
+
+$index-link-color: $gray-very-dark; // #323232
+$index-link-border: $gray-semi-dark; // #868686
+$index-icon-background: tint($gray-very-light, 50%); // #f9f9f9
+$index-icon-border: shade($gray-very-light, 10%); // #dbdbdb
+$index-icon-outline: $link-outline; // #bee74b
+$index-icon-contrast: 100%;
+
+$quick-background: $gray-very-light; // #f3f3f3
+$quick-border: shade($gray-very-light, 10%); // #dbdbdb
+$quick-info-background: $info; // #d9edf7
+$quick-info-border: $info-alt; // #bce8f1
+$quick-info-color: $gray-very-dark; // #323232
+
+$index-box-border: tint($gray-dark, 66%); // #c5c5c5
+
+$drag-n-drop-off: $gray-semi-dark;
+$drag-n-drop-on: $blue;
+
+// - Blog params
+
+$blog-user-border: tint($gray-dark, 66%); // #c5c5c5
+$blog-super-background: rgba($info, 0.5); // #d9edf7 - 50%
+$blog-super-border: $link-outline; // #bee74b
+
+// - Blog theme
+
+$theme-box-border: shade($gray-very-light, 10%); // #dbdbdb
+$theme-box-over: $gray-very-light; // #f3f3f3
+
+$theme-name-color: $gray-dark; // #676e78
+
+$theme-img-background: $gray-very-light; // #f3f3f3
+$theme-img-border: $white; // #ffffff
+$theme-img-shadow: rgba($gray-very-dark, 0.1);
+
+$theme-action-background: $gray-very-light; // #f3f3f3
+$theme-action-border: $gray-lighter; // #ececec
+
+$theme-current-background: $gray-very-light; // #f3f3f3
+$theme-current-border: $gray-lighter; // #ececec
+$theme-current-shadow: rgba($gray-very-dark, 0.1);
+
+$theme-current-img-border: $white; // #ffffff
+$theme-current-name-color: $orange; // #d33800
+
+// - Categories
+
+$cat-placeholder-outline: $link; // #2373a8
+
+$cat-line-border: tint($gray-dark, 66%); // #c5c5c5
+$subcat-line-border: shade($gray-very-light, 10%); // #dbdbdb
+
+$cat-post-counter: $gray-very-dark; // #323232
+
+// - Media
+
+$media-folder-background: transparent;
+$media-folder-border: $gray-lighter; // #ececec
+$media-folderup-border: transparent;
+$media-folder-link: $gray-dark; // #676e78
+$media-item-border: shade($gray-very-light, 10%); // #dbdbdb
+$media-item-border-private: $red;
+$media-files-border: shade($gray-very-light, 10%); // #dbdbdb
+
+$upload-color: shade($success-alt, 45%); // #556f0f
+$upload-filemsg-color: $white; // #ffffff
+$upload-progress-color: $white; // #ffffff
+$upload-progress-background: shade($success-alt, 45%); // #556f0f
+$upload-file-border: shade($gray-very-light, 10%); // #dbdbdb
+$upload-error-color: $error-alt; // #ae323b
+
+// - Plugins
+
+$modules-background: $gray-very-light; // #f3f3f3
+$modules-border: $light-blue; // #a2cbe9
+
+// - Entry
+
+$entry-sb-title-border: $gray-dark; // #676e78
+$entry-sb-background: $gray-very-light; // #f3f3f3
+$entry-sb-focus-background: shade($white, 7%); // #ededed
+
+// - Preferences
+
+$fav-list-background: $gray-very-light; // #f3f3f3
+$fav-list-background-over: $gray-very-light; // #f3f3f3
+$fav-list-border: $gray-lighter; // #ececec
+$fav-list-border-alt: shade($gray-very-light, 10%); // #dbdbdb
+
+$my-favs-border: $green; // #9ac123
+
+// - Charte
+
+$charte-title: $orange; // #d33800
+$charte-one-box-border: shade($gray-very-light, 10%); // #dbdbdb
+
+// Classes
+
+$ac-results-background: $white; // #ffffff
+$ac-results-border: $warning-alt; // #ffd478
+$ac-results-over: $white; // #ffffff
+$ac-results-over-background: $link; // #2373a8
+
+$nav-background: $white; // #ffffff
+$nav-prevnext: $link; // #2373a8
+$nav-prevnext-background: $gray-very-light; // #f3f3f3
+$nav-prevnext-border: shade($gray-very-light, 10%); // #dbdbdb
+
+$onblog-link: $gray-very-dark; // #323232
+$onblog-link-background: $gray-lighter; // #ececec
+$onblog-link-border: $gray-lighter; // #ececec
+$onblog-link-shadow: rgba($gray-very-dark, 0.3); // #323232 30%
+
+$pager-link: $link; // #2373a8
+$pager-active: $gray-dark; // #676e78
+$pager-background: $gray-very-light; // #f3f3f3
+$pager-background-over: shade($gray-very-light, 10%); // #dbdbdb
+$pager-off-background: $white; // #ffffff
+$pager-input-background: $white; // #ffffff
+$pager-border: shade($gray-very-light, 10%); // #dbdbdb
+
+$index-no-link: $gray-light; // #c9c9c9
+$index-active: $white; // #ffffff
+$index-active-background: $gray-dark; // #676e78
+
+$step: $gray-dark; // #676e78
+$step-background: $gray-very-light; // #f3f3f3
+$step-border: tint($gray-dark, 66%); // #c5c5c5
+
+$offline: $gray-dark; // #676e78
+$offline-background: $gray-very-light; // #f3f3f3
+
+$mark-attach-img: url('../images/attach.svg');
+$mark-attach-filter: hue-rotate(300deg) saturate(.64);
+
+// Utils
+
+$color-picker-border: $black; // #000000
+$color-picker-background: $white; // #ffffff
+
+$color-div-border: $gray-light; // #c9c9c9
+
+$badge-color: $white; // #ffffff
+$badge-border: transparent;
+$badge-std-background: $badge; // #d54e21
+$badge-soft-background: $badge-soft;
+$badge-info-background: $badge-info;
+
+// Debug
+
+$debug-color: $error-alt; // #ae323b
+$debug-background: $warning-alt; // #ffd478
+$debug-border: $warning-alt; // #ffd478
diff --git a/dotclear._no/admin/style/scss/vendor/_codemirror.scss b/dotclear._no/admin/style/scss/vendor/_codemirror.scss
new file mode 100644
index 0000000..58a1816
--- /dev/null
+++ b/dotclear._no/admin/style/scss/vendor/_codemirror.scss
@@ -0,0 +1,6 @@
+/* CodeMirror CSS */
+
+.CodeMirror {
+ /* Set height, width, borders, and global font properties here */
+ font-family: $monospace !important;
+}
diff --git a/dotclear._no/admin/style/scss/vendor/_magnific-popup.scss b/dotclear._no/admin/style/scss/vendor/_magnific-popup.scss
new file mode 100644
index 0000000..85a0110
--- /dev/null
+++ b/dotclear._no/admin/style/scss/vendor/_magnific-popup.scss
@@ -0,0 +1,585 @@
+/* Magnific Popup CSS */
+
+////////////////////////
+// Settings //
+////////////////////////
+
+// overlay
+$mfp-overlay-color: #0b0b0b !default; // Color of overlay screen
+$mfp-overlay-opacity: 0.8 !default; // Opacity of overlay screen
+$mfp-shadow: 0 0 8px rgba(0, 0, 0, 0.6) !default; // Shadow on image or iframe
+
+// spacing
+$mfp-popup-padding-left: 8px !default; // Padding from left and from right side
+$mfp-popup-padding-left-mobile: 6px !default; // Same as above, but is applied when width of window is less than 800px
+
+$mfp-z-index-base: 1040 !default; // Base z-index of popup
+
+// controls
+$mfp-include-arrows: true !default; // Include styles for nav arrows
+$mfp-controls-opacity: 0.65 !default; // Opacity of controls
+$mfp-controls-color: #FFF !default; // Color of controls
+$mfp-controls-border-color: #3F3F3F !default; // Border color of controls
+$mfp-inner-close-icon-color: #333 !default; // Color of close button when inside
+$mfp-controls-text-color: #CCC !default; // Color of preloader and "1 of X" indicator
+$mfp-controls-text-color-hover: #FFF !default; // Hover color of preloader and "1 of X" indicator
+
+// Iframe-type options
+$mfp-include-iframe-type: true !default; // Enable Iframe-type popups
+$mfp-iframe-padding-top: 40px !default; // Iframe padding top
+$mfp-iframe-background: #000 !default; // Background color of iframes
+$mfp-iframe-max-width: 98%; // 900px !default; // Maximum width of iframes
+$mfp-iframe-ratio: 9/16 !default; // Ratio of iframe (9/16 = widescreen, 3/4 = standard, etc.)
+
+// Image-type options
+$mfp-include-image-type: true !default; // Enable Image-type popups
+$mfp-image-background: #444 !default;
+$mfp-image-padding-top: 40px !default; // Image padding top
+$mfp-image-padding-bottom: 40px !default; // Image padding bottom
+$mfp-include-mobile-layout-for-image: true !default; // Removes paddings from top and bottom
+
+// Image caption options
+$mfp-caption-title-color: #F3F3F3 !default; // Caption title color
+$mfp-caption-subtitle-color: #BDBDBD !default; // Caption subtitle color
+
+// A11y
+$mfp-use-visuallyhidden: false !default; // Hide content from browsers, but make it available for screen readers
+
+////////////////////////
+//
+// Contents:
+//
+// 1. General styles
+// - Transluscent overlay
+// - Containers, wrappers
+// - Cursors
+// - Helper classes
+// 2. Appearance
+// - Preloader & text that displays error messages
+// - CSS reset for buttons
+// - Close icon
+// - "1 of X" counter
+// - Navigation (left/right) arrows
+// - Iframe content type styles
+// - Image content type styles
+// - Media query where size of arrows is reduced
+// - IE7 support
+//
+////////////////////////
+
+
+
+////////////////////////
+// 1. General styles
+////////////////////////
+
+// Transluscent overlay
+.mfp-bg {
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: $mfp-z-index-base + 2;
+ overflow: hidden;
+ position: fixed;
+
+ background: $mfp-overlay-color;
+ opacity: $mfp-overlay-opacity;
+}
+
+// Wrapper for popup
+.mfp-wrap {
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: $mfp-z-index-base + 3;
+ position: fixed;
+ outline: none !important;
+ -webkit-backface-visibility: hidden; // fixes webkit bug that can cause "false" scrollbar
+}
+
+// Root container
+.mfp-container {
+ text-align: center;
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ left: 0;
+ top: 0;
+ padding: 0 $mfp-popup-padding-left;
+ box-sizing: border-box;
+}
+
+// Vertical centerer helper
+.mfp-container {
+ &:before {
+ content: '';
+ display: inline-block;
+ height: 100%;
+ vertical-align: middle;
+ }
+}
+
+// Remove vertical centering when popup has class `mfp-align-top`
+.mfp-align-top {
+ .mfp-container {
+ &:before {
+ display: none;
+ }
+ }
+}
+
+// Popup content holder
+.mfp-content {
+ position: relative;
+ display: inline-block;
+ vertical-align: middle;
+ margin: 0 auto;
+ text-align: left;
+ z-index: $mfp-z-index-base + 5;
+}
+.mfp-inline-holder,
+.mfp-ajax-holder {
+ .mfp-content {
+ width: 100%;
+ cursor: auto;
+ }
+}
+
+// Cursors
+.mfp-ajax-cur {
+ cursor: progress;
+}
+.mfp-zoom-out-cur {
+ &, .mfp-image-holder .mfp-close {
+ cursor: -moz-zoom-out;
+ cursor: -webkit-zoom-out;
+ cursor: zoom-out;
+ }
+}
+.mfp-zoom {
+ cursor: pointer;
+ cursor: -webkit-zoom-in;
+ cursor: -moz-zoom-in;
+ cursor: zoom-in;
+}
+.mfp-auto-cursor {
+ .mfp-content {
+ cursor: auto;
+ }
+}
+
+.mfp-close,
+.mfp-arrow,
+.mfp-preloader,
+.mfp-counter {
+ -webkit-user-select:none;
+ -moz-user-select: none;
+ user-select: none;
+}
+
+// Hide the image during the loading
+.mfp-loading {
+ &.mfp-figure {
+ display: none;
+ }
+}
+
+// Helper class that hides stuff
+@if $mfp-use-visuallyhidden {
+ // From HTML5 Boilerplate https://github.com/h5bp/html5-boilerplate/blob/v4.2.0/doc/css.md#visuallyhidden
+ .mfp-hide {
+ border: 0 !important;
+ clip: rect(0 0 0 0) !important;
+ height: 1px !important;
+ margin: -1px !important;
+ overflow: hidden !important;
+ padding: 0 !important;
+ position: absolute !important;
+ width: 1px !important;
+ }
+} @else {
+ .mfp-hide {
+ display: none !important;
+ }
+}
+
+
+////////////////////////
+// 2. Appearance
+////////////////////////
+
+// Preloader and text that displays error messages
+.mfp-preloader {
+ color: $mfp-controls-text-color;
+ position: absolute;
+ top: 50%;
+ width: auto;
+ text-align: center;
+ margin-top: -0.8em;
+ left: 8px;
+ right: 8px;
+ z-index: $mfp-z-index-base + 4;
+ a {
+ color: $mfp-controls-text-color;
+ &:hover {
+ color: $mfp-controls-text-color-hover;
+ }
+ }
+}
+
+// Hide preloader when content successfully loaded
+.mfp-s-ready {
+ .mfp-preloader {
+ display: none;
+ }
+}
+
+// Hide content when it was not loaded
+.mfp-s-error {
+ .mfp-content {
+ display: none;
+ }
+}
+
+// CSS-reset for buttons
+button {
+ &.mfp-close,
+ &.mfp-arrow {
+ overflow: visible;
+ cursor: pointer;
+ background: transparent;
+ border: 0;
+ -webkit-appearance: none;
+ display: block;
+ outline: none;
+ padding: 0;
+ z-index: $mfp-z-index-base + 6;
+ box-shadow: none;
+ touch-action: manipulation;
+ }
+ &::-moz-focus-inner {
+ padding: 0;
+ border: 0
+ }
+}
+
+
+// Close icon
+.mfp-close {
+ width: 44px;
+ height: 44px;
+ line-height: 44px;
+
+ position: absolute;
+ right: 0;
+ top: 0;
+ text-decoration: none;
+ text-align: center;
+ opacity: $mfp-controls-opacity;
+ padding: 0 0 18px 10px;
+ color: $mfp-controls-color;
+
+ font-style: normal;
+ font-size: 28px;
+ font-family: Arial, Baskerville, monospace;
+
+ &:hover,
+ &:focus {
+ opacity: 1;
+ }
+
+ &:active {
+ top: 1px;
+ }
+}
+.mfp-close-btn-in {
+ .mfp-close {
+ color: $mfp-inner-close-icon-color;
+ }
+}
+.mfp-image-holder,
+.mfp-iframe-holder {
+ .mfp-close {
+ color: $mfp-controls-color;
+ right: -6px;
+ text-align: right;
+ padding-right: 6px;
+ width: 100%;
+ }
+}
+
+// "1 of X" counter
+.mfp-counter {
+ position: absolute;
+ top: 0;
+ right: 0;
+ color: $mfp-controls-text-color;
+ font-size: 12px;
+ line-height: 18px;
+ white-space: nowrap;
+}
+
+// Navigation arrows
+@if $mfp-include-arrows {
+ .mfp-arrow {
+ position: absolute;
+ opacity: $mfp-controls-opacity;
+ margin: 0;
+ top: 50%;
+ margin-top: -55px;
+ padding: 0;
+ width: 90px;
+ height: 110px;
+ -webkit-tap-highlight-color: rgba(0,0,0,0);
+ &:active {
+ margin-top: -54px;
+ }
+ &:hover,
+ &:focus {
+ opacity: 1;
+ }
+ &:before,
+ &:after {
+ content: '';
+ display: block;
+ width: 0;
+ height: 0;
+ position: absolute;
+ left: 0;
+ top: 0;
+ margin-top: 35px;
+ margin-left: 35px;
+ border: medium inset transparent;
+ }
+
+ &:after {
+
+ border-top-width: 13px;
+ border-bottom-width: 13px;
+ top:8px;
+ }
+
+ &:before {
+ border-top-width: 21px;
+ border-bottom-width: 21px;
+ opacity: 0.7;
+ }
+
+ }
+
+ .mfp-arrow-left {
+ left: 0;
+ &:after {
+ border-right: 17px solid $mfp-controls-color;
+ margin-left: 31px;
+ }
+ &:before {
+ margin-left: 25px;
+ border-right: 27px solid $mfp-controls-border-color;
+ }
+ }
+
+ .mfp-arrow-right {
+ right: 0;
+ &:after {
+ border-left: 17px solid $mfp-controls-color;
+ margin-left: 39px
+ }
+ &:before {
+ border-left: 27px solid $mfp-controls-border-color;
+ }
+ }
+}
+
+
+
+// Iframe content type
+@if $mfp-include-iframe-type {
+ .mfp-iframe-holder {
+ padding-top: $mfp-iframe-padding-top;
+ padding-bottom: $mfp-iframe-padding-top;
+ .mfp-content {
+ line-height: 0;
+ width: 100%;
+ max-width: $mfp-iframe-max-width;
+ }
+ .mfp-close {
+ top: -40px;
+ }
+ }
+ .mfp-iframe-scaler {
+ width: 100%;
+ height: 0;
+ overflow: hidden;
+ padding-top: $mfp-iframe-ratio * 100%;
+ iframe {
+ position: absolute;
+ display: block;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ box-shadow: $mfp-shadow;
+ background: $mfp-iframe-background;
+ }
+ }
+}
+
+
+
+// Image content type
+@if $mfp-include-image-type {
+
+ /* Main image in popup */
+ img {
+ &.mfp-img {
+ width: auto;
+ max-width: 100%;
+ height: auto;
+ display: block;
+ line-height: 0;
+ box-sizing: border-box;
+ padding: $mfp-image-padding-top 0 $mfp-image-padding-bottom;
+ margin: 0 auto;
+ }
+ }
+
+ /* The shadow behind the image */
+ .mfp-figure {
+ line-height: 0;
+ &:after {
+ content: '';
+ position: absolute;
+ left: 0;
+ top: $mfp-image-padding-top;
+ bottom: $mfp-image-padding-bottom;
+ display: block;
+ right: 0;
+ width: auto;
+ height: auto;
+ z-index: -1;
+ box-shadow: $mfp-shadow;
+ background: $mfp-image-background;
+ }
+ small {
+ color: $mfp-caption-subtitle-color;
+ display: block;
+ font-size: 12px;
+ line-height: 14px;
+ }
+ figure {
+ margin: 0;
+ }
+ }
+ .mfp-bottom-bar {
+ margin-top: -$mfp-image-padding-bottom + 4;
+ position: absolute;
+ top: 100%;
+ left: 0;
+ width: 100%;
+ cursor: auto;
+ }
+ .mfp-title {
+ text-align: left;
+ line-height: 18px;
+ color: $mfp-caption-title-color;
+ word-wrap: break-word;
+ padding-right: 36px; // leave some space for counter at right side
+ }
+
+ .mfp-image-holder {
+ .mfp-content {
+ max-width: 100%;
+ }
+ }
+
+ .mfp-gallery {
+ .mfp-image-holder {
+ .mfp-figure {
+ cursor: pointer;
+ }
+ }
+ }
+
+
+ @if $mfp-include-mobile-layout-for-image {
+ @media screen and (max-width: 800px) and (orientation:landscape), screen and (max-height: 300px) {
+ /**
+ * Remove all paddings around the image on small screen
+ */
+ .mfp-img-mobile {
+ .mfp-image-holder {
+ padding-left: 0;
+ padding-right: 0;
+ }
+ img {
+ &.mfp-img {
+ padding: 0;
+ }
+ }
+ .mfp-figure {
+ // The shadow behind the image
+ &:after {
+ top: 0;
+ bottom: 0;
+ }
+ small {
+ display: inline;
+ margin-left: 5px;
+ }
+ }
+ .mfp-bottom-bar {
+ background: rgba(0,0,0,0.6);
+ bottom: 0;
+ margin: 0;
+ top: auto;
+ padding: 3px 5px;
+ position: fixed;
+ box-sizing: border-box;
+ &:empty {
+ padding: 0;
+ }
+ }
+ .mfp-counter {
+ right: 5px;
+ top: 3px;
+ }
+ .mfp-close {
+ top: 0;
+ right: 0;
+ width: 35px;
+ height: 35px;
+ line-height: 35px;
+ background: rgba(0, 0, 0, 0.6);
+ position: fixed;
+ text-align: center;
+ padding: 0;
+ }
+ }
+ }
+ }
+}
+
+
+
+// Scale navigation arrows and reduce padding from sides
+@media all and (max-width: 900px) {
+ .mfp-arrow {
+ -webkit-transform: scale(0.75);
+ transform: scale(0.75);
+ }
+ .mfp-arrow-left {
+ -webkit-transform-origin: 0;
+ transform-origin: 0;
+ }
+ .mfp-arrow-right {
+ -webkit-transform-origin: 100%;
+ transform-origin: 100%;
+ }
+ .mfp-container {
+ padding-left: $mfp-popup-padding-left-mobile;
+ padding-right: $mfp-popup-padding-left-mobile;
+ }
+}
diff --git a/dotclear._no/admin/style/search.png b/dotclear._no/admin/style/search.png
new file mode 100644
index 0000000..e480785
Binary files /dev/null and b/dotclear._no/admin/style/search.png differ
diff --git a/dotclear._no/admin/style/search.svg b/dotclear._no/admin/style/search.svg
new file mode 100644
index 0000000..0212c44
--- /dev/null
+++ b/dotclear._no/admin/style/search.svg
@@ -0,0 +1,10 @@
+
+
+
+
+
diff --git a/dotclear._no/admin/style/settings.png b/dotclear._no/admin/style/settings.png
new file mode 100644
index 0000000..f67f610
Binary files /dev/null and b/dotclear._no/admin/style/settings.png differ
diff --git a/dotclear._no/admin/style/texture.png b/dotclear._no/admin/style/texture.png
new file mode 100755
index 0000000..d386a02
Binary files /dev/null and b/dotclear._no/admin/style/texture.png differ
diff --git a/dotclear._no/admin/style/user.png b/dotclear._no/admin/style/user.png
new file mode 100644
index 0000000..036ba3a
Binary files /dev/null and b/dotclear._no/admin/style/user.png differ
diff --git a/dotclear._no/admin/style/warning.png b/dotclear._no/admin/style/warning.png
new file mode 100755
index 0000000..81e9ed2
Binary files /dev/null and b/dotclear._no/admin/style/warning.png differ
diff --git a/dotclear._no/admin/update.php b/dotclear._no/admin/update.php
new file mode 100644
index 0000000..8db3bbd
--- /dev/null
+++ b/dotclear._no/admin/update.php
@@ -0,0 +1,238 @@
+Access denied';
+ dcPage::close();
+ exit;
+}
+
+$updater = new dcUpdate(DC_UPDATE_URL, 'dotclear', DC_UPDATE_VERSION, DC_TPL_CACHE . '/versions');
+$new_v = $updater->check(DC_VERSION, !empty($_GET['nocache']));
+$zip_file = $new_v ? DC_BACKUP_PATH . '/' . basename($updater->getFileURL()) : '';
+$version_info = $new_v ? $updater->getInfoURL() : '';
+
+# Hide "update me" message
+if (!empty($_GET['hide_msg'])) {
+ $updater->setNotify(false);
+ http::redirect('index.php');
+}
+
+$p_url = 'update.php';
+
+$step = isset($_GET['step']) ? $_GET['step'] : '';
+$step = in_array($step, ['check', 'download', 'backup', 'unzip']) ? $step : '';
+
+$default_tab = !empty($_GET['tab']) ? html::escapeHTML($_GET['tab']) : 'update';
+if (!empty($_POST['backup_file'])) {
+ $default_tab = 'files';
+}
+
+$archives = [];
+foreach (files::scanDir(DC_BACKUP_PATH) as $v) {
+ if (preg_match('/backup-([0-9A-Za-z\.-]+).zip/', $v)) {
+ $archives[] = $v;
+ }
+}
+if (!empty($archives)) {
+ usort($archives, "version_compare");
+} else {
+ $default_tab = 'update';
+}
+
+# Revert or delete backup file
+if (!empty($_POST['backup_file']) && in_array($_POST['backup_file'], $archives)) {
+ $b_file = $_POST['backup_file'];
+
+ try
+ {
+ if (!empty($_POST['b_del'])) {
+ if (!@unlink(DC_BACKUP_PATH . '/' . $b_file)) {
+ throw new Exception(sprintf(__('Unable to delete file %s'), html::escapeHTML($b_file)));
+ }
+ http::redirect($p_url . '?tab=files');
+ }
+
+ if (!empty($_POST['b_revert'])) {
+ $zip = new fileUnzip(DC_BACKUP_PATH . '/' . $b_file);
+ $zip->unzipAll(DC_BACKUP_PATH . '/');
+ @unlink(DC_BACKUP_PATH . '/' . $b_file);
+ http::redirect($p_url . '?tab=files');
+ }
+ } catch (Exception $e) {
+ $core->error->add($e->getMessage());
+ }
+}
+
+# Upgrade process
+if ($new_v && $step) {
+ try
+ {
+ $updater->setForcedFiles('inc/digests');
+
+ switch ($step) {
+ case 'check':
+ $updater->checkIntegrity(DC_ROOT . '/inc/digests', DC_ROOT);
+ http::redirect($p_url . '?step=download');
+ break;
+ case 'download':
+ $updater->download($zip_file);
+ if (!$updater->checkDownload($zip_file)) {
+ throw new Exception(
+ sprintf(__('Downloaded Dotclear archive seems to be corrupted. ' .
+ 'Try download it again.'), 'href="' . $p_url . '?step=download"') .
+ ' ' .
+ __('If this problem persists try to ' .
+ 'update manually .')
+ );
+ }
+ http::redirect($p_url . '?step=backup');
+ break;
+ case 'backup':
+ $updater->backup(
+ $zip_file, 'dotclear/inc/digests',
+ DC_ROOT, DC_ROOT . '/inc/digests',
+ DC_BACKUP_PATH . '/backup-' . DC_VERSION . '.zip'
+ );
+ http::redirect($p_url . '?step=unzip');
+ break;
+ case 'unzip':
+ $updater->performUpgrade(
+ $zip_file, 'dotclear/inc/digests', 'dotclear',
+ DC_ROOT, DC_ROOT . '/inc/digests'
+ );
+ break;
+ }
+ } catch (Exception $e) {
+ $msg = $e->getMessage();
+
+ if ($e->getCode() == dcUpdate::ERR_FILES_CHANGED) {
+ $msg =
+ __('The following files of your Dotclear installation ' .
+ 'have been modified so we won\'t try to update your installation. ' .
+ 'Please try to update manually .');
+ } elseif ($e->getCode() == dcUpdate::ERR_FILES_UNREADABLE) {
+ $msg =
+ sprintf(__('The following files of your Dotclear installation are not readable. ' .
+ 'Please fix this or try to make a backup file named %s manually.'),
+ 'backup-' . DC_VERSION . '.zip ');
+ } elseif ($e->getCode() == dcUpdate::ERR_FILES_UNWRITALBE) {
+ $msg =
+ __('The following files of your Dotclear installation cannot be written. ' .
+ 'Please fix this or try to update manually .');
+ }
+
+ if (isset($e->bad_files)) {
+ $msg .=
+ '' .
+ implode(' ', $e->bad_files) .
+ ' ';
+ }
+
+ $core->error->add($msg);
+
+ $core->callBehavior('adminDCUpdateException', $e);
+ }
+}
+
+/* DISPLAY Main page
+-------------------------------------------------------- */
+dcPage::open(__('Dotclear update'),
+ (!$step ?
+ dcPage::jsPageTabs($default_tab) .
+ dcPage::jsLoad('js/_update.js')
+ : ''),
+ dcPage::breadcrumb(
+ [
+ __('System') => '',
+ __('Dotclear update') => ''
+ ])
+);
+
+if (!$core->error->flag()) {
+ if (!empty($_GET['nocache'])) {
+ dcPage::success(__('Manual checking of update done successfully.'));
+ }
+}
+
+if (!$step) {
+ echo '';
+ if (empty($new_v)) {
+ echo '
' . __('No newer Dotclear version available.') . '
' .
+ '
';
+ } else {
+ echo
+ '
' . sprintf(__('Dotclear %s is available.'), $new_v) .
+ ($version_info ? ' (' .
+ __('Information about this version') . ') ' : '') .
+ '
';
+ if (version_compare(phpversion(), $updater->getPHPVersion()) < 0) {
+ echo
+ '
' . sprintf(__('PHP version is %s (%s or earlier needed).'), phpversion(), $updater->getPHPVersion()) . '
';
+ } else {
+ echo
+ '
' . __('To upgrade your Dotclear installation simply click on the following button. ' .
+ 'A backup file of your current installation will be created in your root directory.') . '
' .
+ '
';
+ }
+ }
+ echo '
';
+
+ if (!empty($archives)) {
+ echo '';
+
+ echo
+ '
' . __('Update backup files') . ' ' .
+ '
' . __('The following files are backups of previously updates. ' .
+ 'You can revert your previous installation or delete theses files.') . '
';
+
+ echo '
';
+
+ echo '
';
+ }
+} elseif ($step == 'unzip' && !$core->error->flag()) {
+ echo
+ '' .
+ __("Congratulations, you're one click away from the end of the update.") .
+ ' ' . __('Finish the update.') . ' ' .
+ '
';
+}
+
+dcPage::helpBlock('core_update');
+dcPage::close();
diff --git a/dotclear._no/admin/user.php b/dotclear._no/admin/user.php
new file mode 100644
index 0000000..aebacb4
--- /dev/null
+++ b/dotclear._no/admin/user.php
@@ -0,0 +1,398 @@
+auth->getInfo('user_lang');
+$user_tz = $core->auth->getInfo('user_tz');
+$user_post_status = '';
+
+$user_options = $core->userDefaults();
+
+# Formaters combo
+$formaters_combo = dcAdminCombos::getFormatersCombo();
+
+$status_combo = dcAdminCombos::getPostStatusesCombo();
+
+# Language codes
+$lang_combo = dcAdminCombos::getAdminLangsCombo();
+
+# Get user if we have an ID
+if (!empty($_REQUEST['id'])) {
+ try {
+ $rs = $core->getUser($_REQUEST['id']);
+
+ $user_id = $rs->user_id;
+ $user_super = $rs->user_super;
+ $user_pwd = $rs->user_pwd;
+ $user_change_pwd = $rs->user_change_pwd;
+ $user_name = $rs->user_name;
+ $user_firstname = $rs->user_firstname;
+ $user_displayname = $rs->user_displayname;
+ $user_email = $rs->user_email;
+ $user_url = $rs->user_url;
+ $user_lang = $rs->user_lang;
+ $user_tz = $rs->user_tz;
+ $user_post_status = $rs->user_post_status;
+
+ $user_options = array_merge($user_options, $rs->options());
+
+ $page_title = $user_id;
+ } catch (Exception $e) {
+ $core->error->add($e->getMessage());
+ }
+}
+
+# Add or update user
+if (isset($_POST['user_name'])) {
+ try
+ {
+ if (empty($_POST['your_pwd']) || !$core->auth->checkPassword($_POST['your_pwd'])) {
+ throw new Exception(__('Password verification failed'));
+ }
+
+ $cur = $core->con->openCursor($core->prefix . 'user');
+
+ $cur->user_id = $_POST['user_id'];
+ $cur->user_super = $user_super = !empty($_POST['user_super']) ? 1 : 0;
+ $cur->user_name = $user_name = html::escapeHTML($_POST['user_name']);
+ $cur->user_firstname = $user_firstname = html::escapeHTML($_POST['user_firstname']);
+ $cur->user_displayname = $user_displayname = html::escapeHTML($_POST['user_displayname']);
+ $cur->user_email = $user_email = html::escapeHTML($_POST['user_email']);
+ $cur->user_url = $user_url = html::escapeHTML($_POST['user_url']);
+ $cur->user_lang = $user_lang = html::escapeHTML($_POST['user_lang']);
+ $cur->user_tz = $user_tz = html::escapeHTML($_POST['user_tz']);
+ $cur->user_post_status = $user_post_status = html::escapeHTML($_POST['user_post_status']);
+
+ if ($user_id && $cur->user_id == $core->auth->userID() && $core->auth->isSuperAdmin()) {
+ // force super_user to true if current user
+ $cur->user_super = $user_super = true;
+ }
+ if ($core->auth->allowPassChange()) {
+ $cur->user_change_pwd = !empty($_POST['user_change_pwd']) ? 1 : 0;
+ }
+
+ if (!empty($_POST['new_pwd'])) {
+ if ($_POST['new_pwd'] != $_POST['new_pwd_c']) {
+ throw new Exception(__("Passwords don't match"));
+ } else {
+ $cur->user_pwd = $_POST['new_pwd'];
+ }
+ }
+
+ $user_options['post_format'] = html::escapeHTML($_POST['user_post_format']);
+ $user_options['edit_size'] = (integer) $_POST['user_edit_size'];
+
+ if ($user_options['edit_size'] < 1) {
+ $user_options['edit_size'] = 10;
+ }
+
+ $cur->user_options = new ArrayObject($user_options);
+
+ # Udate user
+ if ($user_id) {
+ # --BEHAVIOR-- adminBeforeUserUpdate
+ $core->callBehavior('adminBeforeUserUpdate', $cur, $user_id);
+
+ $new_id = $core->updUser($user_id, $cur);
+
+ # --BEHAVIOR-- adminAfterUserUpdate
+ $core->callBehavior('adminAfterUserUpdate', $cur, $new_id);
+
+ if ($user_id == $core->auth->userID() &&
+ $user_id != $new_id) {
+ $core->session->destroy();
+ }
+
+ dcPage::addSuccessNotice(__('User has been successfully updated.'));
+ $core->adminurl->redirect("admin.user", ['id' => $new_id]);
+ }
+ # Add user
+ else {
+ if ($core->getUsers(['user_id' => $cur->user_id], true)->f(0) > 0) {
+ throw new Exception(sprintf(__('User "%s" already exists.'), html::escapeHTML($cur->user_id)));
+ }
+
+ # --BEHAVIOR-- adminBeforeUserCreate
+ $core->callBehavior('adminBeforeUserCreate', $cur);
+
+ $new_id = $core->addUser($cur);
+
+ # --BEHAVIOR-- adminAfterUserCreate
+ $core->callBehavior('adminAfterUserCreate', $cur, $new_id);
+
+ dcPage::addSuccessNotice(__('User has been successfully created.'));
+ dcPage::addWarningNotice(__('User has no permission, he will not be able to login yet. See below to add some.'));
+ if (!empty($_POST['saveplus'])) {
+ $core->adminurl->redirect("admin.user");
+ } else {
+ $core->adminurl->redirect("admin.user", ['id' => $new_id]);
+ }
+ }
+ } catch (Exception $e) {
+ $core->error->add($e->getMessage());
+ }
+}
+
+/* DISPLAY
+-------------------------------------------------------- */
+dcPage::open($page_title,
+ dcPage::jsConfirmClose('user-form') .
+ dcPage::jsLoad('js/jquery/jquery.pwstrength.js') .
+ dcPage::jsJson('user', [
+ sprintf(__('Password strength: %s'), __('very weak')),
+ sprintf(__('Password strength: %s'), __('weak')),
+ sprintf(__('Password strength: %s'), __('mediocre')),
+ sprintf(__('Password strength: %s'), __('strong')),
+ sprintf(__('Password strength: %s'), __('very strong'))
+ ]) .
+ dcPage::jsLoad('js/_user.js') .
+ $core->callBehavior('adminUserHeaders'),
+
+ dcPage::breadcrumb(
+ [
+ __('System') => '',
+ __('Users') => $core->adminurl->get("admin.users"),
+ $page_title => ''
+ ])
+);
+
+if (!empty($_GET['upd'])) {
+ dcPage::success(__('User has been successfully updated.'));
+}
+
+if (!empty($_GET['add'])) {
+ dcPage::success(__('User has been successfully created.'));
+}
+
+echo
+'';
+
+if ($user_id) {
+ echo '' .
+ '
' . __('Permissions') . ' ';
+
+ if (!$user_super) {
+ echo
+ '
';
+
+ $permissions = $core->getUserPermissions($user_id);
+ $perm_types = $core->auth->getPermissionsTypes();
+
+ if (count($permissions) == 0) {
+ echo '
' . __('No permissions so far.') . '
';
+ } else {
+ foreach ($permissions as $k => $v) {
+ if (count($v['p']) > 0) {
+ echo
+ '
';
+ }
+ }
+ }
+
+ } else {
+ echo '
' . sprintf(__('%s is super admin (all rights on all blogs).'), '' . $user_id . ' ') . '
';
+ }
+ echo '
';
+}
+
+dcPage::helpBlock('core_user');
+dcPage::close();
diff --git a/dotclear._no/admin/users.php b/dotclear._no/admin/users.php
new file mode 100644
index 0000000..d3f5737
--- /dev/null
+++ b/dotclear._no/admin/users.php
@@ -0,0 +1,179 @@
+ 'user_id',
+ __('Last Name') => 'user_name',
+ __('First Name') => 'user_firstname',
+ __('Display name') => 'user_displayname',
+ __('Number of entries') => 'nb_post'
+];
+
+$sortby_lex = [
+ // key in sorty_combo (see above) => field in SQL request
+ 'user_id' => 'U.user_id',
+ 'user_name' => 'user_name',
+ 'user_firstname' => 'user_firstname',
+ 'user_displayname' => 'user_displayname'];
+
+$order_combo = [
+ __('Descending') => 'desc',
+ __('Ascending') => 'asc'
+];
+
+# Actions combo box
+$combo_action = [
+ __('Set permissions') => 'blogs',
+ __('Delete') => 'deleteuser'
+];
+
+# --BEHAVIOR-- adminUsersActionsCombo
+$core->callBehavior('adminUsersActionsCombo', [&$combo_action]);
+
+$show_filters = false;
+
+#?Get users
+$page = !empty($_GET['page']) ? max(1, (integer) $_GET['page']) : 1;
+$nb_per_page = 30;
+
+if (!empty($_GET['nb']) && (integer) $_GET['nb'] > 0) {
+ if ($nb_per_page != (integer) $_GET['nb']) {
+ $show_filters = true;
+ }
+ $nb_per_page = (integer) $_GET['nb'];
+}
+
+$q = !empty($_GET['q']) ? $_GET['q'] : '';
+$sortby = !empty($_GET['sortby']) ? $_GET['sortby'] : 'user_id';
+$order = !empty($_GET['order']) ? $_GET['order'] : 'asc';
+
+$params['limit'] = [(($page - 1) * $nb_per_page), $nb_per_page];
+
+# - Search filter
+if ($q) {
+ $params['q'] = $q;
+ $show_filters = true;
+}
+
+# - Sortby and order filter
+if ($sortby !== '' && in_array($sortby, $sortby_combo, true)) {
+ if (array_key_exists($sortby, $sortby_lex)) {
+ $params['order'] = $core->con->lexFields($sortby_lex[$sortby]);
+ } else {
+ $params['order'] = $sortby;
+ }
+ if ($order !== '' && in_array($order, $order_combo, true)) {
+ $params['order'] .= ' ' . $order;
+ } else {
+ $order = 'asc';
+ }
+} else {
+ $sortby = 'user_id';
+ $order = 'asc';
+}
+if ($sortby != 'user_id' || $order != 'asc') {
+ $show_filters = true;
+}
+
+# Get users
+try {
+ $rs = $core->getUsers($params);
+ $counter = $core->getUsers($params, 1);
+ $rsStatic = $rs->toStatic();
+ if ($sortby != 'nb_post') {
+ // Sort user list using lexical order if necessary
+ $rsStatic->extend('rsExtUser');
+ $rsStatic = $rsStatic->toExtStatic();
+ $rsStatic->lexicalSort($sortby, $order);
+ }
+ $user_list = new adminUserList($core, $rsStatic, $counter->f(0));
+} catch (Exception $e) {
+ $core->error->add($e->getMessage());
+}
+
+/* DISPLAY
+-------------------------------------------------------- */
+
+dcPage::open(__('Users'),
+ dcPage::jsLoad('js/_users.js') . dcPage::jsFilterControl($show_filters),
+ dcPage::breadcrumb(
+ [
+ __('System') => '',
+ __('Users') => ''
+ ])
+);
+
+if (!$core->error->flag()) {
+ if (!empty($_GET['del'])) {
+ dcPage::message(__('User has been successfully removed.'));
+ }
+ if (!empty($_GET['upd'])) {
+ dcPage::message(__('The permissions have been successfully updated.'));
+ }
+
+ echo
+ '' . __('New user') . '
' .
+ '';
+
+ # Show users
+ $user_list->display($page, $nb_per_page,
+ '',
+ $show_filters
+ );
+}
+dcPage::helpBlock('core_users');
+dcPage::close();
diff --git a/dotclear._no/admin/users_actions.php b/dotclear._no/admin/users_actions.php
new file mode 100644
index 0000000..acd2200
--- /dev/null
+++ b/dotclear._no/admin/users_actions.php
@@ -0,0 +1,300 @@
+userExists($u)) {
+ $users[] = $u;
+ }
+ }
+}
+
+$blogs = [];
+if (!empty($_POST['blogs']) && is_array($_POST['blogs'])) {
+ foreach ($_POST['blogs'] as $b) {
+ if ($core->blogExists($b)) {
+ $blogs[] = $b;
+ }
+ }
+}
+
+/* Actions
+-------------------------------------------------------- */
+if (!empty($_POST['action']) && !empty($_POST['users'])) {
+ $action = $_POST['action'];
+
+ if (isset($_POST['redir']) && strpos($_POST['redir'], '://') === false) {
+ $redir = $_POST['redir'];
+ } else {
+ $redir = $core->adminurl->get("admin.users", [
+ 'q' => $_POST['q'],
+ 'sortby' => $_POST['sortby'],
+ 'order' => $_POST['order'],
+ 'page' => $_POST['page'],
+ 'nb' => $_POST['nb']
+ ]);
+ }
+
+ if (empty($users)) {
+ $core->error->add(__('No blog or user given.'));
+ }
+
+ # --BEHAVIOR-- adminUsersActions
+ $core->callBehavior('adminUsersActions', $core, $users, $blogs, $action, $redir);
+
+ # Delete users
+ if ($action == 'deleteuser' && !empty($users)) {
+ foreach ($users as $u) {
+ try
+ {
+ if ($u == $core->auth->userID()) {
+ throw new Exception(__('You cannot delete yourself.'));
+ }
+
+ # --BEHAVIOR-- adminBeforeUserDelete
+ $core->callBehavior('adminBeforeUserDelete', $u);
+
+ $core->delUser($u);
+ } catch (Exception $e) {
+ $core->error->add($e->getMessage());
+ }
+ }
+ if (!$core->error->flag()) {
+ dcPage::addSuccessNotice(__('User has been successfully deleted.'));
+ http::redirect($redir);
+ }
+ }
+
+ # Update users perms
+ if ($action == 'updateperm' && !empty($users) && !empty($blogs)) {
+ try
+ {
+ if (empty($_POST['your_pwd']) || !$core->auth->checkPassword($_POST['your_pwd'])) {
+ throw new Exception(__('Password verification failed'));
+ }
+
+ foreach ($users as $u) {
+ foreach ($blogs as $b) {
+ $set_perms = [];
+
+ if (!empty($_POST['perm'][$b])) {
+ foreach ($_POST['perm'][$b] as $perm_id => $v) {
+ if ($v) {
+ $set_perms[$perm_id] = true;
+ }
+ }
+ }
+
+ $core->setUserBlogPermissions($u, $b, $set_perms, true);
+ }
+ }
+ } catch (Exception $e) {
+ $core->error->add($e->getMessage());
+ }
+ if (!$core->error->flag()) {
+ dcPage::addSuccessNotice(__('User has been successfully updated.'));
+ http::redirect($redir);
+ }
+ }
+}
+
+/* DISPLAY
+-------------------------------------------------------- */
+if (!empty($users) && empty($blogs) && $action == 'blogs') {
+ $breadcrumb = dcPage::breadcrumb(
+ [
+ __('System') => '',
+ __('Users') => $core->adminurl->get("admin.users"),
+ __('Permissions') => ''
+ ]);
+} else {
+ $breadcrumb = dcPage::breadcrumb(
+ [
+ __('System') => '',
+ __('Users') => $core->adminurl->get("admin.users"),
+ __('Actions') => ''
+ ]);
+}
+
+dcPage::open(
+ __('Users'),
+ dcPage::jsLoad('js/_users_actions.js') .
+ # --BEHAVIOR-- adminUsersActionsHeaders
+ $core->callBehavior('adminUsersActionsHeaders'),
+ $breadcrumb
+);
+
+if (!isset($action)) {
+ dcPage::close();
+ exit;
+}
+
+$hidden_fields = '';
+foreach ($users as $u) {
+ $hidden_fields .= form::hidden(['users[]'], $u);
+}
+
+if (isset($_POST['redir']) && strpos($_POST['redir'], '://') === false) {
+ $hidden_fields .= form::hidden(['redir'], html::escapeURL($_POST['redir']));
+} else {
+ $hidden_fields .=
+ form::hidden(['q'], html::escapeHTML($_POST['q'])) .
+ form::hidden(['sortby'], $_POST['sortby']) .
+ form::hidden(['order'], $_POST['order']) .
+ form::hidden(['page'], $_POST['page']) .
+ form::hidden(['nb'], $_POST['nb']);
+}
+
+echo '' . __('Back to user profile') . '
';
+
+# --BEHAVIOR-- adminUsersActionsContent
+$core->callBehavior('adminUsersActionsContent', $core, $action, $hidden_fields);
+
+# Blog list where to set permissions
+if (!empty($users) && empty($blogs) && $action == 'blogs') {
+ try {
+ $rs = $core->getBlogs();
+ $nb_blog = $rs->count();
+ } catch (Exception $e) {}
+
+ foreach ($users as $u) {
+ $user_list[] = ' $u]) . '">' . $u . ' ';
+ }
+
+ echo
+ '' . sprintf(
+ __('Choose one or more blogs to which you want to give permissions to users %s.'),
+ implode(', ', $user_list)
+ ) . '
';
+
+ if ($nb_blog == 0) {
+ echo '' . __('No blog') . '
';
+ } else {
+ echo
+ '';
+ }
+}
+# Permissions list for each selected blogs
+elseif (!empty($blogs) && !empty($users) && $action == 'perms') {
+ $user_perm = [];
+ if (count($users) == 1) {
+ $user_perm = $core->getUserPermissions($users[0]);
+ }
+
+ foreach ($users as $u) {
+ $user_list[] = ' $u]) . '">' . $u . ' ';
+ }
+
+ echo
+ '' . sprintf(
+ __('You are about to change permissions on the following blogs for users %s.'),
+ implode(', ', $user_list)
+ ) . '
' .
+ '';
+}
+
+dcPage::helpBlock('core_users');
+dcPage::close();
diff --git a/dotclear._no/admin/xmlrpc.php b/dotclear._no/admin/xmlrpc.php
new file mode 100644
index 0000000..7bfc695
--- /dev/null
+++ b/dotclear._no/admin/xmlrpc.php
@@ -0,0 +1,34 @@
+setBlog($blog_id);
+
+# Loading plugins
+$core->plugins->loadModules(DC_PLUGINS_ROOT);
+
+# Start XML-RPC server
+$server = new dcXmlRpc($core, $blog_id);
+$server->serve();
diff --git a/dotclear._no/backup-2.10.2.zip b/dotclear._no/backup-2.10.2.zip
new file mode 100644
index 0000000..368b40b
Binary files /dev/null and b/dotclear._no/backup-2.10.2.zip differ
diff --git a/dotclear._no/backup-2.2.zip b/dotclear._no/backup-2.2.zip
new file mode 100644
index 0000000..a635846
Binary files /dev/null and b/dotclear._no/backup-2.2.zip differ
diff --git a/dotclear._no/backup-2.4.1.2.zip b/dotclear._no/backup-2.4.1.2.zip
new file mode 100644
index 0000000..00605ed
Binary files /dev/null and b/dotclear._no/backup-2.4.1.2.zip differ
diff --git a/dotclear._no/backup-2.4.3.zip b/dotclear._no/backup-2.4.3.zip
new file mode 100644
index 0000000..cb7a976
Binary files /dev/null and b/dotclear._no/backup-2.4.3.zip differ
diff --git a/dotclear._no/backup-2.5.zip b/dotclear._no/backup-2.5.zip
new file mode 100644
index 0000000..ad8c6c5
Binary files /dev/null and b/dotclear._no/backup-2.5.zip differ
diff --git a/dotclear._no/backup-2.6.2.zip b/dotclear._no/backup-2.6.2.zip
new file mode 100644
index 0000000..5dccfd7
Binary files /dev/null and b/dotclear._no/backup-2.6.2.zip differ
diff --git a/dotclear._no/backup-2.6.3.zip b/dotclear._no/backup-2.6.3.zip
new file mode 100644
index 0000000..e87d52e
Binary files /dev/null and b/dotclear._no/backup-2.6.3.zip differ
diff --git a/dotclear._no/backup-2.7.5.zip b/dotclear._no/backup-2.7.5.zip
new file mode 100644
index 0000000..5222dc3
Binary files /dev/null and b/dotclear._no/backup-2.7.5.zip differ
diff --git a/dotclear._no/backup-2.8.zip b/dotclear._no/backup-2.8.zip
new file mode 100644
index 0000000..fae3376
Binary files /dev/null and b/dotclear._no/backup-2.8.zip differ
diff --git a/dotclear._no/cache/.htaccess b/dotclear._no/cache/.htaccess
new file mode 100644
index 0000000..569316c
--- /dev/null
+++ b/dotclear._no/cache/.htaccess
@@ -0,0 +1,2 @@
+Require all denied
+Deny from all
diff --git a/dotclear._no/cache/cbfeed/1e/01/1e015be8e3af41e02d8b195e07385dbf.php b/dotclear._no/cache/cbfeed/1e/01/1e015be8e3af41e02d8b195e07385dbf.php
new file mode 100755
index 0000000..5e31489
--- /dev/null
+++ b/dotclear._no/cache/cbfeed/1e/01/1e015be8e3af41e02d8b195e07385dbf.php
@@ -0,0 +1 @@
+O:10:"feedParser":7:{s:9:"feed_type";s:7:"rss 2.0";s:5:"title";s:13:"Dotclear l10n";s:4:"link";s:52:"http://services.dotclear.net/dc2.l10n/?version=2.4.3";s:11:"description";s:0:"";s:7:"pubdate";s:31:"Mon, 09 Jul 2012 13:44:26 +0000";s:9:"generator";s:0:"";s:5:"items";a:27:{i:0;O:8:"stdClass":9:{s:5:"title";s:2:"bn";s:4:"link";s:52:"http://download.dotclear.net/l10n/2.4.3/bn-2.4.3.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:52:"http://download.dotclear.net/l10n/2.4.3/bn-2.4.3.zip";}i:1;O:8:"stdClass":9:{s:5:"title";s:2:"ca";s:4:"link";s:52:"http://download.dotclear.net/l10n/2.4.3/ca-2.4.3.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:52:"http://download.dotclear.net/l10n/2.4.3/ca-2.4.3.zip";}i:2;O:8:"stdClass":9:{s:5:"title";s:2:"cs";s:4:"link";s:52:"http://download.dotclear.net/l10n/2.4.3/cs-2.4.3.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:52:"http://download.dotclear.net/l10n/2.4.3/cs-2.4.3.zip";}i:3;O:8:"stdClass":9:{s:5:"title";s:2:"da";s:4:"link";s:52:"http://download.dotclear.net/l10n/2.4.3/da-2.4.3.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:52:"http://download.dotclear.net/l10n/2.4.3/da-2.4.3.zip";}i:4;O:8:"stdClass":9:{s:5:"title";s:2:"de";s:4:"link";s:52:"http://download.dotclear.net/l10n/2.4.3/de-2.4.3.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:52:"http://download.dotclear.net/l10n/2.4.3/de-2.4.3.zip";}i:5;O:8:"stdClass":9:{s:5:"title";s:2:"eo";s:4:"link";s:52:"http://download.dotclear.net/l10n/2.4.3/eo-2.4.3.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:52:"http://download.dotclear.net/l10n/2.4.3/eo-2.4.3.zip";}i:6;O:8:"stdClass":9:{s:5:"title";s:2:"es";s:4:"link";s:52:"http://download.dotclear.net/l10n/2.4.3/es-2.4.3.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:52:"http://download.dotclear.net/l10n/2.4.3/es-2.4.3.zip";}i:7;O:8:"stdClass":9:{s:5:"title";s:5:"es-ar";s:4:"link";s:55:"http://download.dotclear.net/l10n/2.4.3/es-ar-2.4.3.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:55:"http://download.dotclear.net/l10n/2.4.3/es-ar-2.4.3.zip";}i:8;O:8:"stdClass":9:{s:5:"title";s:2:"eu";s:4:"link";s:52:"http://download.dotclear.net/l10n/2.4.3/eu-2.4.3.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:52:"http://download.dotclear.net/l10n/2.4.3/eu-2.4.3.zip";}i:9;O:8:"stdClass":9:{s:5:"title";s:2:"fr";s:4:"link";s:52:"http://download.dotclear.net/l10n/2.4.3/fr-2.4.3.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:52:"http://download.dotclear.net/l10n/2.4.3/fr-2.4.3.zip";}i:10;O:8:"stdClass":9:{s:5:"title";s:2:"hu";s:4:"link";s:52:"http://download.dotclear.net/l10n/2.4.3/hu-2.4.3.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:52:"http://download.dotclear.net/l10n/2.4.3/hu-2.4.3.zip";}i:11;O:8:"stdClass":9:{s:5:"title";s:2:"it";s:4:"link";s:52:"http://download.dotclear.net/l10n/2.4.3/it-2.4.3.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:52:"http://download.dotclear.net/l10n/2.4.3/it-2.4.3.zip";}i:12;O:8:"stdClass":9:{s:5:"title";s:2:"ja";s:4:"link";s:52:"http://download.dotclear.net/l10n/2.4.3/ja-2.4.3.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:52:"http://download.dotclear.net/l10n/2.4.3/ja-2.4.3.zip";}i:13;O:8:"stdClass":9:{s:5:"title";s:2:"ko";s:4:"link";s:52:"http://download.dotclear.net/l10n/2.4.3/ko-2.4.3.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:52:"http://download.dotclear.net/l10n/2.4.3/ko-2.4.3.zip";}i:14;O:8:"stdClass":9:{s:5:"title";s:2:"lt";s:4:"link";s:52:"http://download.dotclear.net/l10n/2.4.3/lt-2.4.3.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:52:"http://download.dotclear.net/l10n/2.4.3/lt-2.4.3.zip";}i:15;O:8:"stdClass":9:{s:5:"title";s:2:"nl";s:4:"link";s:52:"http://download.dotclear.net/l10n/2.4.3/nl-2.4.3.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:52:"http://download.dotclear.net/l10n/2.4.3/nl-2.4.3.zip";}i:16;O:8:"stdClass":9:{s:5:"title";s:2:"oc";s:4:"link";s:52:"http://download.dotclear.net/l10n/2.4.3/oc-2.4.3.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:52:"http://download.dotclear.net/l10n/2.4.3/oc-2.4.3.zip";}i:17;O:8:"stdClass":9:{s:5:"title";s:2:"pl";s:4:"link";s:52:"http://download.dotclear.net/l10n/2.4.3/pl-2.4.3.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:52:"http://download.dotclear.net/l10n/2.4.3/pl-2.4.3.zip";}i:18;O:8:"stdClass":9:{s:5:"title";s:2:"pt";s:4:"link";s:52:"http://download.dotclear.net/l10n/2.4.3/pt-2.4.3.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:52:"http://download.dotclear.net/l10n/2.4.3/pt-2.4.3.zip";}i:19;O:8:"stdClass":9:{s:5:"title";s:5:"pt-br";s:4:"link";s:55:"http://download.dotclear.net/l10n/2.4.3/pt-br-2.4.3.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:55:"http://download.dotclear.net/l10n/2.4.3/pt-br-2.4.3.zip";}i:20;O:8:"stdClass":9:{s:5:"title";s:2:"ro";s:4:"link";s:52:"http://download.dotclear.net/l10n/2.4.3/ro-2.4.3.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:52:"http://download.dotclear.net/l10n/2.4.3/ro-2.4.3.zip";}i:21;O:8:"stdClass":9:{s:5:"title";s:2:"ru";s:4:"link";s:52:"http://download.dotclear.net/l10n/2.4.3/ru-2.4.3.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:52:"http://download.dotclear.net/l10n/2.4.3/ru-2.4.3.zip";}i:22;O:8:"stdClass":9:{s:5:"title";s:2:"sr";s:4:"link";s:52:"http://download.dotclear.net/l10n/2.4.3/sr-2.4.3.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:52:"http://download.dotclear.net/l10n/2.4.3/sr-2.4.3.zip";}i:23;O:8:"stdClass":9:{s:5:"title";s:2:"sv";s:4:"link";s:52:"http://download.dotclear.net/l10n/2.4.3/sv-2.4.3.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:52:"http://download.dotclear.net/l10n/2.4.3/sv-2.4.3.zip";}i:24;O:8:"stdClass":9:{s:5:"title";s:2:"te";s:4:"link";s:52:"http://download.dotclear.net/l10n/2.4.3/te-2.4.3.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:52:"http://download.dotclear.net/l10n/2.4.3/te-2.4.3.zip";}i:25;O:8:"stdClass":9:{s:5:"title";s:2:"tr";s:4:"link";s:52:"http://download.dotclear.net/l10n/2.4.3/tr-2.4.3.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:52:"http://download.dotclear.net/l10n/2.4.3/tr-2.4.3.zip";}i:26;O:8:"stdClass":9:{s:5:"title";s:5:"zh-cn";s:4:"link";s:55:"http://download.dotclear.net/l10n/2.4.3/zh-cn-2.4.3.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:55:"http://download.dotclear.net/l10n/2.4.3/zh-cn-2.4.3.zip";}}}
\ No newline at end of file
diff --git a/dotclear._no/cache/cbfeed/20/e2/20e2be0b01566b1dfdb1ebc165bbf470.php b/dotclear._no/cache/cbfeed/20/e2/20e2be0b01566b1dfdb1ebc165bbf470.php
new file mode 100755
index 0000000..acf4f0f
--- /dev/null
+++ b/dotclear._no/cache/cbfeed/20/e2/20e2be0b01566b1dfdb1ebc165bbf470.php
@@ -0,0 +1,323 @@
+O:10:"feedParser":7:{s:9:"feed_type";s:8:"atom 1.0";s:5:"title";s:20:"Blog Dotclear - News";s:4:"link";s:29:"https://fr.dotclear.org/blog/";s:11:"description";s:33:"Prenez le contrôle de votre blog";s:7:"pubdate";s:25:"2020-03-21T09:51:03+01:00";s:9:"generator";s:8:"Dotclear";s:5:"items";a:20:{i:0;O:8:"stdClass":8:{s:4:"link";s:58:"https://fr.dotclear.org/blog/post/2020/03/13/Dotclear-2.16";s:5:"title";s:13:"Dotclear 2.16";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:1257:" Jouons un peu en ces temps pas très encourageants, avec une nouvelle version de Dotclear publiée un … vendredi 13 \o/
+
+
+Au menu :
+
+
+🐘 PHP 5.6+ requis, compatibilité avec PHP 7.4
+🛡 Securité : toutes les requêtes depuis ou vers les serveurs Dotclear et DotAddict sont maintenant en HTTPS
+Mise à jour de la bibliothèque jQuery en 3.4.1, les anciennes versions étant supprimées ; notez que jQuery n'est plus requis pour profiter de la fonctionnalité « Se souvenir de moi » des formulaires de commentaire
+Nouveau mode « Statique » pour la page d'accueil avec possibilité d'utilisation d'une page dédiée comme contenu. Dans ce mode la liste des derniers billets est disponible à l'URL https://example.com/index.php?posts
+Les descriptions des médias (images) peuvent être mises à jour dans la médiathèque
+Ajout du support de l'élément HTML <i [lang="…"]>…</i> dans le wiki Dotclear ; syntaxe : ££text[|lang]££ (ex : ££english|en££)
+
+
+Voilà pour le plus important, on a aussi corrigé quelques bugs, visuels ou pas, apporté le support de MySQL 8+, entre autres choses.
";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2020-03-13T09:59:00+01:00";s:2:"TS";i:1584089940;}i:1;O:8:"stdClass":8:{s:4:"link";s:60:"https://fr.dotclear.org/blog/post/2019/11/28/Dotclear-2.15.3";s:5:"title";s:15:"Dotclear 2.15.3";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:328:" Une nouvelle version qui corrige quelques soucis :
+
+
+Effet de bord avec le compresseur de script javascript
+Insertion d'un média non image, vidéo ou audi dans une entrée au format XHTML
+Gestion de la case à cocher « Se souvenir de moi » pour les vieux thèmes
+ ";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2019-11-28T10:17:00+01:00";s:2:"TS";i:1574932620;}i:2;O:8:"stdClass":8:{s:4:"link";s:60:"https://fr.dotclear.org/blog/post/2019/10/01/Dotclear-2.15.2";s:5:"title";s:15:"Dotclear 2.15.2";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:336:" Une nouvelle version qui corrige :
+
+
+la sauvegarde des fichiers modifiés dans l'éditeur de thème lorsque la coloration syntaxique est activée ;
+l'insertion de vidéo avec les deux éditeurs
+l'affichage des badges (compteurs) utilisés par certains modules de tableau de bord
+ ";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2019-10-01T10:21:00+02:00";s:2:"TS";i:1569918060;}i:3;O:8:"stdClass":8:{s:4:"link";s:60:"https://fr.dotclear.org/blog/post/2019/08/29/Dotclear-2.15.1";s:5:"title";s:15:"Dotclear 2.15.1";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:352:" Une petite mise à jour qui devrait régler le problème de modification des pages et billets dans une langue différente de celle de l'interface, lors de l'utilisation de CKEditor. Elle devrait également régler le problème rencontré au moment de la mise à jour de la 2.15 par les utilisateurs de bases de données au format PostgreSQL.
";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2019-08-29T13:22:00+02:00";s:2:"TS";i:1567077720;}i:4;O:8:"stdClass":8:{s:4:"link";s:58:"https://fr.dotclear.org/blog/post/2019/08/12/Dotclear-2.15";s:5:"title";s:13:"Dotclear 2.15";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:1223:" Une nouvelle version de Dotclear légèrement plus robuste côté code, il n'y a entre autre plus de javascript « inline », et c'est mieux pour la sécurité ; et en général un peu plus de confort côté administration avec en particulier des listes et des tables « responsives », qui s'adaptent mieux à la taille d'écran disponible. Il devrait être plus aisé de gérer les commentaires sur smartphone, par exemple.
+
+
+Notez que les lecteurs Flash (vidéo et audio) ne sont plus intégrés dans la distribution — ils ne sont cependant pas supprimés au cours de la mise à jour.
+
+
+Côté tableau de bord vous pourrez maintenant organiser à votre guise la disposition des différents blocs (icônes, …) ; le format WebP est nativement supporté — à condition que votre serveur soit configuré pour ce faire ; quelques codes supplémentaires ont été ajoutés côté wiki (indice, exposant)…
+
+
+Pour le reste je vous laisse consulter le « CHANGELOG » ;-)
+
+
+On refait un festival de rétroliens, demain, pour l'anniversaire de Dotclear ?
";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2019-08-12T11:04:00+02:00";s:2:"TS";i:1565600640;}i:5;O:8:"stdClass":8:{s:4:"link";s:80:"https://fr.dotclear.org/blog/post/2019/01/15/Serveur-DotAddict-op%C3%A9rationnel";s:5:"title";s:31:"Serveur DotAddict opérationnel";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:481:" Un grand merci collectif à Noé (aka Lomalarch) qui a configuré et restauré sur un nouveau serveur ce qui restait de l'ancien qui nous a lâchement abandonné à la veille du nouvel an !
+
+
+Vous pouvez donc profiter comme il vous plait des thèmes et plugins , astuces , et autres geekeries \o/
";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2019-01-15T10:13:00+01:00";s:2:"TS";i:1547543580;}i:6;O:8:"stdClass":8:{s:4:"link";s:71:"https://fr.dotclear.org/blog/post/2018/12/26/Serveur-DotAddict-en-panne";s:5:"title";s:26:"Serveur DotAddict en panne";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:757:" Bonjour,
+
+
+Le serveur DotAddict, qui gère les plugins et thèmes des contributeurs, plus quelques autres ressources, a décidé de mourir sans prévenir pendant les agapes de Noël !
+
+
+On va remettre de l'ordre dans tout ça, aussi vite que possible, mais ça risque de prendre du temps, voire même un temps certain.
+
+
+Merci pour votre patience…
+
+
+PS : La non réponse de ce serveur peut ralentir votre tableau de bord, si vous avez l'icône « Gestion des plugins » et/ou « Apparence du blog » dans vos favoris. Si vous trouvez ça trop gênant, vous pouvez les retirer le temps que ça revienne en ligne (voir « Mes préférences », onglet « Mon tableau de bord »).
";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2018-12-26T13:17:00+01:00";s:2:"TS";i:1545826620;}i:7;O:8:"stdClass":8:{s:4:"link";s:60:"https://fr.dotclear.org/blog/post/2018/09/26/Dotclear-2.14.3";s:5:"title";s:15:"Dotclear 2.14.3";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:217:" Une petite mise à jour qui devrait régler le problème de configuration rencontré entre autres chez 1&1 avec une valeur de memory_limit (dans le PHP.ini) initialisée à -1 (illimitée).
";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2018-09-26T11:24:00+02:00";s:2:"TS";i:1537953840;}i:8;O:8:"stdClass":8:{s:4:"link";s:60:"https://fr.dotclear.org/blog/post/2018/09/04/Dotclear-2.14.2";s:5:"title";s:15:"Dotclear 2.14.2";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:560:" Une petite mise à jour qui règle quelques problèmes avec :
+
+
+La sécurité (deux failles mineures)
+L'utilisation de la coloration syntaxique dans l'éditeur de thème
+La gestion de certaines balises template (entre autres celles utilisées pour l'entête des pages)
+
+
+Note : Certains bloqueurs de pub de votre navigateur peuvent empêcher le bon fonctionnement de l'administration, en particulier le ou les éditeurs. Pensez dans ce cas à utiliser la liste blanche de l'extension en question.
";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2018-09-04T15:24:00+02:00";s:2:"TS";i:1536067440;}i:9;O:8:"stdClass":8:{s:4:"link";s:60:"https://fr.dotclear.org/blog/post/2018/08/17/Dotclear-2.14.1";s:5:"title";s:15:"Dotclear 2.14.1";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:246:" Une petite mise à jour qui règle quelques problèmes avec :
+
+
+Le script d'installation
+La taille de police par défaut dans les préférences utilisateur
+La compression des scripts Javascript
+ ";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2018-08-17T09:41:00+02:00";s:2:"TS";i:1534491660;}i:10;O:8:"stdClass":8:{s:4:"link";s:76:"https://fr.dotclear.org/blog/post/2018/08/14/Dotclear-2.14-et-version-de-PHP";s:5:"title";s:31:"Dotclear 2.14 et version de PHP";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:270:" On m'a rapporté à plusieurs reprises des problèmes de connexion avec l'admin sur les hébergements tournant avec une version 5.5 (obsolète) de PHP.
+
+
+Je vous recommande donc de basculer vers la version 5.6 ou encore mieux vers la version 7.1 ou 7.2
";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2018-08-14T09:58:00+02:00";s:2:"TS";i:1534233480;}i:11;O:8:"stdClass":8:{s:4:"link";s:58:"https://fr.dotclear.org/blog/post/2018/08/13/Dotclear-2.14";s:5:"title";s:13:"Dotclear 2.14";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:503:" Nom de code : Dark Crystal
+
+
+Crystal parce que noces de, vu que ça fait 15 ans aujourd'hui que Dotclear tourne sur les meilleurs serveurs de blogs de la planète !
+
+
+Dark parce qu'il y a, depuis cette version, un mode sombre pour l'interface d'administration !
+
+
+Dark Crystal parce que j'aime bien ce film sorti il y a 35 ans :-)
+
+
+Sinon, c'est comme d'habitude…
+
+
+Enjoy!
";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2018-08-13T10:06:00+02:00";s:2:"TS";i:1534147560;}i:12;O:8:"stdClass":8:{s:4:"link";s:60:"https://fr.dotclear.org/blog/post/2018/01/27/Dotclear-2.13.1";s:5:"title";s:15:"Dotclear 2.13.1";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:200:" Une petite mise à jour qui corrige deux bugs agaçants dans l'éditeur de thème (lorsque la coloration syntaxique est activée) et avec l'éditeur traditionnel de Dotclear, en mode XHTML.
";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2018-01-27T07:47:00+01:00";s:2:"TS";i:1517035620;}i:13;O:8:"stdClass":8:{s:4:"link";s:58:"https://fr.dotclear.org/blog/post/2018/01/13/Dotclear-2.13";s:5:"title";s:13:"Dotclear 2.13";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:2353:" Quelques mots :
+
+
+PHP 5.5+ devient obligatoire comme un nouveau système plus sécurisé a été implémenté pour le stockage des mots de passe dans la base de données, système qui nécessite a minima cette version de PHP
+Les informations affichées sur le tableau de bord, comme les mises à jour de Dotclear ou les nouvelles, le sont dorénavant de manière asynchrone , donc plus de blocage inhérent à un serveur parfois fantôme !
+Un nouveau driver pour les bases de données MySQL encodées avec UTF8-mb4
+Quelques bugs corrigés
+Quelques améliorations de l'expérience utilisateur côté administration des blogs
+
+
+Profitez !
+
+
+
+
+
+Note pour les utilisateurs et administrateurs :
+
+
+Le nouveau système de chiffrement des mots de passe ne nécessite aucune procédure de migration. Ceci sera fait au fur et à mesure à la première connexion de chacun des utilisateurs.
+
+
+Notes pour les développeurs :
+
+
+Le nouveau système des mots de passe implique deux incompatibilités par rapport aux versions précédentes :
+
+
+La fonction/méthode checkPassword() (/inc/core/class.dc.auth.php) requiert dorénavant un mot de passe non chiffré (habituellement le contenu du champ de formulaire) plutôt que la forme déjà chiffrée. Vous devez donc maintenant l'utiliser de cette façon : $core->auth->checkPassword($_POST['your_pwd']) plutôt que celle-ci : $core->auth->checkPassword($core->auth->crypt($_POST['your_pwd'])).
+La fonction/méthode crypt() (/inc/core/class.dc.auth.php) ne donne maintenant plus deux fois de suite le même résultat pour le même paramètre. Si vous avez besoin d'un chiffrement à l'ancienne mode vous pouvez utiliser la fonction/méthode cryptLegacy() (même fichier).
+
+
+Si vous avez besoin d'un UID/clé unique , utilisez http::browserUID(DC_MASTER_KEY.$core->auth->userID().$core->auth->cryptLegacy($core->auth->userID())) (sera peut-être sujet à amélioration dans le futur).
";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2018-01-13T12:08:00+01:00";s:2:"TS";i:1515841680;}i:14;O:8:"stdClass":8:{s:4:"link";s:60:"https://fr.dotclear.org/blog/post/2017/08/13/Dotclear-2.12.1";s:5:"title";s:15:"Dotclear 2.12.1";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:651:" Une nouvelle petite mise à jour aujourd'hui qui corrige un bug qui touche ceux qui utilisent un thème basé sur le jeu de template (par défaut) mustek. Elle règle aussi un petit problème avec les titres de certains médias au moment de leur envoi dans la médiathèque.
+
+
+N'oubliez pas de vider le cache des templates (plugin Entretien) ainsi que le cache de votre navigateur après avoir fait une mise à jour. Dans le cas où vous auriez opté pour une mise à jour manuelle, n'oubliez pas non plus de vous déconnecter et de vous reconnecter, certaines mises à jour (concernant la base de données) se font à ce moment là.
";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2017-08-13T04:47:00+02:00";s:2:"TS";i:1502592420;}i:15;O:8:"stdClass":8:{s:4:"link";s:58:"https://fr.dotclear.org/blog/post/2017/07/27/Dotclear-2.12";s:5:"title";s:13:"Dotclear 2.12";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:1809:" Mise à jour minimaliste, bien que le CHANGELOG ne soit pas si ridicule que ça, vu qu'il n'y aucune fonctionnalité supplémentaire, simplement des améliorations et des corrections de bug.
+
+
+Vu l'état des troupes depuis de nombreux mois, voire de nombreuses années, j'ai décidé de réduire la voilure en terme de développement car étant seul pour coder je n'ai pas le courage de me lancer dans des projets conséquents[1 ] — oui, on a un bus factor de 1 chez Dotclear.
+
+
+Exit donc l'API REST que j'avais en tête — cela dit un plugin tiers est en cours de développement qui devrait apporter cette fonctionnalité —, exit aussi l'intégration du moteur Twig pour les thèmes, exit aussi pas mal de tickets qui restaient en souffrance faute de bras pour les prendre en charge.
+
+
+Je vais continuer à maintenir Dotclear, ajouter quelques petites choses qui manquent, comme le support des bases de données MySQL UTF8-MB4, une gestion des mots de passe un peu plus robuste, et ça fera l'objet d'une prochaine 2.13 qui au passage nécessitera PHP 5.5 minimum — il est grand temps de laisser tomber PHP 5.3 vieillissant et plus du tout maintenu.
+
+";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2017-07-27T10:25:00+02:00";s:2:"TS";i:1501143900;}i:16;O:8:"stdClass":8:{s:4:"link";s:60:"https://fr.dotclear.org/blog/post/2016/12/29/Dotclear-2.11.2";s:5:"title";s:15:"Dotclear 2.11.2";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:586:" Une nouvelle petite mise à jour aujourd'hui qui corrige quelques bugs gênants avec PHP 5.3 et PHP 5.4 ; elle règle également le problème de prévisualisation des billets et pages en cours d'édition.
+
+
+N'oubliez pas de vider le cache des templates (plugin Entretien) ainsi que le cache de votre navigateur après avoir fait une mise à jour. Dans le cas où vous auriez opté pour une mise à jour manuelle, n'oubliez pas non plus de vous déconnecter et de vous reconnecter, certaines mises à jour (concernant la base de données) se font à ce moment là.
";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2016-12-29T15:29:00+01:00";s:2:"TS";i:1483021740;}i:17;O:8:"stdClass":8:{s:4:"link";s:60:"https://fr.dotclear.org/blog/post/2016/12/28/Dotclear-2.11.1";s:5:"title";s:15:"Dotclear 2.11.1";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:278:" Une petite mise à jour qui corrige un problème passé inaperçu lorsqu'on utilise une version de PHP antérieure à 5.5.
+
+
+Ce problème empêche l'affichage du menu d'administration (colonne de gauche) avec la plupart des plugins, voire empêche leur accès.
";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2016-12-28T14:20:00+01:00";s:2:"TS";i:1482931200;}i:18;O:8:"stdClass":8:{s:4:"link";s:58:"https://fr.dotclear.org/blog/post/2016/12/28/Dotclear-2.11";s:5:"title";s:13:"Dotclear 2.11";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:6361:" Une nouvelle version qui déroge à nos habitudes, vu qu'elle n'est pas publiée un 13 , et en avance par rapport au planning, vu qu'elle était prévue mi-février 2017 . Elle déroge aussi avec l'habitude d'avoir un copieux CHANGELOG (liste des modifications/corrections).
+
+
+En effet, cette version n'apporte rien d'extraordinaire sauf qu'elle facilite à pas mal d'endroits, l'utilisation de Dotclear, et elle corrige quelques bugs parfois gênants au quotidien :
+
+
+Accès plus facile aux réglages des plugins,
+une personnalisation un peu plus poussée (taille des textes, affichage ou pas d'information complémentaires, …),
+quelques attributs supplémentaires pour les développeurs/bidouilleurs de thèmes,
+les webmentions qui viennent s'ajouter aux existants rétroliens (trackbacks) et pingbacks ,
+le thème Berlin s'appuie maintenant sur le jeu de template dotty , qui exploite au mieux HTML5 ,
+…
+
+
+L'aspect général de l'administration change également parce qu'avec la 2.11, on utilise dorénavant la police système disponible sur votre machine plutôt que l'Helvetica Neue habituelle. Elle change aussi parce qu'elle met en œuvre une taille de police qui s'adapte, entre deux seuils, à la place disponible sur votre écran. Vous pourrez modifier la taille générale de la police dans vos préférences (3 réglages sont proposés).
+
+
+À noter qu'on a laissé tomber le support des vieux navigateurs, en particulier toutes les versions d'Internet Explorer à un chiffre, soit jusqu'à la version 9 incluse ; ça permet d'utiliser un peu plus facilement quelques nouveautés de CSS 3, en particulier le système flex pour l'agencement des blocs dans une zone.
+
+
+Mais je vous laisse découvrir ça chez vous, une fois que vous aurez fait l'attendue mise-à-jour !
+
+
+PS : Cette version nécessite PHP 5.3 a minima, mais je ne saurais trop vous conseiller de passer à PHP 5.6 voire PHP 7 sans attendre — cette dernière offre un gain de vitesse très appréciable. Il est très possible que la version suivante de Dotclear nécessite une version plus récente que la déjà obsolète 5.3.
+
+
+
+
+
+Quelques détails techniques pour les développeurs de plugins et les administrateurs de blog :
+
+
+Réglages et paramètres des plugins
+
+
+La nouvelle version 2.11 introduit un nouveau système qui permet de définir et de trouver les différents endroits où un plugin peut être paramétré.
+
+
+Définitions
+
+
+Il faut définir dans le fichier _define.php du plugin une propriété supplémentaire, nommée settings et qui se construit de la façon suivante :
+
+
+'settings' => array(
+ 'self' => '',
+ 'blog' => '#params.id',
+ 'pref' => '#user-options.id'
+)
+
+
+
+La ligne avec ‘self’ permet d’indiquer qu’il y a des réglages sur la page principale du plugin (c’est-à-dire pour les développeurs, dans le fichier index.php).
+
+
+La ligne avec ‘blog’ permet d’indiquer qu’il y a des réglages dans les paramètres du blog, normalement sur l’onglet « Paramètres » (le #params sert à ça) et que le premier élément concernant le plugin a un identifiant égal à id (on peut par exemple positionner cet id sur l’élément de titre, h4 ou h5, qui précède les options du plugin).
+
+
+La ligne avec ‘pref’ permet d’indiquer qu’il y a des réglages dans les préférences utilisateur, normalement sur l’onglet « Mes options » (le #user-options sert à ça) et que le premier élément concernant le plugin a un identifiant égal à id.
+
+
+Vous pouvez, et même devez, ne préciser que les lignes qui sont pertinentes.
+
+
+Il n’est pas obligatoire de préciser l’id, dans ce cas il suffit de préciser simplement l’onglet. Il n’est pas non plus obligatoire de préciser l’onglet, dans ce cas laisser simplement une chaine vide (”).
+
+
+Les liens seront affichés dans l’ordre où ils sont définis dans la propriété ‘settings’.
+
+
+Nota : À cette liste de lien sera ajoutée en premier, s’il existe, le lien vers le fichier _config.php du plugin.
+
+
+Exemples de définitions
+
+
+Plugin Antispam
+
+
+'settings' => array(
+ 'self' => '',
+ 'blog' => '#params.antispam_params'
+)
+
+
+
+self → accès aux réglages principaux du plugin sur sa propre page (index.php)
+blog → accès aux réglages secondaires dans les paramètres du blog
+
+
+Plugin Mot-clés
+
+
+'settings' => array(
+ 'pref' => '#user-options.tags_prefs'
+)
+
+
+
+pref → accès au réglage du format de liste des mot-clés dans les préférences utilisateur
+
+
+Plugin Maintenance
+
+
+'settings' => array(
+ 'self' => '#settings'
+)
+
+
+
+self → accès à l’onglet “Réglages” de la propre page du plugin (index.php)
+
+
+Affichage
+
+
+L’affichage des URLs de réglage se font à deux endroits :
+
+
+Sur la page de gestion des plugins, en dépliant les infos supplémentaires (il suffit de cliquer sur le nom du plugin pour les obtenir)
+
+
+Sur chacune des pages principales des plugins, à condition d’avoir les droits pour y accéder aux différents réglages, sachant que ce qui est définit pour ‘self’ ne sera pas affiché (a priori on y est déjà).
+
+
+
+
+
+Si vous avez besoin de plus d'information sur ces développements techniques, utilisez le forum et/ou la mailing-list de développement , voire même le canal IRC #dotclear (irc.freenode.net) où certains d'entre nous traînent parfois…
";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2016-12-28T10:00:00+01:00";s:2:"TS";i:1482915600;}i:19;O:8:"stdClass":8:{s:4:"link";s:60:"https://fr.dotclear.org/blog/post/2016/11/02/Dotclear-2.10.4";s:5:"title";s:15:"Dotclear 2.10.4";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:2153:" Une mise à jour qui corrige un problème de connexion à la base de données avec les installations utilisant PostgreSQL inférieur à 9.1
+
+
+Si vous n'êtes pas dans ce cas, la mise à jour automatique fonctionnera comme d'habitude.
+
+
+Si vous êtes dans ce cas , pour pouvoir faire la mise à jour automatique, suivez la procédure suivante :
+
+
+Ouvrez le fichier /inc/libs/clearbricks/dblayer/class.pgsql.php
+Insérez une ligne devant la ligne 103 et insérer sur cette nouvelle ligne le code suivant et sauvegardez :
+
+
+return;
+
+
+
+Vous devriez avoir quelque chose comme ça :
+
+
+ /** @ignore */
+ private function db_post_connect($handle,$database)
+ {
+return;
+ $result = $this->db_query($handle,"SELECT * FROM pg_collation WHERE (collcollate LIKE '%.utf8')");
+ if($this->db_num_rows($result) > 0) {
+ $this->db_result_seek($result, 0);
+ $row = $this->db_fetch_assoc($result);
+ $this->utf8_unicode_ci = '"'.$row['collname'].'"';
+ }
+ }
+
+
+
+Cette modification vous redonnera accès à votre installation.
+
+
+Pour la mise à jour automatique, qui détectera cette modification, il faudra installer au préalable un plugin qui permet de passer outre l'avertissement. Ce plugin, FakeMeUp, est disponible sur DotAddict .
+
+
+Une fois ce plugin installé, vous pourrez faire la mise à jour, puis une fois celle-ci terminée, le désactiver ou le désinstaller.
+
+
+La proposition de mise à jour de votre installation devrait apparaître sur votre tableau de bord aujourd'hui ou demain (selon les réglages de votre hébergement) et un patch est disponible pour les développeurs préférant appliquer cette méthode.
+
+
+
+
+
+Le CHANGELOG de cette version :
+
+
+Dotclear 2.10.4 - 2016-11-02
+===========================================================
+* PostgreSQL < 9.1 fix
+ ";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2016-11-02T10:45:00+01:00";s:2:"TS";i:1478079900;}}}
\ No newline at end of file
diff --git a/dotclear._no/cache/cbfeed/32/1a/321a1b740025ff2362eec8b8e007843a.php b/dotclear._no/cache/cbfeed/32/1a/321a1b740025ff2362eec8b8e007843a.php
new file mode 100755
index 0000000..6641497
--- /dev/null
+++ b/dotclear._no/cache/cbfeed/32/1a/321a1b740025ff2362eec8b8e007843a.php
@@ -0,0 +1,451 @@
+O:10:"feedParser":7:{s:9:"feed_type";s:8:"atom 1.0";s:5:"title";s:20:"Blog Dotclear - News";s:4:"link";s:29:"https://fr.dotclear.org/blog/";s:11:"description";s:33:"Prenez le contrôle de votre blog";s:7:"pubdate";s:25:"2017-09-16T12:45:38+02:00";s:9:"generator";s:8:"Dotclear";s:5:"items";a:20:{i:0;O:8:"stdClass":8:{s:4:"link";s:60:"https://fr.dotclear.org/blog/post/2017/08/13/Dotclear-2.12.1";s:5:"title";s:15:"Dotclear 2.12.1";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:651:" Une nouvelle petite mise à jour aujourd'hui qui corrige un bug qui touche ceux qui utilisent un thème basé sur le jeu de template (par défaut) mustek. Elle règle aussi un petit problème avec les titres de certains médias au moment de leur envoi dans la médiathèque.
+
+
+N'oubliez pas de vider le cache des templates (plugin Entretien) ainsi que le cache de votre navigateur après avoir fait une mise à jour. Dans le cas où vous auriez opté pour une mise à jour manuelle, n'oubliez pas non plus de vous déconnecter et de vous reconnecter, certaines mises à jour (concernant la base de données) se font à ce moment là.
";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2017-08-13T04:47:00+02:00";s:2:"TS";i:1502592420;}i:1;O:8:"stdClass":8:{s:4:"link";s:58:"https://fr.dotclear.org/blog/post/2017/07/27/Dotclear-2.12";s:5:"title";s:13:"Dotclear 2.12";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:1809:" Mise à jour minimaliste, bien que le CHANGELOG ne soit pas si ridicule que ça, vu qu'il n'y aucune fonctionnalité supplémentaire, simplement des améliorations et des corrections de bug.
+
+
+Vu l'état des troupes depuis de nombreux mois, voire de nombreuses années, j'ai décidé de réduire la voilure en terme de développement car étant seul pour coder je n'ai pas le courage de me lancer dans des projets conséquents[1 ] — oui, on a un bus factor de 1 chez Dotclear.
+
+
+Exit donc l'API REST que j'avais en tête — cela dit un plugin tiers est en cours de développement qui devrait apporter cette fonctionnalité —, exit aussi l'intégration du moteur Twig pour les thèmes, exit aussi pas mal de tickets qui restaient en souffrance faute de bras pour les prendre en charge.
+
+
+Je vais continuer à maintenir Dotclear, ajouter quelques petites choses qui manquent, comme le support des bases de données MySQL UTF8-MB4, une gestion des mots de passe un peu plus robuste, et ça fera l'objet d'une prochaine 2.13 qui au passage nécessitera PHP 5.5 minimum — il est grand temps de laisser tomber PHP 5.3 vieillissant et plus du tout maintenu.
+
+";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2017-07-27T10:25:00+02:00";s:2:"TS";i:1501143900;}i:2;O:8:"stdClass":8:{s:4:"link";s:60:"https://fr.dotclear.org/blog/post/2016/12/29/Dotclear-2.11.2";s:5:"title";s:15:"Dotclear 2.11.2";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:586:" Une nouvelle petite mise à jour aujourd'hui qui corrige quelques bugs gênants avec PHP 5.3 et PHP 5.4 ; elle règle également le problème de prévisualisation des billets et pages en cours d'édition.
+
+
+N'oubliez pas de vider le cache des templates (plugin Entretien) ainsi que le cache de votre navigateur après avoir fait une mise à jour. Dans le cas où vous auriez opté pour une mise à jour manuelle, n'oubliez pas non plus de vous déconnecter et de vous reconnecter, certaines mises à jour (concernant la base de données) se font à ce moment là.
";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2016-12-29T15:29:00+01:00";s:2:"TS";i:1483021740;}i:3;O:8:"stdClass":8:{s:4:"link";s:60:"https://fr.dotclear.org/blog/post/2016/12/28/Dotclear-2.11.1";s:5:"title";s:15:"Dotclear 2.11.1";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:278:" Une petite mise à jour qui corrige un problème passé inaperçu lorsqu'on utilise une version de PHP antérieure à 5.5.
+
+
+Ce problème empêche l'affichage du menu d'administration (colonne de gauche) avec la plupart des plugins, voire empêche leur accès.
";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2016-12-28T14:20:00+01:00";s:2:"TS";i:1482931200;}i:4;O:8:"stdClass":8:{s:4:"link";s:58:"https://fr.dotclear.org/blog/post/2016/12/28/Dotclear-2.11";s:5:"title";s:13:"Dotclear 2.11";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:6361:" Une nouvelle version qui déroge à nos habitudes, vu qu'elle n'est pas publiée un 13 , et en avance par rapport au planning, vu qu'elle était prévue mi-février 2017 . Elle déroge aussi avec l'habitude d'avoir un copieux CHANGELOG (liste des modifications/corrections).
+
+
+En effet, cette version n'apporte rien d'extraordinaire sauf qu'elle facilite à pas mal d'endroits, l'utilisation de Dotclear, et elle corrige quelques bugs parfois gênants au quotidien :
+
+
+Accès plus facile aux réglages des plugins,
+une personnalisation un peu plus poussée (taille des textes, affichage ou pas d'information complémentaires, …),
+quelques attributs supplémentaires pour les développeurs/bidouilleurs de thèmes,
+les webmentions qui viennent s'ajouter aux existants rétroliens (trackbacks) et pingbacks ,
+le thème Berlin s'appuie maintenant sur le jeu de template dotty , qui exploite au mieux HTML5 ,
+…
+
+
+L'aspect général de l'administration change également parce qu'avec la 2.11, on utilise dorénavant la police système disponible sur votre machine plutôt que l'Helvetica Neue habituelle. Elle change aussi parce qu'elle met en œuvre une taille de police qui s'adapte, entre deux seuils, à la place disponible sur votre écran. Vous pourrez modifier la taille générale de la police dans vos préférences (3 réglages sont proposés).
+
+
+À noter qu'on a laissé tomber le support des vieux navigateurs, en particulier toutes les versions d'Internet Explorer à un chiffre, soit jusqu'à la version 9 incluse ; ça permet d'utiliser un peu plus facilement quelques nouveautés de CSS 3, en particulier le système flex pour l'agencement des blocs dans une zone.
+
+
+Mais je vous laisse découvrir ça chez vous, une fois que vous aurez fait l'attendue mise-à-jour !
+
+
+PS : Cette version nécessite PHP 5.3 a minima, mais je ne saurais trop vous conseiller de passer à PHP 5.6 voire PHP 7 sans attendre — cette dernière offre un gain de vitesse très appréciable. Il est très possible que la version suivante de Dotclear nécessite une version plus récente que la déjà obsolète 5.3.
+
+
+
+
+
+Quelques détails techniques pour les développeurs de plugins et les administrateurs de blog :
+
+
+Réglages et paramètres des plugins
+
+
+La nouvelle version 2.11 introduit un nouveau système qui permet de définir et de trouver les différents endroits où un plugin peut être paramétré.
+
+
+Définitions
+
+
+Il faut définir dans le fichier _define.php du plugin une propriété supplémentaire, nommée settings et qui se construit de la façon suivante :
+
+
+'settings' => array(
+ 'self' => '',
+ 'blog' => '#params.id',
+ 'pref' => '#user-options.id'
+)
+
+
+
+La ligne avec ‘self’ permet d’indiquer qu’il y a des réglages sur la page principale du plugin (c’est-à-dire pour les développeurs, dans le fichier index.php).
+
+
+La ligne avec ‘blog’ permet d’indiquer qu’il y a des réglages dans les paramètres du blog, normalement sur l’onglet « Paramètres » (le #params sert à ça) et que le premier élément concernant le plugin a un identifiant égal à id (on peut par exemple positionner cet id sur l’élément de titre, h4 ou h5, qui précède les options du plugin).
+
+
+La ligne avec ‘pref’ permet d’indiquer qu’il y a des réglages dans les préférences utilisateur, normalement sur l’onglet « Mes options » (le #user-options sert à ça) et que le premier élément concernant le plugin a un identifiant égal à id.
+
+
+Vous pouvez, et même devez, ne préciser que les lignes qui sont pertinentes.
+
+
+Il n’est pas obligatoire de préciser l’id, dans ce cas il suffit de préciser simplement l’onglet. Il n’est pas non plus obligatoire de préciser l’onglet, dans ce cas laisser simplement une chaine vide (”).
+
+
+Les liens seront affichés dans l’ordre où ils sont définis dans la propriété ‘settings’.
+
+
+Nota : À cette liste de lien sera ajoutée en premier, s’il existe, le lien vers le fichier _config.php du plugin.
+
+
+Exemples de définitions
+
+
+Plugin Antispam
+
+
+'settings' => array(
+ 'self' => '',
+ 'blog' => '#params.antispam_params'
+)
+
+
+
+self → accès aux réglages principaux du plugin sur sa propre page (index.php)
+blog → accès aux réglages secondaires dans les paramètres du blog
+
+
+Plugin Mot-clés
+
+
+'settings' => array(
+ 'pref' => '#user-options.tags_prefs'
+)
+
+
+
+pref → accès au réglage du format de liste des mot-clés dans les préférences utilisateur
+
+
+Plugin Maintenance
+
+
+'settings' => array(
+ 'self' => '#settings'
+)
+
+
+
+self → accès à l’onglet “Réglages” de la propre page du plugin (index.php)
+
+
+Affichage
+
+
+L’affichage des URLs de réglage se font à deux endroits :
+
+
+Sur la page de gestion des plugins, en dépliant les infos supplémentaires (il suffit de cliquer sur le nom du plugin pour les obtenir)
+
+
+Sur chacune des pages principales des plugins, à condition d’avoir les droits pour y accéder aux différents réglages, sachant que ce qui est définit pour ‘self’ ne sera pas affiché (a priori on y est déjà).
+
+
+
+
+
+Si vous avez besoin de plus d'information sur ces développements techniques, utilisez le forum et/ou la mailing-list de développement , voire même le canal IRC #dotclear (irc.freenode.net) où certains d'entre nous traînent parfois…
";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2016-12-28T10:00:00+01:00";s:2:"TS";i:1482915600;}i:5;O:8:"stdClass":8:{s:4:"link";s:60:"https://fr.dotclear.org/blog/post/2016/11/02/Dotclear-2.10.4";s:5:"title";s:15:"Dotclear 2.10.4";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:2153:" Une mise à jour qui corrige un problème de connexion à la base de données avec les installations utilisant PostgreSQL inférieur à 9.1
+
+
+Si vous n'êtes pas dans ce cas, la mise à jour automatique fonctionnera comme d'habitude.
+
+
+Si vous êtes dans ce cas , pour pouvoir faire la mise à jour automatique, suivez la procédure suivante :
+
+
+Ouvrez le fichier /inc/libs/clearbricks/dblayer/class.pgsql.php
+Insérez une ligne devant la ligne 103 et insérer sur cette nouvelle ligne le code suivant et sauvegardez :
+
+
+return;
+
+
+
+Vous devriez avoir quelque chose comme ça :
+
+
+ /** @ignore */
+ private function db_post_connect($handle,$database)
+ {
+return;
+ $result = $this->db_query($handle,"SELECT * FROM pg_collation WHERE (collcollate LIKE '%.utf8')");
+ if($this->db_num_rows($result) > 0) {
+ $this->db_result_seek($result, 0);
+ $row = $this->db_fetch_assoc($result);
+ $this->utf8_unicode_ci = '"'.$row['collname'].'"';
+ }
+ }
+
+
+
+Cette modification vous redonnera accès à votre installation.
+
+
+Pour la mise à jour automatique, qui détectera cette modification, il faudra installer au préalable un plugin qui permet de passer outre l'avertissement. Ce plugin, FakeMeUp, est disponible sur DotAddict .
+
+
+Une fois ce plugin installé, vous pourrez faire la mise à jour, puis une fois celle-ci terminée, le désactiver ou le désinstaller.
+
+
+La proposition de mise à jour de votre installation devrait apparaître sur votre tableau de bord aujourd'hui ou demain (selon les réglages de votre hébergement) et un patch est disponible pour les développeurs préférant appliquer cette méthode.
+
+
+
+
+
+Le CHANGELOG de cette version :
+
+
+Dotclear 2.10.4 - 2016-11-02
+===========================================================
+* PostgreSQL < 9.1 fix
+ ";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2016-11-02T10:45:00+01:00";s:2:"TS";i:1478079900;}i:6;O:8:"stdClass":8:{s:4:"link";s:60:"https://fr.dotclear.org/blog/post/2016/11/01/Dotclear-2.10.3";s:5:"title";s:15:"Dotclear 2.10.3";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:1374:" Une petite mise à jour qui corrige principalement deux failles de sécurité légères et qui devrait permettre un fonctionnement plus souple avec certaines configurations de serveur utilisant un proxy.
+
+
+La proposition de mise à jour de votre installation devrait apparaître sur votre tableau de bord aujourd'hui ou demain (selon les réglages de votre hébergement) et un patch est disponible pour les développeurs préférant appliquer cette méthode.
+
+
+
+
+
+Le CHANGELOG de cette version :
+
+
+Dotclear 2.10.3 - 2016-11-01
+===========================================================
+* Security: Fix CVE-2016-7903: Password Reset Address Spoof — Thank's Hongkun Zeng for report
+* Security: Fix CVE-2016-7902: Media Manager, unrestricted File Upload — Thank's Hongkun Zeng for report
+* CSP: Cope with external sources used in editor's iframe to preview public external content
+* Fix: Cope with post.post_position field during flat import
+* Fix: Prevents precondition failed during currently activated theme update
+* Fix: Remove unecessary header (cope by dotclear) in page plugin
+* Fix: Let some proxies playing with standard http and https ports
+* Fix: Let SSL runs through a proxy, it may be ok, sometimes
+* 🐛 → Various bugs and typos fixed
+ ";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2016-11-01T15:20:00+01:00";s:2:"TS";i:1478010000;}i:7;O:8:"stdClass":8:{s:4:"link";s:60:"https://fr.dotclear.org/blog/post/2016/08/17/Dotclear-2.10.2";s:5:"title";s:15:"Dotclear 2.10.2";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:500:" Une petite mise à jour qui corrige un problème qui empêche la mise à jour avec les installations utilisant le système de base de données PostgreSQL.
+
+
+La proposition de mise à jour de votre installation devrait apparaître sur votre tableau de bord aujourd'hui ou demain (selon les réglages de votre hébergement) et un patch est disponible pour les développeurs préférant appliquer cette méthode.
";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2016-08-17T10:22:00+02:00";s:2:"TS";i:1471422120;}i:8;O:8:"stdClass":8:{s:4:"link";s:60:"https://fr.dotclear.org/blog/post/2016/08/15/Dotclear-2.10.1";s:5:"title";s:15:"Dotclear 2.10.1";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:659:" Une petite mise à jour qui corrige un problème qui empêche l'affichage correct de l'administration pour les nouvelles installations (les mises à jour ne sont pas concernées), une application un peu trop stricte des CSP (Content-Security-Policies) en est la cause, signe que cette protection est efficace !
+
+
+La proposition de mise à jour de votre installation devrait apparaître sur votre tableau de bord aujourd'hui ou demain (selon les réglages de votre hébergement) et un patch est disponible pour les développeurs préférant appliquer cette méthode.
";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2016-08-15T09:15:00+02:00";s:2:"TS";i:1471245300;}i:9;O:8:"stdClass":8:{s:4:"link";s:76:"https://fr.dotclear.org/blog/post/2016/08/14/Dotclear-2.10-%3A-attention-%21";s:5:"title";s:27:"Dotclear 2.10 : attention !";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:662:" On vient de me signaler un problème qui empêche les feuilles de style CSS et les scripts Javascript d'être chargées dans l'administration de Dotclear, mais uniquement pour les nouvelles installations .
+
+
+Si vous êtes confrontés à ce problème, téléchargez plutôt une version 2.9.1 , installez-là, puis faites ensuite la mise à jour proposée sur le tableau de bord, cette dernière n'étant pas concernée par ce problème.
+
+
+Vous pouvez aussi attendre demain que je sorte une version 2.10.1 de Dotclear qui réglera ce bug.
";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2016-08-14T10:35:00+02:00";s:2:"TS";i:1471163700;}i:10;O:8:"stdClass":8:{s:4:"link";s:58:"https://fr.dotclear.org/blog/post/2016/08/13/Dotclear-2.10";s:5:"title";s:13:"Dotclear 2.10";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:7573:" Il ne fallait pas manquer l'occasion de sortir une version pour le 13e anniversaire de Dotclear et donc voilà, la 2.10 est disponible dès maintenant et très rapidement sur votre tableau de bord[1 ] !
+
+
+Au menu (liste non exhaustive, voyez le CHANGELOG pour plus de détails) :
+
+
+Quelques vulnérabilités corrigées
+Pas mal de bugs éradiqués
+Un nouveau jeu de template nommé dotty utilisant autant que faire se peut les nouvelles balises sémantiques HTML5
+De nouvelles options pour personnaliser et utiliser un peu plus facilement votre administration (dossiers favoris pour la médiathèque, colonnes optionnelles pour les listes de pages et de billets, …)
+La mise en place des Content-Security-Policies pour l'administration, prélude à une implémentation côté blogs pour la future version 2.11[2 ]
+De nouvelles facilités et possibilités pour les développeurs de plugins (elles sont détaillées ci-dessous)
+Des mises à jour des librairies Javascript utilisées (CKEditor, Codemirror, …)
+
+
+Pas de révolution donc, mais des évolutions pour une application plus sécurisée et plus robuste ; pour finir, joyeux anniversaire Dotclear \o/
+
+
+PS : Cette version nécessite PHP 5.3 a minima, mais je ne saurais trop vous conseiller de passer à PHP 5.6 voire PHP 7 sans attendre — cette dernière offre un gain de vitesse très appréciable. Il est très possible que la version suivante de Dotclear nécessite une version plus récente que la déjà obsolète 5.3.
+
+
+
+
+
+Quelques détails techniques pour les développeurs de plugins (et de thèmes) et les administrateurs de blog :
+
+
+CSP, aka Content-Security-Policies
+
+
+Content Security Policy (abrégé CSP) est un mécanisme de sécurité permettant de restreindre l'origine du contenu (tel qu'un script Javascript, une feuille de style etc.) dans une page web à certains sites autorisés. Cela permet de mieux se prémunir d'une éventuelle faille XSS.
+
+
+[ Wikipedia « Content Security Policy » ]
+
+
+Les paramètres utilisés (activation et directives) sont accessibles via le module about:config du menu Réglages système . Voir la partie « system », les paramètres concernés étant les suivants :
+
+
+csp_admin_on : activation/désactivation
+csp_admin_default : directive CSP default-src
+csp_admin_img : directive CSP img-src
+csp_admin_script : directive CSP script-src
+csp_admin_style : directive CSP style-src
+
+
+Un plugin tiers devant utiliser les services d'un serveur externe peut compléter tout ou partie de ces directives à l'aide du behavior adminPageHTTPHeaderCSP qui fournit en argument un tableau à clé, Chacune des clés correspond à une des directives CSP, sa valeur fournissant la liste des éléments (séparés par des espaces) de la directive.
+
+
+Exemple :
+
+
+Si un plugin utilise côté administration l'API Google Maps (pour les scripts), il peut ajouter le serveur correspondant de cette façon :
+
+
+$core->addBehavior('adminPageHTTPHeaderCSP',array('myAdminBehaviors','adminPageHTTPHeaderCSP'));
+
+class myAdminBehaviors
+{
+ public static function adminPageHTMLHead($csp)
+ {
+ if (isset($csp['script-src'])) {
+ $csp['script-src'] .= ' maps.googleapis.com';
+ } else {
+ $csp['script-src'] = 'maps.googleapis.com';
+ }
+ }
+}
+
+
+
+Répertoire privé /var
+
+
+Un nouveau répertoire, nommé var fait son apparition avec la 2.10. Il est situé à la racine et devrait être utilisé pour le stockage d'éléments n'ayant rien à faire dans la médiathèque ou dans le répertoire de cache (qui peut être supprimé à n'importe quel moment sans remettre en question le fonctionnement de Dotclear).
+
+
+Une constante, DC_VAR , est disponible automatiquement et peut être personnalisée dans le fichier config.php pour construire des chemins d'accès. Deux fonctions sont également disponibles pour récupérer des URLs :
+
+
+dcPage::getVF() pour une URL basée sur l'URL d'administration du blog
+dcBlog::getVF() pour une URL publique basée sur l'URL du blog
+
+
+Les développeurs de plugin sont fortement encouragés à créer leur propre répertoire au sein de ce répertoire /var afin de conserver un semblant d'ordre.
+
+
+Coloration syntaxique via Codemirror
+
+
+La librairie Codemirror utilisée par l'éditeur de thème est désormais utilisable (côté administration) par n'importe quel plugin. Deux fonctions sont disponibles pour le chargement de la librairie et pour son exécution :
+
+
+dcPage::jsLoadCodeMirror() pour le chargement
+dcPage::jsRunCodeMirror() pour l'exécution
+
+
+Exemple pour du code CSS :
+
+
+# Get interface setting
+$core->auth->user_prefs->addWorkspace('interface');
+$user_ui_colorsyntax = $core->auth->user_prefs->interface->colorsyntax;
+$user_ui_colorsyntax_theme = $core->auth->user_prefs->interface->colorsyntax_theme;
+
+# in <head>
+if ($user_ui_colorsyntax) {
+ echo dcPage::jsLoadCodeMirror($user_ui_colorsyntax_theme,false,array('css'));
+}
+
+# in <body>
+if ($user_ui_colorsyntax) {
+ echo dcPage::jsRunCodeMirror('editor_css','css_content','css',$user_ui_colorsyntax_theme);
+}
+
+
+
+L'activation de la coloration syntaxique et le choix du thème à utiliser (parmi les quarante et plus proposés) se font dans « Mes préférences », onglet « Mes options ».
+
+
+
+
+
+Si vous avez besoin de plus d'information sur ces développements techniques, utilisez le forum et/ou la mailing-list de développement , voire même le canal IRC #dotclear (irc.freenode.net) où certains d'entre nous traînent parfois…
+
+";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2016-08-13T13:13:00+02:00";s:2:"TS";i:1471086780;}i:11;O:8:"stdClass":8:{s:4:"link";s:59:"https://fr.dotclear.org/blog/post/2016/03/27/Dotclear-2.9.1";s:5:"title";s:14:"Dotclear 2.9.1";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:1068:" Une version de maintenance qui corrige quelques défauts de la 2.9 précédente. Je rappelle que Dotclear est compatible avec PHP 7 et que les performances en sont considérablement améliorées[1 ] .
+
+
+La proposition de mise à jour de votre installation devrait apparaître sur votre tableau de bord aujourd'hui ou demain (selon les réglages de votre hébergement) et un patch est disponible pour les développeurs préférant appliquer cette méthode.
+
+";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2016-03-27T11:11:00+02:00";s:2:"TS";i:1459069860;}i:12;O:8:"stdClass":8:{s:4:"link";s:57:"https://fr.dotclear.org/blog/post/2016/02/29/Dotclear-2.9";s:5:"title";s:12:"Dotclear 2.9";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:2924:" Mes agneaux, c'est l'heure de mettre à jour, la nouvelle version 2.9 vous tend les bras !
+
+Fédor Balanovitch (en sortant du bus, ou presque) — Zazie dans le métro, R. Queneau
+
+
+Au menu de cette version essentiellement de quoi faciliter un peu la vie de ceux qui passent du temps du côté de l'administration de leur(s) blog(s). Une recherche et les derniers dossiers visités pour le gestionnaire de médias, des menus mieux triés et des listes un peu plus filtrables, quelques mises à jour bienvenues pour les librairies javascript utilisées[1 ] .
+
+
+Et puis on a aussi fait le nécessaire pour que Dotclear tourne correctement avec la nouvelle version 7 de PHP, version assez impressionnante en termes de gain de vitesse, et vous noterez au passage que la version minimum requise de PHP est la 5.3, comme on l'avait annoncé au moment de la sortie de la version 2.8 [2 ] .
+
+
+Pas mal de bugs ont été éradiqués, quelques possibilités nouvelles ont été implémentées pour les développeurs de plugins et les concepteurs de thème, et pour finir une application plus robuste pour tout le monde.
+
+
+La future version 2.10 sera essentiellement axée sur deux aspects. Premièrement une « remise à plat » des scripts javascript utilisés côté administration, parce qu'à force on finit par avoir quelques vieilleries dans notre « collection », et, deuxièmement, une migration « douce » vers plus de HTML5/CSS3 côté templates et thèmes. Cela dit nous comptons sur vous pour nous dire si vous préféreriez autre chose pour la suite, rien n'est gravé dans le marbre !
+
+
+La proposition de mise à jour de votre installation devrait apparaître sur votre tableau de bord aujourd'hui ou demain (selon les réglages de votre hébergement) et un patch est disponible pour les développeurs préférant appliquer cette méthode.
+
+";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2016-02-29T12:46:00+01:00";s:2:"TS";i:1456746360;}i:13;O:8:"stdClass":8:{s:4:"link";s:59:"https://fr.dotclear.org/blog/post/2015/10/25/Dotclear-2.8.2";s:5:"title";s:14:"Dotclear 2.8.2";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:1247:" Une nouvelle version de maintenance qui règle une potentielle faille de sécurité du côté de la liste des commentaires, et qui renforce le contrôle des extensions de média pouvant être envoyés à la médiathèque[1 ] , vulnérabilités signalées par Tim Coen (Curesec GmbH) que nous remercions pour le signalement, ainsi que deux autres défauts.
+
+
+La proposition de mise à jour de votre installation devrait apparaître sur votre tableau de bord aujourd'hui ou demain (selon les réglages de votre hébergement) et un patch est disponible pour les développeurs préférant appliquer cette méthode.
+
+";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2015-10-25T09:37:00+01:00";s:2:"TS";i:1445762220;}i:14;O:8:"stdClass":8:{s:4:"link";s:59:"https://fr.dotclear.org/blog/post/2015/09/23/Dotclear-2.8.1";s:5:"title";s:14:"Dotclear 2.8.1";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:658:" Une nouvelle version de maintenance qui règle une potentielle faille de sécurité du côté des listes des billets et des pages, faille signalée par Yuji Tounai of NTT Com Security (Japan) KK (via Keiko Yashiki du JPCERT/CC) que nous remercions pour le signalement, ainsi que deux autres défauts moins critiques.
+
+
+La proposition de mise à jour de votre installation devrait apparaître sur votre tableau de bord aujourd'hui ou demain (selon les réglages de votre hébergement) et un patch est disponible pour les développeurs préférant appliquer cette méthode.
";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2015-09-23T15:34:00+02:00";s:2:"TS";i:1443015240;}i:15;O:8:"stdClass":8:{s:4:"link";s:79:"https://fr.dotclear.org/blog/post/2015/08/14/Dotclear-2.8-%3A-CKEditor-en-panne";s:5:"title";s:34:"Dotclear 2.8 : CKEditor en panne ?";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:591:" Quelques uns d'entres vous nous ont rapporté des soucis avec l'usage de CKEditor depuis la mise à jour en 2.8 de Dotclear.
+
+
+Après vérification il s'agit, dans la majorité des cas (a priori tous), d'un souci avec le réglage d'une constante dans le fichier inc/config.php de vos installations.
+
+
+Dans ce fichier, une ligne indique l'URL de l'administration :
+
+
+// Admin URL. You need to set it for some features.
+define('DC_ADMIN_URL','http://exemple.com/dotclear/admin/');
+
+
+
+Vérifiez qu'elle est correctement positionnée.
";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2015-08-14T11:25:00+02:00";s:2:"TS";i:1439544300;}i:16;O:8:"stdClass":8:{s:4:"link";s:57:"https://fr.dotclear.org/blog/post/2015/08/13/Dotclear-2.8";s:5:"title";s:12:"Dotclear 2.8";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:3070:" Quelques mois après la version 2.7.5 voilà aujourd'hui, pour le 12e anniversaire de Dotclear, la version 2.8 qui pointe le bout de son nez sous les traits de Dotty [1 ] , notre nouvelle mascotte[2 ] :
+
+
+
+Dotty
+
+
+
+Cette nouvelle version introduit un nouveau mécanisme qui permet de gérer des dépendances entres modules (plugins pour l'instant et bientôt pour les thèmes), intègre le plugin Breadcrumb (fil d'Ariane que certains d'entre-vous utilisent peut-être déjà), met à jour l'éditeur CKEditor et la librairie jQuery, corrige pas mal de bugs et de petits problèmes cosmétiques.
+
+
+Le système d'extension/héritage des templates a été appliqué au jeu de template mustek, afin de simplifier le développement les thèmes qui s'appuient dessus, quelques critères de tri et de filtre ont été ajoutés pour l'affichage des billets et des commentaires (et spams) côté administration, les mots-clés et widgets sont dorénavant triés selon un ordre lexical pour les langues utilisant un alphabet latin, entre autre choses sur lesquelles nous reviendrons de temps en temps par ici.
+
+
+Important : Si vous avez installé le plugin breadcrumb (intitulé « Fil d'Ariane »), désinstallez-le avant de faire cette mise à jour.
+
+
+Autre chose : nous allons abandonner le support de la version 5.2 de PHP et imposer a minima la version 5.3 (qui rappellons-le est déjà obsolète). Sachez que Dotclear a été testé jusqu'à la version 5.6 de PHP.
+
+
+La proposition de mise à jour de votre installation devrait apparaître sur votre tableau de bord aujourd'hui ou demain (selon les réglages de votre hébergement) et un patch est disponible pour les développeurs préférant appliquer cette méthode.
+
+";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2015-08-13T11:11:00+02:00";s:2:"TS";i:1439457060;}i:17;O:8:"stdClass":8:{s:4:"link";s:59:"https://fr.dotclear.org/blog/post/2015/03/25/Dotclear-2.7.5";s:5:"title";s:14:"Dotclear 2.7.5";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:694:" Une nouvelle version de maintenance qui règle deux potentielles failles de sécurité du côté de l'édition des billets et des pages, failles signalées ce matin par the SecPod Research Team Member Shakeel que nous remercions pour le signalement, ainsi que trois autres défauts moins critiques.
+
+
+La proposition de mise à jour de votre installation devrait apparaître sur votre tableau de bord aujourd'hui ou demain (selon les réglages de votre hébergement) et un patch est disponible pour les développeurs préférant appliquer cette méthode.
";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2015-03-25T10:36:00+01:00";s:2:"TS";i:1427276160;}i:18;O:8:"stdClass":8:{s:4:"link";s:59:"https://fr.dotclear.org/blog/post/2015/02/13/Dotclear-2.7.4";s:5:"title";s:14:"Dotclear 2.7.4";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:503:" Petite version de maintenance, qui a pour petit nom « The Cat », et qui apporte son (petit) lot de corrections et d'améliorations ce vendredi 13 !
+
+
+La proposition de mise à jour de votre installation devrait apparaître sur votre tableau de bord aujourd'hui ou demain (selon les réglages de votre hébergement) et un patch est disponible pour les développeurs préférant appliquer cette méthode.
";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2015-02-13T09:57:00+01:00";s:2:"TS";i:1423817820;}i:19;O:8:"stdClass":8:{s:4:"link";s:59:"https://fr.dotclear.org/blog/post/2015/01/13/Dotclear-2.7.3";s:5:"title";s:14:"Dotclear 2.7.3";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:687:" Petite version de correction, qui a pour petit nom « Le Grand Duduche » et qui restaure la possibilité d'utiliser un éditeur pour la description des catégories, rétablit le fonctionnement correct des popups d'insertion de média, corrige la pagination dans certains cas particuliers, règle quelques messages intempestifs, etc.
+
+
+La proposition de mise à jour de votre installation devrait apparaître sur votre tableau de bord aujourd'hui ou demain (selon les réglages de votre hébergement) et un patch est disponible pour les développeurs préférant appliquer cette méthode.
";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2015-01-13T10:26:00+01:00";s:2:"TS";i:1421141160;}}}
\ No newline at end of file
diff --git a/dotclear._no/cache/cbfeed/c6/bb/c6bbda1a40ef1e72a9ad07ebaf2024f8.php b/dotclear._no/cache/cbfeed/c6/bb/c6bbda1a40ef1e72a9ad07ebaf2024f8.php
new file mode 100755
index 0000000..827708e
--- /dev/null
+++ b/dotclear._no/cache/cbfeed/c6/bb/c6bbda1a40ef1e72a9ad07ebaf2024f8.php
@@ -0,0 +1 @@
+O:10:"feedParser":7:{s:9:"feed_type";s:7:"rss 2.0";s:5:"title";s:13:"Dotclear l10n";s:4:"link";s:50:"http://services.dotclear.net/dc2.l10n/?version=2.5";s:11:"description";s:0:"";s:7:"pubdate";s:31:"Fri, 07 Mar 2014 10:23:20 +0000";s:9:"generator";s:0:"";s:5:"items";a:27:{i:0;O:8:"stdClass":9:{s:5:"title";s:2:"bn";s:4:"link";s:48:"http://download.dotclear.net/l10n/2.5/bn-2.5.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:48:"http://download.dotclear.net/l10n/2.5/bn-2.5.zip";}i:1;O:8:"stdClass":9:{s:5:"title";s:2:"ca";s:4:"link";s:48:"http://download.dotclear.net/l10n/2.5/ca-2.5.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:48:"http://download.dotclear.net/l10n/2.5/ca-2.5.zip";}i:2;O:8:"stdClass":9:{s:5:"title";s:2:"cs";s:4:"link";s:48:"http://download.dotclear.net/l10n/2.5/cs-2.5.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:48:"http://download.dotclear.net/l10n/2.5/cs-2.5.zip";}i:3;O:8:"stdClass":9:{s:5:"title";s:2:"da";s:4:"link";s:48:"http://download.dotclear.net/l10n/2.5/da-2.5.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:48:"http://download.dotclear.net/l10n/2.5/da-2.5.zip";}i:4;O:8:"stdClass":9:{s:5:"title";s:2:"de";s:4:"link";s:48:"http://download.dotclear.net/l10n/2.5/de-2.5.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:48:"http://download.dotclear.net/l10n/2.5/de-2.5.zip";}i:5;O:8:"stdClass":9:{s:5:"title";s:2:"eo";s:4:"link";s:48:"http://download.dotclear.net/l10n/2.5/eo-2.5.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:48:"http://download.dotclear.net/l10n/2.5/eo-2.5.zip";}i:6;O:8:"stdClass":9:{s:5:"title";s:2:"es";s:4:"link";s:48:"http://download.dotclear.net/l10n/2.5/es-2.5.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:48:"http://download.dotclear.net/l10n/2.5/es-2.5.zip";}i:7;O:8:"stdClass":9:{s:5:"title";s:5:"es-ar";s:4:"link";s:51:"http://download.dotclear.net/l10n/2.5/es-ar-2.5.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:51:"http://download.dotclear.net/l10n/2.5/es-ar-2.5.zip";}i:8;O:8:"stdClass":9:{s:5:"title";s:2:"eu";s:4:"link";s:48:"http://download.dotclear.net/l10n/2.5/eu-2.5.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:48:"http://download.dotclear.net/l10n/2.5/eu-2.5.zip";}i:9;O:8:"stdClass":9:{s:5:"title";s:2:"fr";s:4:"link";s:48:"http://download.dotclear.net/l10n/2.5/fr-2.5.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:48:"http://download.dotclear.net/l10n/2.5/fr-2.5.zip";}i:10;O:8:"stdClass":9:{s:5:"title";s:2:"hu";s:4:"link";s:48:"http://download.dotclear.net/l10n/2.5/hu-2.5.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:48:"http://download.dotclear.net/l10n/2.5/hu-2.5.zip";}i:11;O:8:"stdClass":9:{s:5:"title";s:2:"it";s:4:"link";s:48:"http://download.dotclear.net/l10n/2.5/it-2.5.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:48:"http://download.dotclear.net/l10n/2.5/it-2.5.zip";}i:12;O:8:"stdClass":9:{s:5:"title";s:2:"ja";s:4:"link";s:48:"http://download.dotclear.net/l10n/2.5/ja-2.5.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:48:"http://download.dotclear.net/l10n/2.5/ja-2.5.zip";}i:13;O:8:"stdClass":9:{s:5:"title";s:2:"ko";s:4:"link";s:48:"http://download.dotclear.net/l10n/2.5/ko-2.5.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:48:"http://download.dotclear.net/l10n/2.5/ko-2.5.zip";}i:14;O:8:"stdClass":9:{s:5:"title";s:2:"lt";s:4:"link";s:48:"http://download.dotclear.net/l10n/2.5/lt-2.5.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:48:"http://download.dotclear.net/l10n/2.5/lt-2.5.zip";}i:15;O:8:"stdClass":9:{s:5:"title";s:2:"nl";s:4:"link";s:48:"http://download.dotclear.net/l10n/2.5/nl-2.5.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:48:"http://download.dotclear.net/l10n/2.5/nl-2.5.zip";}i:16;O:8:"stdClass":9:{s:5:"title";s:2:"oc";s:4:"link";s:48:"http://download.dotclear.net/l10n/2.5/oc-2.5.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:48:"http://download.dotclear.net/l10n/2.5/oc-2.5.zip";}i:17;O:8:"stdClass":9:{s:5:"title";s:2:"pl";s:4:"link";s:48:"http://download.dotclear.net/l10n/2.5/pl-2.5.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:48:"http://download.dotclear.net/l10n/2.5/pl-2.5.zip";}i:18;O:8:"stdClass":9:{s:5:"title";s:2:"pt";s:4:"link";s:48:"http://download.dotclear.net/l10n/2.5/pt-2.5.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:48:"http://download.dotclear.net/l10n/2.5/pt-2.5.zip";}i:19;O:8:"stdClass":9:{s:5:"title";s:5:"pt-br";s:4:"link";s:51:"http://download.dotclear.net/l10n/2.5/pt-br-2.5.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:51:"http://download.dotclear.net/l10n/2.5/pt-br-2.5.zip";}i:20;O:8:"stdClass":9:{s:5:"title";s:2:"ro";s:4:"link";s:48:"http://download.dotclear.net/l10n/2.5/ro-2.5.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:48:"http://download.dotclear.net/l10n/2.5/ro-2.5.zip";}i:21;O:8:"stdClass":9:{s:5:"title";s:2:"ru";s:4:"link";s:48:"http://download.dotclear.net/l10n/2.5/ru-2.5.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:48:"http://download.dotclear.net/l10n/2.5/ru-2.5.zip";}i:22;O:8:"stdClass":9:{s:5:"title";s:2:"sr";s:4:"link";s:48:"http://download.dotclear.net/l10n/2.5/sr-2.5.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:48:"http://download.dotclear.net/l10n/2.5/sr-2.5.zip";}i:23;O:8:"stdClass":9:{s:5:"title";s:2:"sv";s:4:"link";s:48:"http://download.dotclear.net/l10n/2.5/sv-2.5.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:48:"http://download.dotclear.net/l10n/2.5/sv-2.5.zip";}i:24;O:8:"stdClass":9:{s:5:"title";s:2:"te";s:4:"link";s:48:"http://download.dotclear.net/l10n/2.5/te-2.5.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:48:"http://download.dotclear.net/l10n/2.5/te-2.5.zip";}i:25;O:8:"stdClass":9:{s:5:"title";s:2:"tr";s:4:"link";s:48:"http://download.dotclear.net/l10n/2.5/tr-2.5.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:48:"http://download.dotclear.net/l10n/2.5/tr-2.5.zip";}i:26;O:8:"stdClass":9:{s:5:"title";s:5:"zh-cn";s:4:"link";s:51:"http://download.dotclear.net/l10n/2.5/zh-cn-2.5.zip";s:7:"creator";s:0:"";s:11:"description";s:0:"";s:7:"content";s:0:"";s:7:"subject";a:0:{}s:7:"pubdate";s:0:"";s:2:"TS";b:0;s:4:"guid";s:51:"http://download.dotclear.net/l10n/2.5/zh-cn-2.5.zip";}}}
\ No newline at end of file
diff --git a/dotclear._no/cache/cbtpl/09/31/09315998350115f733c3b2cb4e812607.php b/dotclear._no/cache/cbtpl/09/31/09315998350115f733c3b2cb4e812607.php
new file mode 100755
index 0000000..8559412
--- /dev/null
+++ b/dotclear._no/cache/cbtpl/09/31/09315998350115f733c3b2cb4e812607.php
@@ -0,0 +1,76 @@
+ blog->settings->system->theme,array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogThemeURL'); ?>/style.css" media="screen" />
+
+
+
+
+
+
+tpl->getData('user_head.html'); } catch (Exception $e) {} ?>
+
+hasBehavior('publicHeadContent')) { $core->callBehavior('publicHeadContent',$core,$_ctx);} ?>
\ No newline at end of file
diff --git a/dotclear._no/cache/cbtpl/1f/21/1f21b802428f4835767ed0d11349a88f.php b/dotclear._no/cache/cbtpl/1f/21/1f21b802428f4835767ed0d11349a88f.php
new file mode 100755
index 0000000..8ed0fe9
--- /dev/null
+++ b/dotclear._no/cache/cbtpl/1f/21/1f21b802428f4835767ed0d11349a88f.php
@@ -0,0 +1,46 @@
+ right_sidebar != "none"): ?>
+
+
+
+
+
+
+ left_sidebar != "none"): ?>
+
+
+
+
diff --git a/dotclear._no/cache/cbtpl/2d/e9/2de93fb7e0213f201c5db70d40e4e675.php b/dotclear._no/cache/cbtpl/2d/e9/2de93fb7e0213f201c5db70d40e4e675.php
new file mode 100755
index 0000000..cf7fbec
--- /dev/null
+++ b/dotclear._no/cache/cbtpl/2d/e9/2de93fb7e0213f201c5db70d40e4e675.php
@@ -0,0 +1,408 @@
+xml version="1.0" encoding="utf-8""; ?>
+
+
+ blog->name,array (
+ 0 => NULL,
+ 'encode_xml' => '1',
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogName'); ?>feed_subtitle !== null) { echo context::global_filters($_ctx->feed_subtitle,array (
+ 0 => NULL,
+ 'encode_xml' => '1',
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'SysFeedSubtitle');} ?>
+ blog->desc,array (
+ 0 => NULL,
+ 'encode_xml' => '1',
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogDescription'); ?>
+
+
+ blog->upddt,$core->blog->settings->system->blog_timezone),array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+ 'iso8601' => '1',
+),'BlogUpdateDate'); ?>
+
+ blog->settings->system->editor,array (
+ 0 => NULL,
+ 'encode_xml' => '1',
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogEditor'); ?>
+
+ blog->uid,array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogFeedID'); ?>
+ Dotclear
+
+ exists("meta") && ($_ctx->meta->meta_type == "tag")) { if (!isset($params)) { $params = []; }
+if (!isset($params['from'])) { $params['from'] = ''; }
+if (!isset($params['sql'])) { $params['sql'] = ''; }
+$params['from'] .= ', '.$core->prefix.'meta META ';
+$params['sql'] .= 'AND META.post_id = P.post_id ';
+$params['sql'] .= "AND META.meta_type = 'tag' ";
+$params['sql'] .= "AND META.meta_id = '".$core->con->escape($_ctx->meta->meta_id)."' ";
+} ?>
+nb_entry_first_page; $nb_entry_per_page = $_ctx->nb_entry_per_page;
+if (($core->url->type == 'default') || ($core->url->type == 'default-page')) {
+ $params['limit'] = ($_page_number == 1 ? $nb_entry_first_page : $nb_entry_per_page);
+} else {
+ $params['limit'] = $nb_entry_per_page;
+}
+if (($core->url->type == 'default') || ($core->url->type == 'default-page')) {
+ $params['limit'] = [($_page_number == 1 ? 0 : ($_page_number - 2) * $nb_entry_per_page + $nb_entry_first_page),$params['limit']];
+} else {
+ $params['limit'] = [($_page_number - 1) * $nb_entry_per_page,$params['limit']];
+}
+if ($_ctx->exists("users")) { $params['user_id'] = $_ctx->users->user_id; }
+if ($_ctx->exists("categories")) { $params['cat_id'] = $_ctx->categories->cat_id.($core->blog->settings->system->inc_subcats?' ?sub':'');}
+if ($_ctx->exists("archives")) { $params['post_year'] = $_ctx->archives->year(); $params['post_month'] = $_ctx->archives->month(); unset($params['limit']); }
+if ($_ctx->exists("langs")) { $params['post_lang'] = $_ctx->langs->post_lang; }
+if (isset($_search)) { $params['search'] = $_search; }
+$params['order'] = 'post_dt desc';
+$_ctx->post_params = $params;
+$_ctx->posts = $core->blog->getPosts($params); unset($params);
+?>
+posts->fetch()) : ?>
+
+
+ posts->post_title,array (
+ 0 => NULL,
+ 'encode_xml' => '1',
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'EntryTitle'); ?>
+
+ posts->getFeedID(),array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'EntryFeedID'); ?>
+ posts->getISO8601Date(''),array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+ 'iso8601' => '1',
+),'EntryDate'); ?>
+ posts->isRepublished()) : ?>
+ posts->getISO8601Date('upddt'),array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+ 'iso8601' => '1',
+ 'upddt' => '1',
+),'EntryDate'); ?>
+
+ posts->isRepublished()) : ?>
+ posts->getISO8601Date(''),array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+ 'iso8601' => '1',
+),'EntryDate'); ?>
+
+ posts->getAuthorCN(),array (
+ 0 => NULL,
+ 'encode_xml' => '1',
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'EntryAuthorCommonName'); ?>
+ posts->cat_id) : ?>
+ posts->cat_title,array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => '1',
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'EntryCategory'); ?>
+
+ meta = $core->meta->getMetaRecordset($_ctx->posts->post_meta,'tag'); $_ctx->meta->sort('meta_id_lower','asc'); ?>meta->fetch()) : ?>meta->meta_id,array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'TagID'); ?> meta = null; ?>
+
+ posts->getExcerpt(1),array (
+ 0 => NULL,
+ 'encode_xml' => '1',
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+ 'absolute_urls' => '1',
+),'EntryExcerpt'); ?>
+ posts->getContent(1),array (
+ 0 => NULL,
+ 'encode_xml' => '1',
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+ 'absolute_urls' => '1',
+),'EntryContent'); ?>
+
+ posts !== null && $core->media) {
+$_ctx->attachments = new ArrayObject($core->media->getPostMedia($_ctx->posts->post_id,null,"attachment"));
+?>
+attachments as $attach_i => $attach_f) : $GLOBALS['attach_i'] = $attach_i; $GLOBALS['attach_f'] = $attach_f;$_ctx->file_url = $attach_f->file_url; ?>
+
+ attachments = null; unset($attach_i,$attach_f,$_ctx->file_url); ?>
+
+
+
+ posts->commentsActive()) : ?>
+ posts->getURL(),array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'EntryURL'); ?>#comment-form
+ blog->url.$core->url->getURLFor("feed","atom"),array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+ 'type' => 'atom',
+),'BlogFeedURL'); ?>/comments/posts->post_id,array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'EntryID'); ?>
+
+
+ posts = null; $_ctx->post_params = null; ?>
+
+
diff --git a/dotclear._no/cache/cbtpl/2d/f6/2df6477e48a35b4a9ac931532168d140.php b/dotclear._no/cache/cbtpl/2d/f6/2df6477e48a35b4a9ac931532168d140.php
new file mode 100755
index 0000000..df9ac8f
--- /dev/null
+++ b/dotclear._no/cache/cbtpl/2d/f6/2df6477e48a35b4a9ac931532168d140.php
@@ -0,0 +1,239 @@
+
+
+
+
+
+
+
+ - blog->name,array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => '1',
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogName'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogArchiveURL'); ?>" />
+ NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+ 'type' => 'atom',
+),'BlogFeedURL'); ?>" />
+
+
+ tpl->getData('_head.html'); } catch (Exception $e) {} ?>
+
+
+
+
+
+
+
+
+ tpl->getData('_top.html'); } catch (Exception $e) {} ?>
+
+
+
+
+ tpl->getData('_footer.html'); } catch (Exception $e) {} ?>
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dotclear._no/cache/cbtpl/6e/ca/6ecaaccf72103bf9ac34b331306f1665.php b/dotclear._no/cache/cbtpl/6e/ca/6ecaaccf72103bf9ac34b331306f1665.php
new file mode 100755
index 0000000..8b13789
--- /dev/null
+++ b/dotclear._no/cache/cbtpl/6e/ca/6ecaaccf72103bf9ac34b331306f1665.php
@@ -0,0 +1 @@
+
diff --git a/dotclear._no/cache/cbtpl/7b/fa/7bfaaf7718aa7bbfc051b7260b15fa8e.php b/dotclear._no/cache/cbtpl/7b/fa/7bfaaf7718aa7bbfc051b7260b15fa8e.php
new file mode 100755
index 0000000..9b106b0
--- /dev/null
+++ b/dotclear._no/cache/cbtpl/7b/fa/7bfaaf7718aa7bbfc051b7260b15fa8e.php
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/dotclear._no/cache/cbtpl/82/aa/82aa1e4f810724a82374841429a9376b.php b/dotclear._no/cache/cbtpl/82/aa/82aa1e4f810724a82374841429a9376b.php
new file mode 100755
index 0000000..74721b4
--- /dev/null
+++ b/dotclear._no/cache/cbtpl/82/aa/82aa1e4f810724a82374841429a9376b.php
@@ -0,0 +1,278 @@
+
+
+
+
+
+
+
+ - blog->name,array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => '1',
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogName'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogArchiveURL'); ?>" title="" />
+ NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogrollXbelLink'); ?>" />
+
+
+ tpl->getData('_head.html'); } catch (Exception $e) {} ?>
+
+
+
+
+
+
+
+
+ tpl->getData('_top.html'); } catch (Exception $e) {} ?>
+
+
+
+
+ tpl->getData('_footer.html'); } catch (Exception $e) {} ?>
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dotclear._no/cache/cbtpl/89/8d/898d4b85426a94208cbc931df8b9741a.php b/dotclear._no/cache/cbtpl/89/8d/898d4b85426a94208cbc931df8b9741a.php
new file mode 100755
index 0000000..5643551
--- /dev/null
+++ b/dotclear._no/cache/cbtpl/89/8d/898d4b85426a94208cbc931df8b9741a.php
@@ -0,0 +1,302 @@
+
+
+
+
+
+
+ - blog->name,array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => '1',
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogName'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+ exists("categories")) { $params['cat_id'] = $_ctx->categories->cat_id; }
+$_ctx->archives = $core->blog->getDates($params); unset($params);
+?>
+archives->fetch()) : ?>
+
+ archives = null; ?>
+
+ NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+ 'type' => 'atom',
+),'BlogFeedURL'); ?>" />
+
+ tpl->getData('_head.html'); } catch (Exception $e) {} ?>
+
+
+
+
+
+ tpl->getData('_top.html'); } catch (Exception $e) {} ?>
+
+
+
+ tpl->getData('_footer.html'); } catch (Exception $e) {} ?>
+
+
+
+
\ No newline at end of file
diff --git a/dotclear._no/cache/cbtpl/a6/a7/a6a7f3d2302afed936235a1d2f021f6b.php b/dotclear._no/cache/cbtpl/a6/a7/a6a7f3d2302afed936235a1d2f021f6b.php
new file mode 100755
index 0000000..f3d59ae
--- /dev/null
+++ b/dotclear._no/cache/cbtpl/a6/a7/a6a7f3d2302afed936235a1d2f021f6b.php
@@ -0,0 +1,76 @@
+
+
+ |
+ |
+
\ No newline at end of file
diff --git a/dotclear._no/cache/cbtpl/b5/47/b54799309247e12ba627e4a432ae206d.php b/dotclear._no/cache/cbtpl/b5/47/b54799309247e12ba627e4a432ae206d.php
new file mode 100755
index 0000000..3e85b03
--- /dev/null
+++ b/dotclear._no/cache/cbtpl/b5/47/b54799309247e12ba627e4a432ae206d.php
@@ -0,0 +1,841 @@
+
+
+
+
+
+
+
+ - meta->meta_id,array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'TagID'); ?> - blog->name,array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => '1',
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogName'); ?> - NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'PaginationCurrent'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogArchiveURL'); ?>" title="" />
+
+ exists("meta") && ($_ctx->meta->meta_type == "tag")) { if (!isset($params)) { $params = []; }
+if (!isset($params['from'])) { $params['from'] = ''; }
+if (!isset($params['sql'])) { $params['sql'] = ''; }
+$params['from'] .= ', '.$core->prefix.'meta META ';
+$params['sql'] .= 'AND META.post_id = P.post_id ';
+$params['sql'] .= "AND META.meta_type = 'tag' ";
+$params['sql'] .= "AND META.meta_id = '".$core->con->escape($_ctx->meta->meta_id)."' ";
+} ?>
+nb_entry_first_page; $nb_entry_per_page = $_ctx->nb_entry_per_page;
+if (($core->url->type == 'default') || ($core->url->type == 'default-page')) {
+ $params['limit'] = ($_page_number == 1 ? $nb_entry_first_page : $nb_entry_per_page);
+} else {
+ $params['limit'] = $nb_entry_per_page;
+}
+if (($core->url->type == 'default') || ($core->url->type == 'default-page')) {
+ $params['limit'] = [($_page_number == 1 ? 0 : ($_page_number - 2) * $nb_entry_per_page + $nb_entry_first_page),$params['limit']];
+} else {
+ $params['limit'] = [($_page_number - 1) * $nb_entry_per_page,$params['limit']];
+}
+if ($_ctx->exists("users")) { $params['user_id'] = $_ctx->users->user_id; }
+if ($_ctx->exists("categories")) { $params['cat_id'] = $_ctx->categories->cat_id.($core->blog->settings->system->inc_subcats?' ?sub':'');}
+if ($_ctx->exists("archives")) { $params['post_year'] = $_ctx->archives->year(); $params['post_month'] = $_ctx->archives->month(); unset($params['limit']); }
+if ($_ctx->exists("langs")) { $params['post_lang'] = $_ctx->langs->post_lang; }
+if (isset($_search)) { $params['search'] = $_search; }
+$params['order'] = 'post_dt desc';
+$params['no_content'] = true;
+$_ctx->post_params = $params;
+$_ctx->posts = $core->blog->getPosts($params); unset($params);
+?>
+posts->fetch()) : ?>
+ posts->isStart()) : ?>
+ post_params;
+$_ctx->pagination = $core->blog->getPosts($params,true); unset($params);
+?>
+pagination->f(0) > $_ctx->posts->count()) : ?>
+
+
+
+
+
+
+
+
+
+
+
+ posts = null; $_ctx->post_params = null; ?>
+
+ meta->meta_id)."/atom"),array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+ 'type' => 'atom',
+),'TagFeedURL'); ?>" />
+ NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogrollXbelLink'); ?>" />
+
+
+ tpl->getData('_head.html'); } catch (Exception $e) {} ?>
+
+
+
+
+
+
+
+
+ tpl->getData('_top.html'); } catch (Exception $e) {} ?>
+
+
+
+
+ tpl->getData('_footer.html'); } catch (Exception $e) {} ?>
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dotclear._no/cache/cbtpl/b7/a3/b7a310526ad1b94eeef72859d34888bc.php b/dotclear._no/cache/cbtpl/b7/a3/b7a310526ad1b94eeef72859d34888bc.php
new file mode 100755
index 0000000..6e6858a
--- /dev/null
+++ b/dotclear._no/cache/cbtpl/b7/a3/b7a310526ad1b94eeef72859d34888bc.php
@@ -0,0 +1,825 @@
+
+
+
+
+
+
+ - NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => '1',
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'SysSearchString'),$_search_count);} ?> - blog->name,array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => '1',
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogName'); ?> - NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'PaginationCurrent'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+ NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogArchiveURL'); ?>" title="" />
+
+ exists("meta") && ($_ctx->meta->meta_type == "tag")) { if (!isset($params)) { $params = []; }
+if (!isset($params['from'])) { $params['from'] = ''; }
+if (!isset($params['sql'])) { $params['sql'] = ''; }
+$params['from'] .= ', '.$core->prefix.'meta META ';
+$params['sql'] .= 'AND META.post_id = P.post_id ';
+$params['sql'] .= "AND META.meta_type = 'tag' ";
+$params['sql'] .= "AND META.meta_id = '".$core->con->escape($_ctx->meta->meta_id)."' ";
+} ?>
+nb_entry_first_page; $nb_entry_per_page = $_ctx->nb_entry_per_page;
+if (($core->url->type == 'default') || ($core->url->type == 'default-page')) {
+ $params['limit'] = ($_page_number == 1 ? $nb_entry_first_page : $nb_entry_per_page);
+} else {
+ $params['limit'] = $nb_entry_per_page;
+}
+if (($core->url->type == 'default') || ($core->url->type == 'default-page')) {
+ $params['limit'] = [($_page_number == 1 ? 0 : ($_page_number - 2) * $nb_entry_per_page + $nb_entry_first_page),$params['limit']];
+} else {
+ $params['limit'] = [($_page_number - 1) * $nb_entry_per_page,$params['limit']];
+}
+if ($_ctx->exists("users")) { $params['user_id'] = $_ctx->users->user_id; }
+if ($_ctx->exists("categories")) { $params['cat_id'] = $_ctx->categories->cat_id.($core->blog->settings->system->inc_subcats?' ?sub':'');}
+if ($_ctx->exists("archives")) { $params['post_year'] = $_ctx->archives->year(); $params['post_month'] = $_ctx->archives->month(); unset($params['limit']); }
+if ($_ctx->exists("langs")) { $params['post_lang'] = $_ctx->langs->post_lang; }
+if (isset($_search)) { $params['search'] = $_search; }
+$params['order'] = 'post_dt desc';
+$params['no_content'] = true;
+$_ctx->post_params = $params;
+$_ctx->posts = $core->blog->getPosts($params); unset($params);
+?>
+posts->fetch()) : ?>
+ posts->isStart()) : ?>
+ post_params;
+$_ctx->pagination = $core->blog->getPosts($params,true); unset($params);
+?>
+pagination->f(0) > $_ctx->posts->count()) : ?>
+
+
+
+
+
+
+
+
+
+
+
+ posts = null; $_ctx->post_params = null; ?>
+
+ NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogrollXbelLink'); ?>" />
+
+ tpl->getData('_head.html'); } catch (Exception $e) {} ?>
+
+
+
+
+
+ tpl->getData('_top.html'); } catch (Exception $e) {} ?>
+
+
+
+
+ tpl->getData('_footer.html'); } catch (Exception $e) {} ?>
+
+
+
+
\ No newline at end of file
diff --git a/dotclear._no/cache/cbtpl/c9/31/c931260fa34f89068ddf8d33120db085.php b/dotclear._no/cache/cbtpl/c9/31/c931260fa34f89068ddf8d33120db085.php
new file mode 100755
index 0000000..81a8d2e
--- /dev/null
+++ b/dotclear._no/cache/cbtpl/c9/31/c931260fa34f89068ddf8d33120db085.php
@@ -0,0 +1,1250 @@
+
+
+
+
+
+
+
+
+ posts->post_title,array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => '1',
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'EntryTitle'); ?> - blog->name,array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => '1',
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogName'); ?>
+
+
+
+
+
+
+
+
+ posts->getContent(0),array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => '1',
+ 'cut_string' => '180',
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => '1',
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+ 'full' => '1',
+),'EntryContent'); ?>" />
+
+
+
+
+
+
+ NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogArchiveURL'); ?>" title="" />
+ posts->trackbacksActive()) : ?>
+
+ blog->getNextPost($_ctx->posts,1,0,0); ?>
+posts = $next_post; unset($next_post);
+while ($_ctx->posts->fetch()) : ?> posts = null; ?>
+
+
+ blog->getNextPost($_ctx->posts,-1,0,0); ?>
+posts = $prev_post; unset($prev_post);
+while ($_ctx->posts->fetch()) : ?> posts = null; ?>
+
+
+ NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+ 'type' => 'atom',
+),'BlogFeedURL'); ?>" />
+
+
+ tpl->getData('_head.html'); } catch (Exception $e) {} ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+ posts->trackbacksActive()) { echo $_ctx->posts->getTrackbackData('html'); } ?>
+
+
+ tpl->getData('_top.html'); } catch (Exception $e) {} ?>
+
+
+
+
+
+ tpl->getData('_footer.html'); } catch (Exception $e) {} ?>
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dotclear._no/cache/cbtpl/ca/03/ca03ea0ee4abc80b4c61fcb8492f18fc.php b/dotclear._no/cache/cbtpl/ca/03/ca03ea0ee4abc80b4c61fcb8492f18fc.php
new file mode 100755
index 0000000..f33df2b
--- /dev/null
+++ b/dotclear._no/cache/cbtpl/ca/03/ca03ea0ee4abc80b4c61fcb8492f18fc.php
@@ -0,0 +1,426 @@
+xml version="1.0" encoding="utf-8""; ?>
+
+
+ blog->name,array (
+ 0 => NULL,
+ 'encode_xml' => '1',
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogName'); ?>feed_subtitle !== null) { echo context::global_filters($_ctx->feed_subtitle,array (
+ 0 => NULL,
+ 'encode_xml' => '1',
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'SysFeedSubtitle');} ?>
+ -
+ blog->desc,array (
+ 0 => NULL,
+ 'encode_xml' => '1',
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogDescription'); ?>
+
+
+ blog->upddt,$core->blog->settings->system->blog_timezone),array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+ 'iso8601' => '1',
+),'BlogUpdateDate'); ?>
+
+ blog->settings->system->editor,array (
+ 0 => NULL,
+ 'encode_xml' => '1',
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogEditor'); ?>
+
+ blog->uid,array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogFeedID'); ?>
+ Dotclear
+
+ exists("meta") && ($_ctx->meta->meta_type == "tag")) { if (!isset($params)) { $params = []; }
+if (!isset($params['from'])) { $params['from'] = ''; }
+if (!isset($params['sql'])) { $params['sql'] = ''; }
+$params['from'] .= ', '.$core->prefix.'meta META ';
+$params['sql'] .= 'AND META.post_id = P.post_id ';
+$params['sql'] .= "AND META.meta_type = 'tag' ";
+$params['sql'] .= "AND META.meta_id = '".$core->con->escape($_ctx->meta->meta_id)."' ";
+} ?>
+nb_comment_per_page !== null) { $params['limit'] = $_ctx->nb_comment_per_page; }
+if ($_ctx->posts !== null) { $params['post_id'] = $_ctx->posts->post_id; $core->blog->withoutPassword(false);
+}
+if ($_ctx->exists("categories")) { $params['cat_id'] = $_ctx->categories->cat_id; }
+if ($_ctx->exists("langs")) { $params['sql'] = "AND P.post_lang = '".$core->blog->con->escape($_ctx->langs->post_lang)."' "; }
+$params['order'] = 'comment_dt desc';
+$_ctx->comments = $core->blog->getComments($params); unset($params);
+if ($_ctx->posts !== null) { $core->blog->withoutPassword(true);}
+$_ctx->pings = $_ctx->comments;
+?>
+comments->fetch()) : ?>
+
+ comments->comment_trackback) : ?>
+
+ [ping] pings->post_title,array (
+ 0 => NULL,
+ 'encode_xml' => '1',
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'PingEntryTitle'); ?> - pings->comment_author,array (
+ 0 => NULL,
+ 'encode_xml' => '1',
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'PingBlogName'); ?>
+
+ pings->getFeedID(),array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'PingFeedID'); ?>
+ pings->getISO8601Date(''),array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+ 'iso8601' => '1',
+),'PingDate'); ?>
+ pings->getISO8601Date('upddt'),array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+ 'iso8601' => '1',
+ 'upddt' => '1',
+),'PingDate'); ?>
+ pings->comment_author,array (
+ 0 => NULL,
+ 'encode_xml' => '1',
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'PingBlogName'); ?>
+ <p><a href="pings->getAuthorURL(),array (
+ 0 => NULL,
+ 'encode_xml' => '1',
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'PingAuthorURL'); ?>">pings->getTrackbackTitle(),array (
+ 0 => NULL,
+ 'encode_xml' => '1',
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'PingTitle'); ?></a></p> pings->getTrackbackContent(),array (
+ 0 => NULL,
+ 'encode_xml' => '1',
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'PingContent'); ?>
+
+
+
+
+ comments->comment_trackback) : ?>
+
+ comments->post_title,array (
+ 0 => NULL,
+ 'encode_xml' => '1',
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'CommentEntryTitle'); ?> - comments->comment_author,array (
+ 0 => NULL,
+ 'encode_xml' => '1',
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'CommentAuthor'); ?>
+
+ comments->getFeedID(),array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'CommentFeedID'); ?>
+ comments->getISO8601Date(''),array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+ 'iso8601' => '1',
+),'CommentDate'); ?>
+ comments->getISO8601Date('upddt'),array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+ 'iso8601' => '1',
+ 'upddt' => '1',
+),'CommentDate'); ?>
+ comments->comment_author,array (
+ 0 => NULL,
+ 'encode_xml' => '1',
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'CommentAuthor'); ?>
+ comments->getContent(1),array (
+ 0 => NULL,
+ 'encode_xml' => '1',
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+ 'absolute_urls' => '1',
+),'CommentContent'); ?>
+
+
+ comments = null; ?>
+
+
diff --git a/dotclear._no/cache/cbtpl/ca/f1/caf17d33597c5ae2ef9ec9bb595a2eb6.php b/dotclear._no/cache/cbtpl/ca/f1/caf17d33597c5ae2ef9ec9bb595a2eb6.php
new file mode 100755
index 0000000..e7d419e
--- /dev/null
+++ b/dotclear._no/cache/cbtpl/ca/f1/caf17d33597c5ae2ef9ec9bb595a2eb6.php
@@ -0,0 +1,917 @@
+
+
+
+
+
+
+
+ categories->cat_title,array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => '1',
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'CategoryTitle'); ?> - blog->name,array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => '1',
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogName'); ?> - NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'PaginationCurrent'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogArchiveURL'); ?>" title="" />
+
+ exists("meta") && ($_ctx->meta->meta_type == "tag")) { if (!isset($params)) { $params = []; }
+if (!isset($params['from'])) { $params['from'] = ''; }
+if (!isset($params['sql'])) { $params['sql'] = ''; }
+$params['from'] .= ', '.$core->prefix.'meta META ';
+$params['sql'] .= 'AND META.post_id = P.post_id ';
+$params['sql'] .= "AND META.meta_type = 'tag' ";
+$params['sql'] .= "AND META.meta_id = '".$core->con->escape($_ctx->meta->meta_id)."' ";
+} ?>
+nb_entry_first_page; $nb_entry_per_page = $_ctx->nb_entry_per_page;
+if (($core->url->type == 'default') || ($core->url->type == 'default-page')) {
+ $params['limit'] = ($_page_number == 1 ? $nb_entry_first_page : $nb_entry_per_page);
+} else {
+ $params['limit'] = $nb_entry_per_page;
+}
+if (($core->url->type == 'default') || ($core->url->type == 'default-page')) {
+ $params['limit'] = [($_page_number == 1 ? 0 : ($_page_number - 2) * $nb_entry_per_page + $nb_entry_first_page),$params['limit']];
+} else {
+ $params['limit'] = [($_page_number - 1) * $nb_entry_per_page,$params['limit']];
+}
+if ($_ctx->exists("users")) { $params['user_id'] = $_ctx->users->user_id; }
+if ($_ctx->exists("categories")) { $params['cat_id'] = $_ctx->categories->cat_id.($core->blog->settings->system->inc_subcats?' ?sub':'');}
+if ($_ctx->exists("archives")) { $params['post_year'] = $_ctx->archives->year(); $params['post_month'] = $_ctx->archives->month(); unset($params['limit']); }
+if ($_ctx->exists("langs")) { $params['post_lang'] = $_ctx->langs->post_lang; }
+if (isset($_search)) { $params['search'] = $_search; }
+$params['order'] = 'post_dt desc';
+$params['no_content'] = true;
+$_ctx->post_params = $params;
+$_ctx->posts = $core->blog->getPosts($params); unset($params);
+?>
+posts->fetch()) : ?>
+ posts->isStart()) : ?>
+ post_params;
+$_ctx->pagination = $core->blog->getPosts($params,true); unset($params);
+?>
+pagination->f(0) > $_ctx->posts->count()) : ?>
+
+
+
+
+
+
+
+
+
+
+
+ posts = null; $_ctx->post_params = null; ?>
+
+ categories->cat_url."/atom"),array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+ 'type' => 'atom',
+),'CategoryFeedURL'); ?>" />
+
+
+ tpl->getData('_head.html'); } catch (Exception $e) {} ?>
+
+
+
+
+
+
+
+
+ tpl->getData('_top.html'); } catch (Exception $e) {} ?>
+
+
+
+
+ tpl->getData('_footer.html'); } catch (Exception $e) {} ?>
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dotclear._no/cache/cbtpl/d0/81/d08191ecbcfb65b2250607f9779e1dfb.php b/dotclear._no/cache/cbtpl/d0/81/d08191ecbcfb65b2250607f9779e1dfb.php
new file mode 100755
index 0000000..b53346e
--- /dev/null
+++ b/dotclear._no/cache/cbtpl/d0/81/d08191ecbcfb65b2250607f9779e1dfb.php
@@ -0,0 +1,830 @@
+
+
+
+
+
+
+
+ blog->name,array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => '1',
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogName'); ?> - NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'PaginationCurrent'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogArchiveURL'); ?>" />
+ categories = $core->blog->getCategories($params);
+?>
+categories->fetch()) : ?>
+ categories->cat_url),array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'CategoryURL'); ?>" title="categories->cat_title,array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => '1',
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'CategoryTitle'); ?>" />
+ categories = null; unset($params); ?>
+
+ exists("meta") && ($_ctx->meta->meta_type == "tag")) { if (!isset($params)) { $params = []; }
+if (!isset($params['from'])) { $params['from'] = ''; }
+if (!isset($params['sql'])) { $params['sql'] = ''; }
+$params['from'] .= ', '.$core->prefix.'meta META ';
+$params['sql'] .= 'AND META.post_id = P.post_id ';
+$params['sql'] .= "AND META.meta_type = 'tag' ";
+$params['sql'] .= "AND META.meta_id = '".$core->con->escape($_ctx->meta->meta_id)."' ";
+} ?>
+nb_entry_first_page; $nb_entry_per_page = $_ctx->nb_entry_per_page;
+if (($core->url->type == 'default') || ($core->url->type == 'default-page')) {
+ $params['limit'] = ($_page_number == 1 ? $nb_entry_first_page : $nb_entry_per_page);
+} else {
+ $params['limit'] = $nb_entry_per_page;
+}
+if (($core->url->type == 'default') || ($core->url->type == 'default-page')) {
+ $params['limit'] = [($_page_number == 1 ? 0 : ($_page_number - 2) * $nb_entry_per_page + $nb_entry_first_page),$params['limit']];
+} else {
+ $params['limit'] = [($_page_number - 1) * $nb_entry_per_page,$params['limit']];
+}
+if ($_ctx->exists("users")) { $params['user_id'] = $_ctx->users->user_id; }
+if ($_ctx->exists("categories")) { $params['cat_id'] = $_ctx->categories->cat_id.($core->blog->settings->system->inc_subcats?' ?sub':'');}
+if ($_ctx->exists("archives")) { $params['post_year'] = $_ctx->archives->year(); $params['post_month'] = $_ctx->archives->month(); unset($params['limit']); }
+if ($_ctx->exists("langs")) { $params['post_lang'] = $_ctx->langs->post_lang; }
+if (isset($_search)) { $params['search'] = $_search; }
+$params['order'] = 'post_dt desc';
+$params['no_content'] = true;
+$_ctx->post_params = $params;
+$_ctx->posts = $core->blog->getPosts($params); unset($params);
+?>
+posts->fetch()) : ?>
+ posts->isStart()) : ?>
+ post_params;
+$_ctx->pagination = $core->blog->getPosts($params,true); unset($params);
+?>
+pagination->f(0) > $_ctx->posts->count()) : ?>
+
+
+
+
+
+
+
+
+
+
+
+ posts = null; $_ctx->post_params = null; ?>
+
+ NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+ 'type' => 'atom',
+),'BlogFeedURL'); ?>" />
+
+ NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogrollXbelLink'); ?>" />
+
+
+ tpl->getData('_head.html'); } catch (Exception $e) {} ?>
+
+
+
+
+
+
+
+
+
+
+ tpl->getData('_top.html'); } catch (Exception $e) {} ?>
+
+
+
+
+ tpl->getData('_footer.html'); } catch (Exception $e) {} ?>
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dotclear._no/cache/cbtpl/d1/6d/d16dda30457482fdfe0336382114ddb5.php b/dotclear._no/cache/cbtpl/d1/6d/d16dda30457482fdfe0336382114ddb5.php
new file mode 100755
index 0000000..3becc38
--- /dev/null
+++ b/dotclear._no/cache/cbtpl/d1/6d/d16dda30457482fdfe0336382114ddb5.php
@@ -0,0 +1,792 @@
+
+
+
+
+
+
+
+ - archives->dt),array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'ArchiveDate'); ?> - blog->name,array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => '1',
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogName'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogArchiveURL'); ?>" title="" />
+
+ archives->dt;$_ctx->archives = $core->blog->getDates($params); unset($params);
+?>
+archives->fetch()) : ?> archives = null; ?>
+ archives->dt;$_ctx->archives = $core->blog->getDates($params); unset($params);
+?>
+archives->fetch()) : ?> archives = null; ?>
+
+ exists("meta") && ($_ctx->meta->meta_type == "tag")) { if (!isset($params)) { $params = []; }
+if (!isset($params['from'])) { $params['from'] = ''; }
+if (!isset($params['sql'])) { $params['sql'] = ''; }
+$params['from'] .= ', '.$core->prefix.'meta META ';
+$params['sql'] .= 'AND META.post_id = P.post_id ';
+$params['sql'] .= "AND META.meta_type = 'tag' ";
+$params['sql'] .= "AND META.meta_id = '".$core->con->escape($_ctx->meta->meta_id)."' ";
+} ?>
+nb_entry_first_page; $nb_entry_per_page = $_ctx->nb_entry_per_page;
+if (($core->url->type == 'default') || ($core->url->type == 'default-page')) {
+ $params['limit'] = ($_page_number == 1 ? $nb_entry_first_page : $nb_entry_per_page);
+} else {
+ $params['limit'] = $nb_entry_per_page;
+}
+if (($core->url->type == 'default') || ($core->url->type == 'default-page')) {
+ $params['limit'] = [($_page_number == 1 ? 0 : ($_page_number - 2) * $nb_entry_per_page + $nb_entry_first_page),$params['limit']];
+} else {
+ $params['limit'] = [($_page_number - 1) * $nb_entry_per_page,$params['limit']];
+}
+if ($_ctx->exists("users")) { $params['user_id'] = $_ctx->users->user_id; }
+if ($_ctx->exists("categories")) { $params['cat_id'] = $_ctx->categories->cat_id.($core->blog->settings->system->inc_subcats?' ?sub':'');}
+if ($_ctx->exists("archives")) { $params['post_year'] = $_ctx->archives->year(); $params['post_month'] = $_ctx->archives->month(); unset($params['limit']); }
+if ($_ctx->exists("langs")) { $params['post_lang'] = $_ctx->langs->post_lang; }
+if (isset($_search)) { $params['search'] = $_search; }
+$params['order'] = 'post_dt desc';
+$params['no_content'] = true;
+$_ctx->post_params = $params;
+$_ctx->posts = $core->blog->getPosts($params); unset($params);
+?>
+posts->fetch()) : ?>
+
+ posts = null; $_ctx->post_params = null; ?>
+
+
+ tpl->getData('_head.html'); } catch (Exception $e) {} ?>
+
+
+
+
+
+
+
+
+ tpl->getData('_top.html'); } catch (Exception $e) {} ?>
+
+
+
+
+ tpl->getData('_footer.html'); } catch (Exception $e) {} ?>
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dotclear._no/cache/cbtpl/f9/bf/f9bf296b9b69f6e5967d6691f76c151a.php b/dotclear._no/cache/cbtpl/f9/bf/f9bf296b9b69f6e5967d6691f76c151a.php
new file mode 100755
index 0000000..a53f394
--- /dev/null
+++ b/dotclear._no/cache/cbtpl/f9/bf/f9bf296b9b69f6e5967d6691f76c151a.php
@@ -0,0 +1,10 @@
+
+
+hasBehavior('publicFooterContent')) { $core->callBehavior('publicFooterContent',$core,$_ctx);} ?>
\ No newline at end of file
diff --git a/dotclear._no/cache/cbtpl/fd/2a/fd2a2d2bff6ca621e067387d368d3465.php b/dotclear._no/cache/cbtpl/fd/2a/fd2a2d2bff6ca621e067387d368d3465.php
new file mode 100755
index 0000000..3cb6001
--- /dev/null
+++ b/dotclear._no/cache/cbtpl/fd/2a/fd2a2d2bff6ca621e067387d368d3465.php
@@ -0,0 +1,332 @@
+
+
+
+
+
+
+
+ - blog->name,array (
+ 0 => NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => '1',
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+),'BlogName'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ exists("categories")) { $params['cat_id'] = $_ctx->categories->cat_id; }
+$_ctx->archives = $core->blog->getDates($params); unset($params);
+?>
+archives->fetch()) : ?>
+
+ archives = null; ?>
+ NULL,
+ 'encode_xml' => 0,
+ 'encode_html' => 0,
+ 'cut_string' => 0,
+ 'lower_case' => 0,
+ 'upper_case' => 0,
+ 'encode_url' => 0,
+ 'remove_html' => 0,
+ 'capitalize' => 0,
+ 'strip_tags' => 0,
+ 'type' => 'atom',
+),'BlogFeedURL'); ?>" />
+
+
+ tpl->getData('_head.html'); } catch (Exception $e) {} ?>
+
+
+
+
+
+
+
+
+ tpl->getData('_top.html'); } catch (Exception $e) {} ?>
+
+
+
+
+ tpl->getData('_footer.html'); } catch (Exception $e) {} ?>
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dotclear._no/cache/dcrepo/27/b1/27b160562394b1bec0091781ff90870c.ser b/dotclear._no/cache/dcrepo/27/b1/27b160562394b1bec0091781ff90870c.ser
new file mode 100755
index 0000000..6189982
Binary files /dev/null and b/dotclear._no/cache/dcrepo/27/b1/27b160562394b1bec0091781ff90870c.ser differ
diff --git a/dotclear._no/cache/dcrepo/68/cb/68cbfef3c40e3d56b4fb94709e2b9321.ser b/dotclear._no/cache/dcrepo/68/cb/68cbfef3c40e3d56b4fb94709e2b9321.ser
new file mode 100755
index 0000000..c87242c
Binary files /dev/null and b/dotclear._no/cache/dcrepo/68/cb/68cbfef3c40e3d56b4fb94709e2b9321.ser differ
diff --git a/dotclear._no/cache/dcrepo/e6/d0/e6d043dd6a2217aadd756285f43a5b00.ser b/dotclear._no/cache/dcrepo/e6/d0/e6d043dd6a2217aadd756285f43a5b00.ser
new file mode 100755
index 0000000..a0f494d
Binary files /dev/null and b/dotclear._no/cache/dcrepo/e6/d0/e6d043dd6a2217aadd756285f43a5b00.ser differ
diff --git a/dotclear._no/cache/dcrepo/fa/37/fa37bc9589c9a834517f9ddff94f8683.ser b/dotclear._no/cache/dcrepo/fa/37/fa37bc9589c9a834517f9ddff94f8683.ser
new file mode 100755
index 0000000..66551c6
Binary files /dev/null and b/dotclear._no/cache/dcrepo/fa/37/fa37bc9589c9a834517f9ddff94f8683.ser differ
diff --git a/dotclear._no/cache/versions/dotclear-stable b/dotclear._no/cache/versions/dotclear-stable
new file mode 100644
index 0000000..d06d2af
--- /dev/null
+++ b/dotclear._no/cache/versions/dotclear-stable
@@ -0,0 +1 @@
+a:6:{s:7:"version";s:4:"2.16";s:4:"href";s:54:"https://download.dotclear.org/latest/dotclear-2.16.zip";s:8:"checksum";s:32:"066e174f9b6f20a07ad2931dea9b729a";s:4:"info";s:55:"https://services.dotclear.net/redir?about=dotclear-2.16";s:3:"php";s:3:"5.6";s:6:"notify";b:1;}
\ No newline at end of file
diff --git a/dotclear._no/db/.htaccess b/dotclear._no/db/.htaccess
new file mode 100644
index 0000000..569316c
--- /dev/null
+++ b/dotclear._no/db/.htaccess
@@ -0,0 +1,2 @@
+Require all denied
+Deny from all
diff --git a/dotclear._no/images/00FA000000068727.TN__.jpg b/dotclear._no/images/00FA000000068727.TN__.jpg
new file mode 100644
index 0000000..f8a1f45
Binary files /dev/null and b/dotclear._no/images/00FA000000068727.TN__.jpg differ
diff --git a/dotclear._no/images/00FA000000068727.jpg b/dotclear._no/images/00FA000000068727.jpg
new file mode 100644
index 0000000..4e684c6
Binary files /dev/null and b/dotclear._no/images/00FA000000068727.jpg differ
diff --git a/dotclear._no/images/18413160.TN__.jpg b/dotclear._no/images/18413160.TN__.jpg
new file mode 100644
index 0000000..f84f320
Binary files /dev/null and b/dotclear._no/images/18413160.TN__.jpg differ
diff --git a/dotclear._no/images/18413160.jpg b/dotclear._no/images/18413160.jpg
new file mode 100644
index 0000000..4b21fd4
Binary files /dev/null and b/dotclear._no/images/18413160.jpg differ
diff --git a/dotclear._no/images/20050422-Tae-suk.TN__.jpg b/dotclear._no/images/20050422-Tae-suk.TN__.jpg
new file mode 100644
index 0000000..863ce52
Binary files /dev/null and b/dotclear._no/images/20050422-Tae-suk.TN__.jpg differ
diff --git a/dotclear._no/images/20050422-Tae-suk.jpg b/dotclear._no/images/20050422-Tae-suk.jpg
new file mode 100644
index 0000000..31741a5
Binary files /dev/null and b/dotclear._no/images/20050422-Tae-suk.jpg differ
diff --git a/dotclear._no/images/UniMainz5.TN__.jpg b/dotclear._no/images/UniMainz5.TN__.jpg
new file mode 100644
index 0000000..2f3a1b1
Binary files /dev/null and b/dotclear._no/images/UniMainz5.TN__.jpg differ
diff --git a/dotclear._no/images/UniMainz5.jpg b/dotclear._no/images/UniMainz5.jpg
new file mode 100644
index 0000000..762b5d7
Binary files /dev/null and b/dotclear._no/images/UniMainz5.jpg differ
diff --git a/dotclear._no/images/bot/IMG_0002.TN__.jpg b/dotclear._no/images/bot/IMG_0002.TN__.jpg
new file mode 100644
index 0000000..d320b92
Binary files /dev/null and b/dotclear._no/images/bot/IMG_0002.TN__.jpg differ
diff --git a/dotclear._no/images/bot/IMG_0002.jpg b/dotclear._no/images/bot/IMG_0002.jpg
new file mode 100644
index 0000000..ae70cc6
Binary files /dev/null and b/dotclear._no/images/bot/IMG_0002.jpg differ
diff --git a/dotclear._no/images/bot/IMG_0003.TN__.jpg b/dotclear._no/images/bot/IMG_0003.TN__.jpg
new file mode 100644
index 0000000..fa7a07b
Binary files /dev/null and b/dotclear._no/images/bot/IMG_0003.TN__.jpg differ
diff --git a/dotclear._no/images/bot/IMG_0003.jpg b/dotclear._no/images/bot/IMG_0003.jpg
new file mode 100644
index 0000000..ab7089a
Binary files /dev/null and b/dotclear._no/images/bot/IMG_0003.jpg differ
diff --git a/dotclear._no/images/bot/IMG_0004.TN__.jpg b/dotclear._no/images/bot/IMG_0004.TN__.jpg
new file mode 100644
index 0000000..1c3bea4
Binary files /dev/null and b/dotclear._no/images/bot/IMG_0004.TN__.jpg differ
diff --git a/dotclear._no/images/bot/IMG_0004.jpg b/dotclear._no/images/bot/IMG_0004.jpg
new file mode 100644
index 0000000..524c7bf
Binary files /dev/null and b/dotclear._no/images/bot/IMG_0004.jpg differ
diff --git a/dotclear._no/images/bot/IMG_0005.TN__.jpg b/dotclear._no/images/bot/IMG_0005.TN__.jpg
new file mode 100644
index 0000000..0039528
Binary files /dev/null and b/dotclear._no/images/bot/IMG_0005.TN__.jpg differ
diff --git a/dotclear._no/images/bot/IMG_0005.jpg b/dotclear._no/images/bot/IMG_0005.jpg
new file mode 100644
index 0000000..57fb682
Binary files /dev/null and b/dotclear._no/images/bot/IMG_0005.jpg differ
diff --git a/dotclear._no/images/chaos.TN__.jpg b/dotclear._no/images/chaos.TN__.jpg
new file mode 100644
index 0000000..cfad007
Binary files /dev/null and b/dotclear._no/images/chaos.TN__.jpg differ
diff --git a/dotclear._no/images/chaos.jpg b/dotclear._no/images/chaos.jpg
new file mode 100644
index 0000000..1e8467d
Binary files /dev/null and b/dotclear._no/images/chaos.jpg differ
diff --git a/dotclear._no/images/directory.TN__.jpg b/dotclear._no/images/directory.TN__.jpg
new file mode 100644
index 0000000..6335d17
Binary files /dev/null and b/dotclear._no/images/directory.TN__.jpg differ
diff --git a/dotclear._no/images/directory.jpg b/dotclear._no/images/directory.jpg
new file mode 100644
index 0000000..27909e3
Binary files /dev/null and b/dotclear._no/images/directory.jpg differ
diff --git a/dotclear._no/images/dotclear_pw.TN__.png b/dotclear._no/images/dotclear_pw.TN__.png
new file mode 100644
index 0000000..1f65e1e
Binary files /dev/null and b/dotclear._no/images/dotclear_pw.TN__.png differ
diff --git a/dotclear._no/images/dotclear_pw.png b/dotclear._no/images/dotclear_pw.png
new file mode 100644
index 0000000..1f65e1e
Binary files /dev/null and b/dotclear._no/images/dotclear_pw.png differ
diff --git a/dotclear._no/images/fooo2.TN__.jpg b/dotclear._no/images/fooo2.TN__.jpg
new file mode 100644
index 0000000..fc9d44d
Binary files /dev/null and b/dotclear._no/images/fooo2.TN__.jpg differ
diff --git a/dotclear._no/images/fooo2.jpg b/dotclear._no/images/fooo2.jpg
new file mode 100644
index 0000000..fa0ff6c
Binary files /dev/null and b/dotclear._no/images/fooo2.jpg differ
diff --git a/dotclear._no/images/fun/notfound.TN__.jpg b/dotclear._no/images/fun/notfound.TN__.jpg
new file mode 100644
index 0000000..5199906
Binary files /dev/null and b/dotclear._no/images/fun/notfound.TN__.jpg differ
diff --git a/dotclear._no/images/fun/notfound.jpg b/dotclear._no/images/fun/notfound.jpg
new file mode 100644
index 0000000..4d8d112
Binary files /dev/null and b/dotclear._no/images/fun/notfound.jpg differ
diff --git a/dotclear._no/images/intelinside.TN__.jpg b/dotclear._no/images/intelinside.TN__.jpg
new file mode 100644
index 0000000..5cf6613
Binary files /dev/null and b/dotclear._no/images/intelinside.TN__.jpg differ
diff --git a/dotclear._no/images/intelinside.jpg b/dotclear._no/images/intelinside.jpg
new file mode 100644
index 0000000..1842d3a
Binary files /dev/null and b/dotclear._no/images/intelinside.jpg differ
diff --git a/dotclear._no/images/locataires.TN__.jpg b/dotclear._no/images/locataires.TN__.jpg
new file mode 100644
index 0000000..9b25c5f
Binary files /dev/null and b/dotclear._no/images/locataires.TN__.jpg differ
diff --git a/dotclear._no/images/locataires.jpg b/dotclear._no/images/locataires.jpg
new file mode 100644
index 0000000..b2deb8c
Binary files /dev/null and b/dotclear._no/images/locataires.jpg differ
diff --git a/dotclear._no/images/moustique.TN__.jpg b/dotclear._no/images/moustique.TN__.jpg
new file mode 100644
index 0000000..0e71f58
Binary files /dev/null and b/dotclear._no/images/moustique.TN__.jpg differ
diff --git a/dotclear._no/images/moustique.jpg b/dotclear._no/images/moustique.jpg
new file mode 100644
index 0000000..857c970
Binary files /dev/null and b/dotclear._no/images/moustique.jpg differ
diff --git a/dotclear._no/images/p1003800797.TN__.jpg b/dotclear._no/images/p1003800797.TN__.jpg
new file mode 100644
index 0000000..51a3060
Binary files /dev/null and b/dotclear._no/images/p1003800797.TN__.jpg differ
diff --git a/dotclear._no/images/p1003800797.jpg b/dotclear._no/images/p1003800797.jpg
new file mode 100644
index 0000000..42c3c97
Binary files /dev/null and b/dotclear._no/images/p1003800797.jpg differ
diff --git a/dotclear._no/images/pfstat-queues1.TN__.jpg b/dotclear._no/images/pfstat-queues1.TN__.jpg
new file mode 100644
index 0000000..e35ea19
Binary files /dev/null and b/dotclear._no/images/pfstat-queues1.TN__.jpg differ
diff --git a/dotclear._no/images/pfstat-queues1.jpg b/dotclear._no/images/pfstat-queues1.jpg
new file mode 100644
index 0000000..b0a22a0
Binary files /dev/null and b/dotclear._no/images/pfstat-queues1.jpg differ
diff --git a/dotclear._no/images/pfstat-queues2.TN__.jpg b/dotclear._no/images/pfstat-queues2.TN__.jpg
new file mode 100644
index 0000000..fbbfbbf
Binary files /dev/null and b/dotclear._no/images/pfstat-queues2.TN__.jpg differ
diff --git a/dotclear._no/images/pfstat-queues2.jpg b/dotclear._no/images/pfstat-queues2.jpg
new file mode 100644
index 0000000..3f30967
Binary files /dev/null and b/dotclear._no/images/pfstat-queues2.jpg differ
diff --git a/dotclear._no/images/pirate.TN__.jpg b/dotclear._no/images/pirate.TN__.jpg
new file mode 100644
index 0000000..a6c1f0d
Binary files /dev/null and b/dotclear._no/images/pirate.TN__.jpg differ
diff --git a/dotclear._no/images/pirate.jpg b/dotclear._no/images/pirate.jpg
new file mode 100644
index 0000000..f249151
Binary files /dev/null and b/dotclear._no/images/pirate.jpg differ
diff --git a/dotclear._no/images/sex_is_zero/5382.TN__.jpg b/dotclear._no/images/sex_is_zero/5382.TN__.jpg
new file mode 100644
index 0000000..507e8c8
Binary files /dev/null and b/dotclear._no/images/sex_is_zero/5382.TN__.jpg differ
diff --git a/dotclear._no/images/sex_is_zero/5382.jpg b/dotclear._no/images/sex_is_zero/5382.jpg
new file mode 100644
index 0000000..0a16179
Binary files /dev/null and b/dotclear._no/images/sex_is_zero/5382.jpg differ
diff --git a/dotclear._no/images/sex_is_zero/sexiszero3.TN__.jpg b/dotclear._no/images/sex_is_zero/sexiszero3.TN__.jpg
new file mode 100644
index 0000000..258c609
Binary files /dev/null and b/dotclear._no/images/sex_is_zero/sexiszero3.TN__.jpg differ
diff --git a/dotclear._no/images/sex_is_zero/sexiszero3.jpg b/dotclear._no/images/sex_is_zero/sexiszero3.jpg
new file mode 100644
index 0000000..45ea987
Binary files /dev/null and b/dotclear._no/images/sex_is_zero/sexiszero3.jpg differ
diff --git a/dotclear._no/images/sex_is_zero/sexiszero8.TN__.jpg b/dotclear._no/images/sex_is_zero/sexiszero8.TN__.jpg
new file mode 100644
index 0000000..6ae2e58
Binary files /dev/null and b/dotclear._no/images/sex_is_zero/sexiszero8.TN__.jpg differ
diff --git a/dotclear._no/images/sex_is_zero/sexiszero8.jpg b/dotclear._no/images/sex_is_zero/sexiszero8.jpg
new file mode 100644
index 0000000..3cef32b
Binary files /dev/null and b/dotclear._no/images/sex_is_zero/sexiszero8.jpg differ
diff --git a/dotclear._no/images/sincity.TN__.jpg b/dotclear._no/images/sincity.TN__.jpg
new file mode 100644
index 0000000..9b6d914
Binary files /dev/null and b/dotclear._no/images/sincity.TN__.jpg differ
diff --git a/dotclear._no/images/sincity.jpg b/dotclear._no/images/sincity.jpg
new file mode 100644
index 0000000..62759f5
Binary files /dev/null and b/dotclear._no/images/sincity.jpg differ
diff --git a/dotclear._no/images/usps/chrono1.TN__.jpg b/dotclear._no/images/usps/chrono1.TN__.jpg
new file mode 100644
index 0000000..614a7c8
Binary files /dev/null and b/dotclear._no/images/usps/chrono1.TN__.jpg differ
diff --git a/dotclear._no/images/usps/chrono1.jpg b/dotclear._no/images/usps/chrono1.jpg
new file mode 100644
index 0000000..4cea951
Binary files /dev/null and b/dotclear._no/images/usps/chrono1.jpg differ
diff --git a/dotclear._no/images/usps/chrono2.TN__.jpg b/dotclear._no/images/usps/chrono2.TN__.jpg
new file mode 100644
index 0000000..08d199b
Binary files /dev/null and b/dotclear._no/images/usps/chrono2.TN__.jpg differ
diff --git a/dotclear._no/images/usps/chrono2.jpg b/dotclear._no/images/usps/chrono2.jpg
new file mode 100644
index 0000000..54b9498
Binary files /dev/null and b/dotclear._no/images/usps/chrono2.jpg differ
diff --git a/dotclear._no/images/usps/usps.TN__.jpg b/dotclear._no/images/usps/usps.TN__.jpg
new file mode 100644
index 0000000..bde58f1
Binary files /dev/null and b/dotclear._no/images/usps/usps.TN__.jpg differ
diff --git a/dotclear._no/images/usps/usps.jpg b/dotclear._no/images/usps/usps.jpg
new file mode 100644
index 0000000..94fc023
Binary files /dev/null and b/dotclear._no/images/usps/usps.jpg differ
diff --git a/dotclear._no/inc/.htaccess b/dotclear._no/inc/.htaccess
new file mode 100644
index 0000000..569316c
--- /dev/null
+++ b/dotclear._no/inc/.htaccess
@@ -0,0 +1,2 @@
+Require all denied
+Deny from all
diff --git a/dotclear._no/inc/admin/actions/class.dcaction.php b/dotclear._no/inc/admin/actions/class.dcaction.php
new file mode 100644
index 0000000..cfa75ea
--- /dev/null
+++ b/dotclear._no/inc/admin/actions/class.dcaction.php
@@ -0,0 +1,392 @@
+core = $core;
+ $this->actions = new ArrayObject();
+ $this->combo = [];
+ $this->uri = $uri;
+ $this->redir_args = $redirect_args;
+ $this->redirect_fields = [];
+ $this->action = '';
+ $this->cb_title = __('Title');
+ $this->entries = [];
+ $this->from = new ArrayObject($_POST);
+ $this->field_entries = 'entries';
+ $this->caller_title = __('Entries');
+ if (isset($this->redir_args['_ANCHOR'])) {
+ $this->redir_anchor = '#' . $this->redir_args['_ANCHOR'];
+ unset($this->redir_args['_ANCHOR']);
+ } else {
+ $this->redir_anchor = '';
+ }
+ $u = explode('?', $_SERVER['REQUEST_URI']);
+ $this->in_plugin = (strpos($u[0], 'plugin.php') !== false);
+ $this->enable_redir_selection = true;
+ }
+
+ /**
+ * setEnableRedirSelection - define whether to keep selection when redirecting
+ * Can be usefull to be disabled to preserve some compatibility.
+ *
+ * @param boolean $enable true to enable, false otherwise
+ *
+ * @access public
+ */
+ public function setEnableRedirSelection($enable)
+ {
+ $this->enable_redir_selection = $enable;
+ }
+
+ /**
+ * addAction - adds an action
+ *
+ * @param string $actions the actions names as if it was a standalone combo array.
+ * It will be merged with other actions.
+ * Can be bound to multiple values, if the same callback is to be called
+ * @param callback $callback the callback for the action.
+ *
+ * @access public
+ *
+ * @return dcActionsPage the actions page itself, enabling to chain addAction().
+ */
+ public function addAction($actions, $callback)
+ {
+ foreach ($actions as $k => $a) {
+ // Check each case of combo definition
+ // Store form values in $values
+ if (is_array($a)) {
+ $values = array_values($a);
+ if (!isset($this->combo[$k])) {
+ $this->combo[$k] = [];
+ }
+ $this->combo[$k] = array_merge($this->combo[$k], $a);
+ } elseif ($a instanceof formSelectOption) {
+ $values = [$a->value];
+ $this->combo[$k] = $a->value;
+ } else {
+ $values = [$a];
+ $this->combo[$k] = $a;
+ }
+ // Associate each potential value to the callback
+ foreach ($values as $v) {
+ $this->actions[$v] = $callback;
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * getCombo - returns the actions combo, useable through form::combo
+ *
+ * @access public
+ *
+ * @return array the actions combo
+ */
+ public function getCombo()
+ {
+ return $this->combo;
+ }
+
+ /**
+ * getIDS() - returns the list of selected entries
+ *
+ * @access public
+ *
+ * @return array the list
+ */
+ public function getIDs()
+ {
+ return array_keys($this->entries);
+ }
+
+ /**
+ * getIDS() - returns the list of selected entries as HTML hidden fields string
+ *
+ * @access public
+ *
+ * @return string the HTML code for hidden fields
+ */
+ public function getIDsHidden()
+ {
+ $ret = '';
+ foreach ($this->entries as $id => $v) {
+ $ret .= form::hidden($this->field_entries . '[]', $id);
+ }
+ return $ret;
+ }
+
+ /**
+ * getHiddenFields() - returns all redirection parameters as HTML hidden fields
+ *
+ * @param boolean $with_ids if true, also include ids in HTML code
+ *
+ * @access public
+ *
+ * @return string the HTML code for hidden fields
+ */
+ public function getHiddenFields($with_ids = false)
+ {
+ $ret = '';
+ foreach ($this->redir_args as $k => $v) {
+ $ret .= form::hidden([$k], $v);
+ }
+ if ($with_ids) {
+ $ret .= $this->getIDsHidden();
+ }
+ return $ret;
+ }
+
+ /**
+ * getRS() - get record from DB Query containing requested IDs
+ *
+ * @param boolean $with_ids if true, also include ids in HTML code
+ *
+ * @access public
+ *
+ * @return string the HTML code for hidden fields
+ */
+ public function getRS()
+ {
+ return $this->rs;
+ }
+
+ /**
+ * setupRedir - setup redirection arguments
+ * by default, $_POST fields as defined in redirect_fields attributes
+ * are set into redirect_args.
+ *
+ * @param array $from input to parse fields from (usually $_POST)
+ *
+ * @access protected
+ */
+ protected function setupRedir($from)
+ {
+ foreach ($this->redirect_fields as $p) {
+ if (isset($from[$p])) {
+ $this->redir_args[$p] = $from[$p];
+ }
+ }
+ }
+
+ /**
+ * getRedirection - returns redirection URL
+ *
+ * @param array $params extra parameters to append to redirection
+ * must be an array : each key is the name,
+ * each value is the wanted value
+ * @param boolean $with_selected_entries if true, add selected entries in url
+ *
+ * @access public
+ *
+ * @return string the redirection url
+ */
+ public function getRedirection($with_selected_entries = false, $params = [])
+ {
+ $redir_args = array_merge($params, $this->redir_args);
+ if (isset($redir_args['redir'])) {
+ unset($redir_args['redir']);
+ }
+
+ if ($with_selected_entries && $this->enable_redir_selection) {
+ $redir_args[$this->field_entries] = array_keys($this->entries);
+ }
+ return $this->uri . '?' . http_build_query($redir_args) . $this->redir_anchor;
+ }
+
+ /**
+ * redirect - redirects to redirection page
+ *
+ * @see getRedirection for arguments details
+ *
+ * @access public
+ */
+ public function redirect($with_selected_entries = false, $params = [])
+ {
+ http::redirect($this->getRedirection($with_selected_entries, $params));
+ exit;
+ }
+
+ /**
+ * getURI - returns current form URI, if any
+ *
+ * @access public
+ *
+ * @return string the form URI
+ */
+ public function getURI()
+ {
+ return $this->uri;
+ }
+
+ /**
+ * getCallerTitle - returns current form URI, if any
+ *
+ * @access public
+ *
+ * @return string the form URI
+ */
+ public function getCallerTitle()
+ {
+ return $this->caller_title;
+ }
+
+ /**
+ * getAction - returns current action, if any
+ *
+ * @access public
+ *
+ * @return string the action
+ */
+ public function getAction()
+ {
+ return $this->action;
+ }
+
+ /**
+ * process - proceeds action handling, if any
+ * this method may issue an exit() if
+ * an action is being processed. If it
+ * returns, no action has been performed
+ *
+ * @access public
+ */
+ public function process()
+ {
+
+ $this->setupRedir($this->from);
+ $this->fetchEntries($this->from);
+ if (isset($this->from['action'])) {
+ $this->action = $this->from['action'];
+ try {
+ $performed = false;
+ foreach ($this->actions as $k => $v) {
+ if ($this->from['action'] == $k) {
+ $performed = true;
+ call_user_func($v, $this->core, $this, $this->from);
+ }
+ }
+ if ($performed) {
+ return true;
+ }
+ } catch (Exception $e) {
+ $this->error($e);
+ return true;
+ }
+ }
+ }
+
+ /**
+ * getcheckboxes -returns html code for selected entries
+ * as a table containing entries checkboxes
+ *
+ * @access public
+ *
+ * @return string the html code for checkboxes
+ */
+ public function getCheckboxes()
+ {
+ $ret =
+ '' .
+ '' . $this->cb_title . ' ' .
+ ' ';
+ foreach ($this->entries as $id => $title) {
+ $ret .=
+ '' .
+ form::checkbox([$this->field_entries . '[]'], $id, [
+ 'checked' => true
+ ]) .
+ ' ' .
+ '' . $title . ' ';
+ }
+ $ret .= '
';
+ return $ret;
+ }
+
+ /**
+ * beginPage, endPage - displays the beginning/ending of a page, if action does not redirects dirtectly
+ *
+ * These methods are called from the actions themselves.
+ *
+ * @param string $breadcrumb breadcrumb to display
+ * @param string $head page header to include
+ *
+ * @access public
+ */
+ abstract public function beginPage($breadcrumb = '', $head = '');
+ abstract public function endPage();
+
+ /**
+ * fetchEntries - fills-in information by requesting into db
+ * this method may setup the following attributes
+ * * entries : list of entries (checked against permissions)
+ * entries ids are array keys, values contain entry description (if relevant)
+ * * rs : record given by db request
+ * @access protected
+ */
+ abstract protected function fetchEntries($from);
+
+}
diff --git a/dotclear._no/inc/admin/actions/class.dcactionblogs.php b/dotclear._no/inc/admin/actions/class.dcactionblogs.php
new file mode 100644
index 0000000..7924357
--- /dev/null
+++ b/dotclear._no/inc/admin/actions/class.dcactionblogs.php
@@ -0,0 +1,205 @@
+redirect_fields = ['status', 'sortby', 'order', 'page', 'nb'];
+ $this->field_entries = 'blogs';
+ $this->title_cb = __('Blogs');
+ $this->loadDefaults();
+ $core->callBehavior('adminBlogsActionsPage', $core, $this);
+ }
+
+ protected function loadDefaults()
+ {
+ // We could have added a behavior here, but we want default action
+ // to be setup first
+ dcDefaultBlogActions::adminBlogsActionsPage($this->core, $this);
+ }
+
+ public function beginPage($breadcrumb = '', $head = '')
+ {
+ if ($this->in_plugin) {
+ echo '' . __('Blogs') . ' ' .
+ dcPage::jsLoad('js/_blogs_actions.js') .
+ $head .
+ '' .
+ $breadcrumb;
+ } else {
+ dcPage::open(
+ __('Blogs'),
+ dcPage::jsLoad('js/_blogs_actions.js') .
+ $head,
+ $breadcrumb
+ );
+ }
+ echo '' . __('Back to blogs list') . '
';
+ }
+
+ public function endPage()
+ {
+ dcPage::close();
+ }
+
+ public function error(Exception $e)
+ {
+ $this->core->error->add($e->getMessage());
+ $this->beginPage(dcPage::breadcrumb(
+ [
+ html::escapeHTML($this->core->blog->name) => '',
+ __('Blogs') => $this->core->adminurl->get('admin.blogs'),
+ __('Blogs actions') => ''
+ ])
+ );
+ $this->endPage();
+ }
+
+ public function getCheckboxes()
+ {
+ $ret = '';
+ foreach ($this->entries as $id => $res) {
+ $ret .=
+ '' .
+ '' . form::checkbox([$this->field_entries . '[]'], $id,
+ [
+ 'checked' => true
+ ]) .
+ ' ' .
+ '' . $res['blog'] . ' ' .
+ '' . $res['name'] . ' ' .
+ ' ';
+ }
+
+ return
+ '' .
+ '' . __('Blog id') . ' ' . __('Blog name') . ' ' .
+ ' ' . $ret . '
';
+ }
+
+ protected function fetchEntries($from)
+ {
+ $params = [];
+ if (!empty($from['blogs'])) {
+ $params['blog_id'] = $from['blogs'];
+ }
+
+ $bl = $this->core->getBlogs($params);
+ while ($bl->fetch()) {
+ $this->entries[$bl->blog_id] = [
+ 'blog' => $bl->blog_id,
+ 'name' => $bl->blog_name
+ ];
+ }
+ $this->rs = $bl;
+ }
+}
+
+class dcDefaultBlogActions
+{
+ public static function adminBlogsActionsPage($core, dcBlogsActionsPage $ap)
+ {
+ if (!$core->auth->isSuperAdmin()) {
+ return;
+ }
+
+ $ap->addAction(
+ [__('Status') => [
+ __('Set online') => 'online',
+ __('Set offline') => 'offline',
+ __('Set as removed') => 'remove'
+ ]],
+ ['dcDefaultBlogActions', 'doChangeBlogStatus']
+ );
+ $ap->addAction(
+ [__('Delete') => [
+ __('Delete') => 'delete']],
+ ['dcDefaultBlogActions', 'doDeleteBlog']
+ );
+ }
+
+ public static function doChangeBlogStatus($core, dcBlogsActionsPage $ap, $post)
+ {
+ if (!$core->auth->isSuperAdmin()) {
+ return;
+ }
+
+ $action = $ap->getAction();
+ $ids = $ap->getIDs();
+ if (empty($ids)) {
+ throw new Exception(__('No blog selected'));
+ }
+ switch ($action) {
+ case 'online':$status = 1;
+ break;
+ case 'offline':$status = 0;
+ break;
+ case 'remove':$status = -1;
+ break;
+ default:$status = 1;
+ break;
+ }
+
+ $cur = $core->con->openCursor($core->prefix . 'blog');
+ $cur->blog_status = $status;
+ //$cur->blog_upddt = date('Y-m-d H:i:s');
+ $cur->update('WHERE blog_id ' . $core->con->in($ids));
+
+ dcPage::addSuccessNotice(__('Selected blogs have been successfully updated.'));
+ $ap->redirect(true);
+ }
+
+ public static function doDeleteBlog($core, dcBlogsActionsPage $ap, $post)
+ {
+ if (!$core->auth->isSuperAdmin()) {
+ return;
+ }
+
+ $ap_ids = $ap->getIDs();
+ if (empty($ap_ids)) {
+ throw new Exception(__('No blog selected'));
+ }
+
+ if (!$core->auth->checkPassword($_POST['pwd'])) {
+ throw new Exception(__('Password verification failed'));
+ }
+
+ $ids = [];
+ foreach ($ap_ids as $id) {
+ if ($id == $core->blog->id) {
+ dcPage::addWarningNotice(__('The current blog cannot be deleted.'));
+ } else {
+ $ids[] = $id;
+ }
+ }
+
+ if (!empty($ids)) {
+ # --BEHAVIOR-- adminBeforeBlogsDelete
+ $core->callBehavior('adminBeforeBlogsDelete', $ids);
+
+ foreach ($ids as $id) {
+ $core->delBlog($id);
+ }
+
+ dcPage::addSuccessNotice(sprintf(
+ __(
+ '%d blog has been successfully deleted',
+ '%d blogs have been successfully deleted',
+ count($ids)
+ ),
+ count($ids))
+ );
+ }
+ $ap->redirect(false);
+ }
+}
diff --git a/dotclear._no/inc/admin/actions/class.dcactioncomments.php b/dotclear._no/inc/admin/actions/class.dcactioncomments.php
new file mode 100644
index 0000000..6a74070
--- /dev/null
+++ b/dotclear._no/inc/admin/actions/class.dcactioncomments.php
@@ -0,0 +1,235 @@
+redirect_fields = ['type', 'author', 'status',
+ 'sortby', 'ip', 'order', 'page', 'nb', 'section'];
+ $this->field_entries = 'comments';
+ $this->title_cb = __('Comments');
+ $this->loadDefaults();
+ $core->callBehavior('adminCommentsActionsPage', $core, $this);
+ }
+
+ protected function loadDefaults()
+ {
+ // We could have added a behavior here, but we want default action
+ // to be setup first
+ dcDefaultCommentActions::adminCommentsActionsPage($this->core, $this);
+ }
+
+ public function beginPage($breadcrumb = '', $head = '')
+ {
+ if ($this->in_plugin) {
+ echo '' . __('Comments') . ' ' .
+ dcPage::jsLoad('js/_comments_actions.js') .
+ $head .
+ '' .
+ $breadcrumb;
+ } else {
+ dcPage::open(
+ __('Comments'),
+ dcPage::jsLoad('js/_comments_actions.js') .
+ $head,
+ $breadcrumb
+ );
+
+ }
+ echo '' . __('Back to comments list') . '
';
+ }
+
+ public function endPage()
+ {
+ dcPage::close();
+ }
+
+ public function error(Exception $e)
+ {
+ $this->core->error->add($e->getMessage());
+ $this->beginPage(dcPage::breadcrumb(
+ [
+ html::escapeHTML($this->core->blog->name) => '',
+ __('Comments') => $this->core->adminurl->get('admin.comments'),
+ __('Comments actions') => ''
+ ])
+ );
+ $this->endPage();
+ }
+
+ /**
+ * getcheckboxes -returns html code for selected entries
+ * as a table containing entries checkboxes
+ *
+ * @access public
+ *
+ * @return string the html code for checkboxes
+ */
+ public function getCheckboxes()
+ {
+ $ret =
+ '' .
+ '' . __('Author') . ' ' . __('Title') . ' ' .
+ ' ';
+ foreach ($this->entries as $id => $title) {
+ $ret .=
+ '' .
+ form::checkbox([$this->field_entries . '[]'], $id,
+ [
+ 'checked' => true
+ ]) .
+ ' ' .
+ '' . $title['author'] . ' ' . $title['title'] . ' ';
+ }
+ $ret .= '
';
+ return $ret;
+ }
+
+ protected function fetchEntries($from)
+ {
+ $params = [];
+ if (!empty($from['comments'])) {
+ $comments = $from['comments'];
+
+ foreach ($comments as $k => $v) {
+ $comments[$k] = (integer) $v;
+ }
+
+ $params['sql'] = 'AND C.comment_id IN(' . implode(',', $comments) . ') ';
+ } else {
+ $params['sql'] = 'AND 1=0 ';
+ }
+
+ if (!isset($from['full_content']) || empty($from['full_content'])) {
+ $params['no_content'] = true;
+ }
+ $co = $this->core->blog->getComments($params);
+ while ($co->fetch()) {
+ $this->entries[$co->comment_id] = [
+ 'title' => $co->post_title,
+ 'author' => $co->comment_author
+ ];
+ }
+ $this->rs = $co;
+ }
+}
+
+class dcDefaultCommentActions
+{
+ public static function adminCommentsActionsPage($core, dcCommentsActionsPage $ap)
+ {
+ if ($core->auth->check('publish,contentadmin', $core->blog->id)) {
+ $ap->addAction(
+ [__('Status') => [
+ __('Publish') => 'publish',
+ __('Unpublish') => 'unpublish',
+ __('Mark as pending') => 'pending',
+ __('Mark as junk') => 'junk'
+ ]],
+ ['dcDefaultCommentActions', 'doChangeCommentStatus']
+ );
+ }
+
+ if ($core->auth->check('delete,contentadmin', $core->blog->id)) {
+ $ap->addAction(
+ [__('Delete') => [
+ __('Delete') => 'delete']],
+ ['dcDefaultCommentActions', 'doDeleteComment']
+ );
+ }
+
+ $ip_filter_active = true;
+ if ($core->blog->settings->antispam->antispam_filters !== null) {
+ $filters_opt = $core->blog->settings->antispam->antispam_filters;
+ if (is_array($filters_opt)) {
+ $ip_filter_active = isset($filters_opt['dcFilterIP']) && is_array($filters_opt['dcFilterIP']) && $filters_opt['dcFilterIP'][0] == 1;
+ }
+ }
+
+ if ($ip_filter_active) {
+ $blacklist_actions = [__('Blacklist IP') => 'blacklist'];
+ if ($core->auth->isSuperAdmin()) {
+ $blacklist_actions[__('Blacklist IP (global)')] = 'blacklist_global';
+ }
+
+ $ap->addAction(
+ [__('IP address') => $blacklist_actions],
+ ['dcDefaultCommentActions', 'doBlacklistIP']
+ );
+ }
+ }
+
+ public static function doChangeCommentStatus($core, dcCommentsActionsPage $ap, $post)
+ {
+ $action = $ap->getAction();
+ $co_ids = $ap->getIDs();
+ if (empty($co_ids)) {
+ throw new Exception(__('No comment selected'));
+ }
+ switch ($action) {
+ case 'unpublish':$status = 0;
+ break;
+ case 'pending':$status = -1;
+ break;
+ case 'junk':$status = -2;
+ break;
+ default:$status = 1;
+ break;
+ }
+
+ $core->blog->updCommentsStatus($co_ids, $status);
+
+ dcPage::addSuccessNotice(__('Selected comments have been successfully updated.'));
+ $ap->redirect(true);
+ }
+
+ public static function doDeleteComment($core, dcCommentsActionsPage $ap, $post)
+ {
+ $co_ids = $ap->getIDs();
+ if (empty($co_ids)) {
+ throw new Exception(__('No comment selected'));
+ }
+ // Backward compatibility
+ foreach ($co_ids as $comment_id) {
+ # --BEHAVIOR-- adminBeforeCommentDelete
+ $core->callBehavior('adminBeforeCommentDelete', $comment_id);
+ }
+
+ # --BEHAVIOR-- adminBeforeCommentsDelete
+ $core->callBehavior('adminBeforeCommentsDelete', $co_ids);
+
+ $core->blog->delComments($co_ids);
+ dcPage::addSuccessNotice(__('Selected comments have been successfully deleted.'));
+ $ap->redirect(false);
+ }
+
+ public static function doBlacklistIP($core, dcCommentsActionsPage $ap, $post)
+ {
+ $action = $ap->getAction();
+ $co_ids = $ap->getIDs();
+ if (empty($co_ids)) {
+ throw new Exception(__('No comment selected'));
+ }
+
+ $global = !empty($action) && $action == 'blacklist_global' && $core->auth->isSuperAdmin();
+
+ $ip_filter = new dcFilterIP($core);
+ $rs = $ap->getRS();
+ while ($rs->fetch()) {
+ $ip_filter->addIP('black', $rs->comment_ip, $global);
+ }
+
+ dcPage::addSuccessNotice(__('IP addresses for selected comments have been blacklisted.'));
+ $ap->redirect(true);
+ }
+}
diff --git a/dotclear._no/inc/admin/actions/class.dcactionposts.php b/dotclear._no/inc/admin/actions/class.dcactionposts.php
new file mode 100644
index 0000000..33a02f5
--- /dev/null
+++ b/dotclear._no/inc/admin/actions/class.dcactionposts.php
@@ -0,0 +1,448 @@
+redirect_fields = ['user_id', 'cat_id', 'status',
+ 'selected', 'attachment', 'month', 'lang', 'sortby', 'order', 'page', 'nb'];
+ $this->loadDefaults();
+ }
+
+ protected function loadDefaults()
+ {
+ // We could have added a behavior here, but we want default action
+ // to be setup first
+ dcDefaultPostActions::adminPostsActionsPage($this->core, $this);
+ $this->core->callBehavior('adminPostsActionsPage', $this->core, $this);
+
+ }
+
+ public function beginPage($breadcrumb = '', $head = '')
+ {
+ if ($this->in_plugin) {
+ echo '' . __('Posts') . ' ' .
+ dcPage::jsLoad('js/_posts_actions.js') .
+ $head .
+ '' .
+ $breadcrumb;
+ } else {
+ dcPage::open(
+ __('Posts'),
+ dcPage::jsLoad('js/_posts_actions.js') .
+ $head,
+ $breadcrumb
+ );
+ }
+ echo '' . __('Back to entries list') . '
';
+ }
+
+ public function endPage()
+ {
+ if ($this->in_plugin) {
+ echo '';
+ } else {
+ dcPage::close();
+ }
+ }
+
+ public function error(Exception $e)
+ {
+ $this->core->error->add($e->getMessage());
+ $this->beginPage(dcPage::breadcrumb(
+ [
+ html::escapeHTML($this->core->blog->name) => '',
+ $this->getCallerTitle() => $this->getRedirection(true),
+ __('Posts actions') => ''
+ ])
+ );
+ $this->endPage();
+ }
+
+ protected function fetchEntries($from)
+ {
+ $params = [];
+ if (!empty($from['entries'])) {
+ $entries = $from['entries'];
+
+ foreach ($entries as $k => $v) {
+ $entries[$k] = (integer) $v;
+ }
+
+ $params['sql'] = 'AND P.post_id IN(' . implode(',', $entries) . ') ';
+ } else {
+ $params['sql'] = 'AND 1=0 ';
+ }
+
+ if (!isset($from['full_content']) || empty($from['full_content'])) {
+ $params['no_content'] = true;
+ }
+
+ if (isset($from['post_type'])) {
+ $params['post_type'] = $from['post_type'];
+ }
+
+ $posts = $this->core->blog->getPosts($params);
+ while ($posts->fetch()) {
+ $this->entries[$posts->post_id] = $posts->post_title;
+ }
+ $this->rs = $posts;
+ }
+}
+
+class dcDefaultPostActions
+{
+ public static function adminPostsActionsPage($core, $ap)
+ {
+ if ($core->auth->check('publish,contentadmin', $core->blog->id)) {
+ $ap->addAction(
+ [__('Status') => [
+ __('Publish') => 'publish',
+ __('Unpublish') => 'unpublish',
+ __('Schedule') => 'schedule',
+ __('Mark as pending') => 'pending'
+ ]],
+ ['dcDefaultPostActions', 'doChangePostStatus']
+ );
+ }
+ $ap->addAction(
+ [__('Mark') => [
+ __('Mark as selected') => 'selected',
+ __('Mark as unselected') => 'unselected'
+ ]],
+ ['dcDefaultPostActions', 'doUpdateSelectedPost']
+ );
+ $ap->addAction(
+ [__('Change') => [
+ __('Change category') => 'category'
+ ]],
+ ['dcDefaultPostActions', 'doChangePostCategory']
+ );
+ $ap->addAction(
+ [__('Change') => [
+ __('Change language') => 'lang'
+ ]],
+ ['dcDefaultPostActions', 'doChangePostLang']
+ );
+ if ($core->auth->check('admin', $core->blog->id)) {
+ $ap->addAction(
+ [__('Change') => [
+ __('Change author') => 'author']],
+ ['dcDefaultPostActions', 'doChangePostAuthor']
+ );
+ }
+ if ($core->auth->check('delete,contentadmin', $core->blog->id)) {
+ $ap->addAction(
+ [__('Delete') => [
+ __('Delete') => 'delete']],
+ ['dcDefaultPostActions', 'doDeletePost']
+ );
+ }
+ }
+
+ public static function doChangePostStatus($core, dcPostsActionsPage $ap, $post)
+ {
+ switch ($ap->getAction()) {
+ case 'unpublish':$status = 0;
+ break;
+ case 'schedule':$status = -1;
+ break;
+ case 'pending':$status = -2;
+ break;
+ default:$status = 1;
+ break;
+ }
+ $posts_ids = $ap->getIDs();
+ if (empty($posts_ids)) {
+ throw new Exception(__('No entry selected'));
+ }
+ $core->blog->updPostsStatus($posts_ids, $status);
+ dcPage::addSuccessNotice(sprintf(
+ __(
+ '%d entry has been successfully updated to status : "%s"',
+ '%d entries have been successfully updated to status : "%s"',
+ count($posts_ids)
+ ),
+ count($posts_ids),
+ $core->blog->getPostStatus($status))
+ );
+ $ap->redirect(true);
+ }
+
+ public static function doUpdateSelectedPost($core, dcPostsActionsPage $ap, $post)
+ {
+ $posts_ids = $ap->getIDs();
+ if (empty($posts_ids)) {
+ throw new Exception(__('No entry selected'));
+ }
+ $action = $ap->getAction();
+ $core->blog->updPostsSelected($posts_ids, $action == 'selected');
+ if ($action == 'selected') {
+ dcPage::addSuccessNotice(sprintf(
+ __(
+ '%d entry has been successfully marked as selected',
+ '%d entries have been successfully marked as selected',
+ count($posts_ids)
+ ),
+ count($posts_ids))
+ );
+ } else {
+ dcPage::addSuccessNotice(sprintf(
+ __(
+ '%d entry has been successfully marked as unselected',
+ '%d entries have been successfully marked as unselected',
+ count($posts_ids)
+ ),
+ count($posts_ids))
+ );
+ }
+ $ap->redirect(true);
+ }
+
+ public static function doDeletePost($core, dcPostsActionsPage $ap, $post)
+ {
+
+ $posts_ids = $ap->getIDs();
+ if (empty($posts_ids)) {
+ throw new Exception(__('No entry selected'));
+ }
+ // Backward compatibility
+ foreach ($posts_ids as $post_id) {
+ # --BEHAVIOR-- adminBeforePostDelete
+ $core->callBehavior('adminBeforePostDelete', (integer) $post_id);
+ }
+
+ # --BEHAVIOR-- adminBeforePostsDelete
+ $core->callBehavior('adminBeforePostsDelete', $posts_ids);
+
+ $core->blog->delPosts($posts_ids);
+ dcPage::addSuccessNotice(sprintf(
+ __(
+ '%d entry has been successfully deleted',
+ '%d entries have been successfully deleted',
+ count($posts_ids)
+ ),
+ count($posts_ids))
+ );
+
+ $ap->redirect(false);
+ }
+
+ public static function doChangePostCategory($core, dcPostsActionsPage $ap, $post)
+ {
+ if (isset($post['new_cat_id'])) {
+ $posts_ids = $ap->getIDs();
+ if (empty($posts_ids)) {
+ throw new Exception(__('No entry selected'));
+ }
+ $new_cat_id = $post['new_cat_id'];
+ if (!empty($post['new_cat_title']) && $core->auth->check('categories', $core->blog->id)) {
+ $cur_cat = $core->con->openCursor($core->prefix . 'category');
+ $cur_cat->cat_title = $post['new_cat_title'];
+ $cur_cat->cat_url = '';
+ $title = $cur_cat->cat_title;
+
+ $parent_cat = !empty($post['new_cat_parent']) ? $post['new_cat_parent'] : '';
+
+ # --BEHAVIOR-- adminBeforeCategoryCreate
+ $core->callBehavior('adminBeforeCategoryCreate', $cur_cat);
+
+ $new_cat_id = $core->blog->addCategory($cur_cat, (integer) $parent_cat);
+
+ # --BEHAVIOR-- adminAfterCategoryCreate
+ $core->callBehavior('adminAfterCategoryCreate', $cur_cat, $new_cat_id);
+ }
+
+ $core->blog->updPostsCategory($posts_ids, $new_cat_id);
+ $title = $core->blog->getCategory($new_cat_id);
+ dcPage::addSuccessNotice(sprintf(
+ __(
+ '%d entry has been successfully moved to category "%s"',
+ '%d entries have been successfully moved to category "%s"',
+ count($posts_ids)
+ ),
+ count($posts_ids),
+ html::escapeHTML($title->cat_title))
+ );
+
+ $ap->redirect(true);
+ } else {
+
+ $ap->beginPage(
+ dcPage::breadcrumb(
+ [
+ html::escapeHTML($core->blog->name) => '',
+ $ap->getCallerTitle() => $ap->getRedirection(true),
+ __('Change category for this selection') => ''
+ ]));
+ # categories list
+ # Getting categories
+ $categories_combo = dcAdminCombos::getCategoriesCombo(
+ $core->blog->getCategories()
+ );
+ echo
+ '';
+ $ap->endPage();
+
+ }
+
+ }
+ public static function doChangePostAuthor($core, dcPostsActionsPage $ap, $post)
+ {
+ if (isset($post['new_auth_id']) && $core->auth->check('admin', $core->blog->id)) {
+ $new_user_id = $post['new_auth_id'];
+ $posts_ids = $ap->getIDs();
+ if (empty($posts_ids)) {
+ throw new Exception(__('No entry selected'));
+ }
+ if ($core->getUser($new_user_id)->isEmpty()) {
+ throw new Exception(__('This user does not exist'));
+ }
+
+ $cur = $core->con->openCursor($core->prefix . 'post');
+ $cur->user_id = $new_user_id;
+ $cur->update('WHERE post_id ' . $core->con->in($posts_ids));
+ dcPage::addSuccessNotice(sprintf(
+ __(
+ '%d entry has been successfully set to user "%s"',
+ '%d entries have been successfully set to user "%s"',
+ count($posts_ids)
+ ),
+ count($posts_ids),
+ html::escapeHTML($new_user_id))
+ );
+
+ $ap->redirect(true);
+ } else {
+ $usersList = [];
+ if ($core->auth->check('admin', $core->blog->id)) {
+ $params = [
+ 'limit' => 100,
+ 'order' => 'nb_post DESC'
+ ];
+ $rs = $core->getUsers($params);
+ $rsStatic = $rs->toStatic();
+ $rsStatic->extend('rsExtUser');
+ $rsStatic = $rsStatic->toExtStatic();
+ $rsStatic->lexicalSort('user_id');
+ while ($rsStatic->fetch()) {
+ $usersList[] = $rsStatic->user_id;
+ }
+ }
+ $ap->beginPage(
+ dcPage::breadcrumb(
+ [
+ html::escapeHTML($core->blog->name) => '',
+ $ap->getCallerTitle() => $ap->getRedirection(true),
+ __('Change author for this selection') => '']),
+ dcPage::jsLoad('js/jquery/jquery.autocomplete.js') .
+ dcPage::jsJson('users_list', $usersList)
+ );
+
+ echo
+ '';
+ $ap->endPage();
+ }
+ }
+ public static function doChangePostLang($core, dcPostsActionsPage $ap, $post)
+ {
+ $posts_ids = $ap->getIDs();
+ if (empty($posts_ids)) {
+ throw new Exception(__('No entry selected'));
+ }
+ if (isset($post['new_lang'])) {
+ $new_lang = $post['new_lang'];
+ $cur = $core->con->openCursor($core->prefix . 'post');
+ $cur->post_lang = $new_lang;
+ $cur->update('WHERE post_id ' . $core->con->in($posts_ids));
+ dcPage::addSuccessNotice(sprintf(
+ __(
+ '%d entry has been successfully set to language "%s"',
+ '%d entries have been successfully set to language "%s"',
+ count($posts_ids)
+ ),
+ count($posts_ids),
+ html::escapeHTML(l10n::getLanguageName($new_lang)))
+ );
+ $ap->redirect(true);
+ } else {
+ $ap->beginPage(
+ dcPage::breadcrumb(
+ [
+ html::escapeHTML($core->blog->name) => '',
+ $ap->getCallerTitle() => $ap->getRedirection(true),
+ __('Change language for this selection') => ''
+ ]));
+ # lang list
+ # Languages combo
+ $rs = $core->blog->getLangs(['order' => 'asc']);
+ $all_langs = l10n::getISOcodes(0, 1);
+ $lang_combo = ['' => '', __('Most used') => [], __('Available') => l10n::getISOcodes(1, 1)];
+ while ($rs->fetch()) {
+ if (isset($all_langs[$rs->post_lang])) {
+ $lang_combo[__('Most used')][$all_langs[$rs->post_lang]] = $rs->post_lang;
+ unset($lang_combo[__('Available')][$all_langs[$rs->post_lang]]);
+ } else {
+ $lang_combo[__('Most used')][$rs->post_lang] = $rs->post_lang;
+ }
+ }
+ unset($all_langs);
+ unset($rs);
+
+ echo
+ '';
+ $ap->endPage();
+ }
+ }
+}
diff --git a/dotclear._no/inc/admin/class.dc.blog_pref.php b/dotclear._no/inc/admin/class.dc.blog_pref.php
new file mode 100644
index 0000000..9d4928c
--- /dev/null
+++ b/dotclear._no/inc/admin/class.dc.blog_pref.php
@@ -0,0 +1,26 @@
+ $GLOBALS['core']->blog->url
+ ]) .
+ dcPage::jsLoad('js/_blog_pref_popup_posts.js');
+
+ return $res;
+ }
+}
diff --git a/dotclear._no/inc/admin/class.dc.favorites.php b/dotclear._no/inc/admin/class.dc.favorites.php
new file mode 100644
index 0000000..8431f0e
--- /dev/null
+++ b/dotclear._no/inc/admin/class.dc.favorites.php
@@ -0,0 +1,510 @@
+core = $core;
+ $this->fav_defs = new ArrayObject();
+ $this->ws = $core->auth->user_prefs->addWorkspace('dashboard');
+ $this->user_prefs = [];
+
+ if ($this->ws->prefExists('favorites')) {
+ $this->local_prefs = $this->ws->getLocal('favorites');
+ $this->global_prefs = $this->ws->getGlobal('favorites');
+ // Since we never know what user puts through user:preferences ...
+ if (!is_array($this->local_prefs)) {
+ $this->local_prefs = [];
+ }
+ if (!is_array($this->global_prefs)) {
+ $this->global_prefs = [];
+ }
+ } else {
+ // No favorite defined ? Huhu, let's go for a migration
+ $this->migrateFavorites();
+ }
+ }
+
+ /**
+ * setup - sets up favorites, fetch user favorites (against his permissions)
+ * This method is to be called after loading plugins
+ *
+ * @access public
+ *
+ */
+ public function setup()
+ {
+ defaultFavorites::initDefaultFavorites($this);
+ $this->legacyFavorites();
+ $this->core->callBehavior('adminDashboardFavorites', $this->core, $this);
+ $this->setUserPrefs();
+ }
+
+ /**
+ * getFavorite - retrieves a favorite (complete description) from its id.
+ *
+ * @param string $id the favorite id, or an array having 1 key 'name' set to id, ther keys are merged to favorite.
+ *
+ * @access public
+ *
+ * @return array the favorite, false if not found (or not permitted)
+ */
+ public function getFavorite($p)
+ {
+ if (is_array($p)) {
+ $fname = $p['name'];
+ if (!isset($this->fav_defs[$fname])) {
+ return false;
+ }
+ $fattr = $p;
+ unset($fattr['name']);
+ $fattr = array_merge($this->fav_defs[$fname], $fattr);
+ } else {
+ if (!isset($this->fav_defs[$p])) {
+ return false;
+ }
+ $fattr = $this->fav_defs[$p];
+ }
+ $fattr = array_merge(['id' => null, 'class' => null], $fattr);
+ if (isset($fattr['permissions'])) {
+ if (is_bool($fattr['permissions']) && !$fattr['permissions']) {
+ return false;
+ }
+ if (!$this->core->auth->check($fattr['permissions'], $this->core->blog->id)) {
+ return false;
+ }
+ } elseif (!$this->core->auth->isSuperAdmin()) {
+ return false;
+ }
+ return $fattr;
+ }
+
+ /**
+ * getFavorites - retrieves a list of favorites.
+ *
+ * @param string $ids an array of ids, as defined in getFavorite.
+ *
+ * @access public
+ *
+ * @return array array of favorites, can be empty if ids are not found (or not permitted)
+ */
+ public function getFavorites($ids)
+ {
+ $prefs = [];
+ foreach ($ids as $id) {
+ $f = $this->getFavorite($id);
+ if ($f !== false) {
+ $prefs[$id] = $f;
+ }
+ }
+ return $prefs;
+ }
+
+ /**
+ * setUserPrefs - get user favorites from settings. These are complete favorites, not ids only
+ * returned favorites are the first non-empty list from :
+ * * user-defined favorites
+ * * globally-defined favorites
+ * * a failback list "new post" (shall never be empty)
+ * This method is called by ::setup()
+ * @access protected
+ *
+ */
+ protected function setUserPrefs()
+ {
+ $this->user_prefs = $this->getFavorites($this->local_prefs);
+ if (!count($this->user_prefs)) {
+ $this->user_prefs = $this->getFavorites($this->global_prefs);
+ }
+ if (!count($this->user_prefs)) {
+ $this->user_prefs = $this->getFavorites(['new_post']);
+ }
+ $u = explode('?', $_SERVER['REQUEST_URI']);
+ // Loop over prefs to enable active favorites
+ foreach ($this->user_prefs as $k => &$v) {
+ if (isset($v['active_cb']) && is_callable($v['active_cb'])) {
+ // Use callback if defined to match whether favorite is active or not
+ $v['active'] = call_user_func($v['active_cb'], $u[0], $_REQUEST);
+ } else {
+ // Failback active detection. We test against URI name & parameters
+ $v['active'] = true; // true until something proves it is false
+ $u = explode('?', $v['url'], 2);
+ if (!preg_match('/' . preg_quote($u[0], "/") . '/', $_SERVER['REQUEST_URI'])) {
+ $v['active'] = false; // no URI match
+ }
+ if (count($u) == 2) {
+ parse_str($u[1], $p);
+ // test against each request parameter.
+ foreach ($p as $k2 => $v2) {
+ if (!isset($_REQUEST[$k2]) || $_REQUEST[$k2] !== $v2) {
+ $v['active'] = false;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * migrateFavorites - migrate dc < 2.6 favorites to new format
+ *
+ * @access protected
+ *
+ */
+ protected function migrateFavorites()
+ {
+ $fav_ws = $this->core->auth->user_prefs->addWorkspace('favorites');
+ $this->local_prefs = [];
+ $this->global_prefs = [];
+ foreach ($fav_ws->dumpPrefs() as $k => $v) {
+ $fav = @unserialize($v['value']);
+ if (is_array($fav)) {
+ if ($v['global']) {
+ $this->global_prefs[] = $fav['name'];
+ } else {
+ $this->local_prefs[] = $fav['name'];
+ }
+ }
+ }
+ $this->ws->put('favorites', $this->global_prefs, 'array', 'User favorites', true, true);
+ $this->ws->put('favorites', $this->local_prefs);
+ $this->user_prefs = $this->getFavorites($this->local_prefs);
+ }
+
+ /**
+ * legacyFavorites - handle legacy favorites using adminDashboardFavs behavior
+ *
+ * @access protected
+ *
+ */
+ protected function legacyFavorites()
+ {
+ $f = new ArrayObject();
+ $this->core->callBehavior('adminDashboardFavs', $this->core, $f);
+ foreach ($f as $k => $v) {
+ $fav = [
+ 'title' => __($v[1]),
+ 'url' => $v[2],
+ 'small-icon' => $v[3],
+ 'large-icon' => $v[4],
+ 'permissions' => $v[5],
+ 'id' => $v[6],
+ 'class' => $v[7]
+ ];
+ $this->register($v[0], $fav);
+ }
+
+ }
+
+ /**
+ * getUserFavorites - returns favorites that correspond to current user
+ * (may be local, global, or failback favorites)
+ *
+ * @access public
+ *
+ * @return array array of favorites (enriched)
+ */
+ public function getUserFavorites()
+ {
+ return $this->user_prefs;
+ }
+
+ /**
+ * getFavoriteIDs - returns user-defined or global favorites ids list
+ * shall not be called outside preferences.php...
+ *
+ * @param boolean $global if true, retrieve global favs, user favs otherwise
+ *
+ * @access public
+ *
+ * @return array array of favorites ids (only ids, not enriched)
+ */
+ public function getFavoriteIDs($global = false)
+ {
+ return $global ? $this->global_prefs : $this->local_prefs;
+ }
+
+ /**
+ * setFavoriteIDs - stores user-defined or global favorites ids list
+ * shall not be called outside preferences.php...
+ *
+ * @param array $ids list of fav ids
+ * @param boolean $global if true, retrieve global favs, user favs otherwise
+ *
+ * @access public
+ */
+ public function setFavoriteIDs($ids, $global = false)
+ {
+ $this->ws->put('favorites', $ids, 'array', null, true, $global);
+ }
+
+ /**
+ * getAvailableFavoritesIDs - returns all available fav ids
+ *
+ * @access public
+ *
+ * @return array array of favorites ids (only ids, not enriched)
+ */
+ public function getAvailableFavoritesIDs()
+ {
+ return array_keys($this->fav_defs->getArrayCopy());
+ }
+
+ /**
+ * appendMenuTitle - adds favorites section title to sidebar menu
+ * shall not be called outside admin/prepend.php...
+ *
+ * @param dcMenu $menu admin menu instance
+ *
+ * @access public
+ */
+ public function appendMenuTitle($menu)
+ {
+ $menu['Favorites'] = new dcMenu('favorites-menu', 'My favorites');
+ $menu['Favorites']->title = __('My favorites');
+ }
+
+ /**
+ * appendMenu - adds favorites items title to sidebar menu
+ * shall not be called outside admin/prepend.php...
+ *
+ * @param dcMenu $menu admin menu instance
+ *
+ * @access public
+ */
+ public function appendMenu($menu)
+ {
+ foreach ($this->user_prefs as $k => $v) {
+ $menu['Favorites']->addItem(
+ $v['title'],
+ $v['url'],
+ $v['small-icon'],
+ $v['active'],
+ true,
+ $v['id'],
+ $v['class'],
+ true
+ );
+ }
+ }
+
+ /**
+ * appendDashboardIcons - adds favorites icons to index page
+ * shall not be called outside admin/index.php...
+ *
+ * @param array $icons dashboard icon list to enrich
+ *
+ * @access public
+ */
+ public function appendDashboardIcons($icons)
+ {
+ foreach ($this->user_prefs as $k => $v) {
+ if (isset($v['dashboard_cb']) && is_callable($v['dashboard_cb'])) {
+ $v = new ArrayObject($v);
+ call_user_func($v['dashboard_cb'], $this->core, $v);
+ }
+ $icons[$k] = new ArrayObject([$v['title'], $v['url'], $v['large-icon']]);
+ $this->core->callBehavior('adminDashboardFavsIcon', $this->core, $k, $icons[$k]);
+ }
+ }
+
+ /**
+ * register - registers a new favorite definition
+ *
+ * @param string $id favorite id
+ * @param array $data favorite information. Array keys are :
+ * 'title' => favorite title (localized)
+ * 'url' => favorite URL,
+ * 'small-icon' => favorite small icon (for menu)
+ * 'large-icon' => favorite large icon (for dashboard)
+ * 'permissions' => (optional) comma-separated list of permissions for thie fav, if not set : no restriction
+ * 'dashboard_cb' => (optional) callback to modify title if dynamic, if not set : title is taken as is
+ * 'active_cb' => (optional) callback to tell whether current page matches favorite or not, for complex pages
+ *
+ * @access public
+ */
+ public function register($id, $data)
+ {
+ $this->fav_defs[$id] = $data;
+ return $this;
+ }
+
+ /**
+ * registerMultiple - registers a list of favorites definition
+ *
+ * @param array an array defining all favorites key is the id, value is the data.
+ * see register method for data format
+ * @access public
+ */
+ public function registerMultiple($data)
+ {
+ foreach ($data as $k => $v) {
+ $this->register($k, $v);
+ }
+ return $this;
+ }
+
+ /**
+ * exists - tells whether a fav definition exists or not
+ *
+ * @param string $id : the fav id to test
+ *
+ * @access public
+ *
+ * @return true if the fav definition exists, false otherwise
+ */
+ public function exists($id)
+ {
+ return isset($this->fav_defs[$id]);
+ }
+
+}
+
+/**
+ * defaultFavorites -- default favorites definition
+ *
+ */
+class defaultFavorites
+{
+ public static function initDefaultFavorites($favs)
+ {
+ $core = &$GLOBALS['core'];
+ $favs->registerMultiple([
+ 'prefs' => [
+ 'title' => __('My preferences'),
+ 'url' => $core->adminurl->get("admin.user.preferences"),
+ 'small-icon' => 'images/menu/user-pref.png',
+ 'large-icon' => 'images/menu/user-pref-b.png'],
+ 'new_post' => [
+ 'title' => __('New post'),
+ 'url' => $core->adminurl->get("admin.post"),
+ 'small-icon' => 'images/menu/edit.png',
+ 'large-icon' => 'images/menu/edit-b.png',
+ 'permissions' => 'usage,contentadmin'],
+ 'posts' => [
+ 'title' => __('Posts'),
+ 'url' => $core->adminurl->get("admin.posts"),
+ 'small-icon' => 'images/menu/entries.png',
+ 'large-icon' => 'images/menu/entries-b.png',
+ 'permissions' => 'usage,contentadmin',
+ 'dashboard_cb' => ['defaultFavorites', 'postsDashboard']],
+ 'comments' => [
+ 'title' => __('Comments'),
+ 'url' => $core->adminurl->get("admin.comments"),
+ 'small-icon' => 'images/menu/comments.png',
+ 'large-icon' => 'images/menu/comments-b.png',
+ 'permissions' => 'usage,contentadmin',
+ 'dashboard_cb' => ['defaultFavorites', 'commentsDashboard']],
+ 'search' => [
+ 'title' => __('Search'),
+ 'url' => $core->adminurl->get("admin.search"),
+ 'small-icon' => 'images/menu/search.png',
+ 'large-icon' => 'images/menu/search-b.png',
+ 'permissions' => 'usage,contentadmin'],
+ 'categories' => [
+ 'title' => __('Categories'),
+ 'url' => $core->adminurl->get("admin.categories"),
+ 'small-icon' => 'images/menu/categories.png',
+ 'large-icon' => 'images/menu/categories-b.png',
+ 'permissions' => 'categories'],
+ 'media' => [
+ 'title' => __('Media manager'),
+ 'url' => $core->adminurl->get("admin.media"),
+ 'small-icon' => 'images/menu/media.png',
+ 'large-icon' => 'images/menu/media-b.png',
+ 'permissions' => 'media,media_admin'],
+ 'blog_pref' => [
+ 'title' => __('Blog settings'),
+ 'url' => $core->adminurl->get("admin.blog.pref"),
+ 'small-icon' => 'images/menu/blog-pref.png',
+ 'large-icon' => 'images/menu/blog-pref-b.png',
+ 'permissions' => 'admin'],
+ 'blog_theme' => [
+ 'title' => __('Blog appearance'),
+ 'url' => $core->adminurl->get("admin.blog.theme"),
+ 'small-icon' => 'images/menu/themes.png',
+ 'large-icon' => 'images/menu/blog-theme-b.png',
+ 'permissions' => 'admin'],
+ 'blogs' => [
+ 'title' => __('Blogs'),
+ 'url' => $core->adminurl->get("admin.blogs"),
+ 'small-icon' => 'images/menu/blogs.png',
+ 'large-icon' => 'images/menu/blogs-b.png',
+ 'permissions' => 'usage,contentadmin'],
+ 'users' => [
+ 'title' => __('Users'),
+ 'url' => $core->adminurl->get("admin.users"),
+ 'small-icon' => 'images/menu/users.png',
+ 'large-icon' => 'images/menu/users-b.png'],
+ 'plugins' => [
+ 'title' => __('Plugins management'),
+ 'url' => $core->adminurl->get("admin.plugins"),
+ 'small-icon' => 'images/menu/plugins.png',
+ 'large-icon' => 'images/menu/plugins-b.png'],
+ 'langs' => [
+ 'title' => __('Languages'),
+ 'url' => $core->adminurl->get("admin.langs"),
+ 'small-icon' => 'images/menu/langs.png',
+ 'large-icon' => 'images/menu/langs-b.png'],
+ 'help' => [
+ 'title' => __('Global help'),
+ 'url' => $core->adminurl->get("admin.help"),
+ 'small-icon' => 'images/menu/help.png',
+ 'large-icon' => 'images/menu/help-b.png']
+ ]);
+ }
+
+ public static function postsDashboard($core, $v)
+ {
+ $post_count = $core->blog->getPosts([], true)->f(0);
+ $str_entries = __('%d post', '%d posts', $post_count);
+ $v['title'] = sprintf($str_entries, $post_count);
+ }
+
+ public static function commentsDashboard($core, $v)
+ {
+ $comment_count = $core->blog->getComments([], true)->f(0);
+ $str_comments = __('%d comment', '%d comments', $comment_count);
+ $v['title'] = sprintf($str_comments, $comment_count);
+ }
+}
diff --git a/dotclear._no/inc/admin/class.dc.menu.php b/dotclear._no/inc/admin/class.dc.menu.php
new file mode 100644
index 0000000..7a0aeea
--- /dev/null
+++ b/dotclear._no/inc/admin/class.dc.menu.php
@@ -0,0 +1,109 @@
+id = $id;
+ $this->title = $title;
+ $this->itemSpace = $itemSpace;
+ $this->pinned = [];
+ $this->items = [];
+ }
+
+ public function addItem($title, $url, $img, $active, $show = true, $id = null, $class = null, $pinned = false)
+ {
+ if ($show) {
+ $item = $this->itemDef($title, $url, $img, $active, $id, $class);
+ if ($pinned) {
+ $this->pinned[] = $item;
+ } else {
+ $this->items[$title] = $item;
+ }
+ }
+ }
+
+ public function prependItem($title, $url, $img, $active, $show = true, $id = null, $class = null, $pinned = false)
+ {
+ if ($show) {
+ $item = $this->itemDef($title, $url, $img, $active, $id, $class);
+ if ($pinned) {
+ array_unshift($this->pinned, $item);
+ } else {
+ $this->items[$title] = $item;
+ }
+ }
+ }
+
+ public function draw()
+ {
+ if (count($this->items) + count($this->pinned) == 0) {
+ return '';
+ }
+
+ $res =
+ '' .
+ ($this->title ? '
' . $this->title . ' ' : '') .
+ '
' . "\n";
+
+ // 1. Display pinned items (unsorted)
+ for ($i = 0; $i < count($this->pinned); $i++) {
+ if ($i + 1 < count($this->pinned) && $this->itemSpace != '') {
+ $res .= preg_replace('|$|', $this->itemSpace . '', $this->pinned[$i]);
+ $res .= "\n";
+ } else {
+ $res .= $this->pinned[$i] . "\n";
+ }
+ }
+
+ // 2. Display unpinned itmes (sorted)
+ $i = 0;
+ dcUtils::lexicalKeySort($this->items);
+ foreach ($this->items as $title => $item) {
+ if ($i + 1 < count($this->items) && $this->itemSpace != '') {
+ $res .= preg_replace('|$|', $this->itemSpace . '', $item);
+ $res .= "\n";
+ } else {
+ $res .= $item . "\n";
+ }
+ $i++;
+ }
+
+ $res .= ' ' . "\n";
+
+ return $res;
+ }
+
+ protected function itemDef($title, $url, $img, $active, $id = null, $class = null)
+ {
+ if (is_array($url)) {
+ $link = $url[0];
+ $ahtml = (!empty($url[1])) ? ' ' . $url[1] : '';
+ } else {
+ $link = $url;
+ $ahtml = '';
+ }
+
+ $img = dc_admin_icon_url($img);
+
+ return
+ '' .
+
+ '' . $title . ' ' . "\n";
+ }
+}
diff --git a/dotclear._no/inc/admin/class.dc.notices.php b/dotclear._no/inc/admin/class.dc.notices.php
new file mode 100644
index 0000000..c350e9d
--- /dev/null
+++ b/dotclear._no/inc/admin/class.dc.notices.php
@@ -0,0 +1,155 @@
+ "success",
+ "warning" => "warning-msg",
+ "error" => "error",
+ "message" => "message",
+ "static" => "static-msg"];
+
+ private $error_displayed = false;
+
+ /**
+ * Class constructor
+ *
+ * @param mixed $core dotclear core
+ *
+ * @access public
+ *
+ * @return mixed Value.
+ */
+ public function __construct($core)
+ {
+ $this->core = $core;
+ }
+
+ /* Session based notices */
+
+ public function getNotices()
+ {
+ $res = '';
+
+ // return error messages if any
+ if ($this->core->error->flag() && !$this->error_displayed) {
+
+ # --BEHAVIOR-- adminPageNotificationError
+ $notice_error = $this->core->callBehavior('adminPageNotificationError', $this->core, $this->core->error);
+
+ if (isset($notice_error) && !empty($notice_error)) {
+ $res .= $notice_error;
+ } else {
+ $res .= '' .
+ '' . (count($this->core->error->getErrors()) > 1 ? __('Errors:') : __('Error:')) . ' ' .
+ '
' . $this->core->error->toHTML() . '
';
+ }
+ $this->error_displayed = true;
+ }
+
+ // return notices if any
+ if (isset($_SESSION['notifications'])) {
+ foreach ($_SESSION['notifications'] as $notification) {
+ # --BEHAVIOR-- adminPageNotification
+ $notice = $this->core->callBehavior('adminPageNotification', $this->core, $notification);
+
+ $res .= (isset($notice) && !empty($notice) ? $notice : $this->getNotification($notification));
+ }
+ unset($_SESSION['notifications']);
+ // unset seems not to be sufficient, so initialize to an empty array
+ $_SESSION['notifications'] = [];
+ }
+ return $res;
+ }
+
+ public function addNotice($type, $message, $options = [])
+ {
+ if (isset($this->N_TYPES[$type])) {
+ $class = $this->N_TYPES[$type];
+ } else {
+ $class = $type;
+ }
+ if (isset($_SESSION['notifications']) && is_array($_SESSION['notifications'])) {
+ $notifications = $_SESSION['notifications'];
+ } else {
+ $notifications = [];
+ }
+
+ $n = array_merge($options, ['class' => $class, 'ts' => time(), 'text' => $message]);
+ if ($type != "static") {
+ $notifications[] = $n;
+ } else {
+ array_unshift($notifications, $n);
+ }
+ $_SESSION['notifications'] = $notifications;
+ }
+
+ public function addSuccessNotice($message, $options = [])
+ {
+ $this->addNotice("success", $message, $options);
+ }
+
+ public function addWarningNotice($message, $options = [])
+ {
+ $this->addNotice("warning", $message, $options);
+ }
+
+ public function addErrorNotice($message, $options = [])
+ {
+ $this->addNotice("error", $message, $options);
+ }
+
+ protected function getNotification($n)
+ {
+ $tag = (isset($n['divtag']) && $n['divtag']) ? 'div' : 'p';
+ $ts = '';
+ if (!isset($n['with_ts']) || ($n['with_ts'] == true)) {
+ $ts = dt::str(__('[%H:%M:%S]'), $n['ts'], $this->core->auth->getInfo('user_tz')) . ' ';
+ }
+ $res = '<' . $tag . ' class="' . $n['class'] . '" role="alert">' . $ts . $n['text'] . '' . $tag . '>';
+ return $res;
+ }
+
+ /* Direct messages, usually immediately displayed */
+
+ public function message($msg, $timestamp = true, $div = false, $echo = true, $class = 'message')
+ {
+ $res = '';
+ if ($msg != '') {
+ $res = ($div ? '' : '') . '
' .
+ ($timestamp ? dt::str(__('[%H:%M:%S]'), null, $this->core->auth->getInfo('user_tz')) . ' ' : '') . $msg .
+ '
' . ($div ? '
' : '');
+ if ($echo) {
+ echo $res;
+ }
+ }
+ return $res;
+ }
+
+ public function success($msg, $timestamp = true, $div = false, $echo = true)
+ {
+ return $this->message($msg, $timestamp, $div, $echo, "success");
+ }
+
+ public function warning($msg, $timestamp = true, $div = false, $echo = true)
+ {
+ return $this->message($msg, $timestamp, $div, $echo, "warning-msg");
+ }
+}
diff --git a/dotclear._no/inc/admin/lib.admincombos.php b/dotclear._no/inc/admin/lib.admincombos.php
new file mode 100644
index 0000000..ee511bf
--- /dev/null
+++ b/dotclear._no/inc/admin/lib.admincombos.php
@@ -0,0 +1,214 @@
+record the category record
+ @return array the combo box (form::combo -compatible format)
+ */
+ public static function getCategoriesCombo($categories, $include_empty = true, $use_url = false)
+ {
+ $categories_combo = [];
+ if ($include_empty) {
+ $categories_combo = [new formSelectOption(__('(No cat)'), '')];
+ }
+ while ($categories->fetch()) {
+ $categories_combo[] = new formSelectOption(
+ str_repeat(' ', ($categories->level - 1) * 4) .
+ html::escapeHTML($categories->cat_title) . ' (' . $categories->nb_post . ')',
+ ($use_url ? $categories->cat_url : $categories->cat_id),
+ ($categories->level - 1 ? 'sub-option' . ($categories->level - 1) : '')
+ );
+ }
+ return $categories_combo;
+ }
+
+ /**
+ Returns available post status combo
+
+ @return array the combo box (form::combo -compatible format)
+ */
+ public static function getPostStatusesCombo()
+ {
+ $status_combo = [];
+ foreach (self::$core->blog->getAllPostStatus() as $k => $v) {
+ $status_combo[$v] = (string) $k;
+ }
+ return $status_combo;
+ }
+
+ /**
+ Returns an users combo from a users record
+
+ @param users record the users record
+ @return array the combo box (form::combo -compatible format)
+ */
+ public static function getUsersCombo($users)
+ {
+ $users_combo = [];
+ while ($users->fetch()) {
+ $user_cn = dcUtils::getUserCN($users->user_id, $users->user_name,
+ $users->user_firstname, $users->user_displayname);
+
+ if ($user_cn != $users->user_id) {
+ $user_cn .= ' (' . $users->user_id . ')';
+ }
+
+ $users_combo[$user_cn] = $users->user_id;
+ }
+ return $users_combo;
+ }
+
+ /**
+ Returns an date combo from a date record
+
+ @param dates record the dates record
+ @return array the combo box (form::combo -compatible format)
+ */
+ public static function getDatesCombo($dates)
+ {
+ $dt_m_combo = [];
+ while ($dates->fetch()) {
+ $dt_m_combo[dt::str('%B %Y', $dates->ts())] = $dates->year() . $dates->month();
+ }
+ return $dt_m_combo;
+ }
+
+ /**
+ Returns an lang combo from a lang record
+
+ @param langs record the langs record
+ @param with_available boolean if false, only list items from record
+ if true, also list available languages
+ @return array the combo box (form::combo -compatible format)
+ */
+ public static function getLangsCombo($langs, $with_available = false)
+ {
+ $all_langs = l10n::getISOcodes(0, 1);
+ if ($with_available) {
+ $langs_combo = ['' => '', __('Most used') => [], __('Available') => l10n::getISOcodes(1, 1)];
+ while ($langs->fetch()) {
+ if (isset($all_langs[$langs->post_lang])) {
+ $langs_combo[__('Most used')][$all_langs[$langs->post_lang]] = $langs->post_lang;
+ unset($langs_combo[__('Available')][$all_langs[$langs->post_lang]]);
+ } else {
+ $langs_combo[__('Most used')][$langs->post_lang] = $langs->post_lang;
+ }
+ }
+ } else {
+ $langs_combo = [];
+ while ($langs->fetch()) {
+ $lang_name = isset($all_langs[$langs->post_lang]) ? $all_langs[$langs->post_lang] : $langs->post_lang;
+ $langs_combo[$lang_name] = $langs->post_lang;
+ }
+ }
+ unset($all_langs);
+ return $langs_combo;
+ }
+
+ /**
+ Returns a combo containing all available and installed languages for administration pages
+
+ @return array the combo box (form::combo -compatible format)
+ */
+ public static function getAdminLangsCombo()
+ {
+ $lang_combo = [];
+ $langs = l10n::getISOcodes(1, 1);
+ foreach ($langs as $k => $v) {
+ $lang_avail = $v == 'en' || is_dir(DC_L10N_ROOT . '/' . $v);
+ $lang_combo[] = new formSelectOption($k, $v, $lang_avail ? 'avail10n' : '');
+ }
+ return $lang_combo;
+ }
+
+ /**
+ Returns a combo containing all available editors in admin
+
+ @return array the combo box (form::combo -compatible format)
+ */
+ public static function getEditorsCombo()
+ {
+ $editors_combo = [];
+
+ foreach (self::$core->getEditors() as $v) {
+ $editors_combo[$v] = $v;
+ }
+
+ return $editors_combo;
+ }
+
+ /**
+ Returns a combo containing all available formaters by editor in admin
+
+ @param editor_id string Editor id (dcLegacyEditor, dcCKEditor, ...)
+ @return array the combo box (form::combo -compatible format)
+ */
+ public static function getFormatersCombo($editor_id = '')
+ {
+ $formaters_combo = [];
+
+ if (!empty($editor_id)) {
+ foreach (self::$core->getFormaters($editor_id) as $formater) {
+ $formaters_combo[$formater] = $formater;
+ }
+ } else {
+ foreach (self::$core->getFormaters() as $editor => $formaters) {
+ foreach ($formaters as $formater) {
+ $formaters_combo[$editor][$formater] = $formater;
+ }
+ }
+ }
+
+ return $formaters_combo;
+ }
+
+ /**
+ Returns a combo containing available blog statuses
+
+ @return array the combo box (form::combo -compatible format)
+ */
+ public static function getBlogStatusesCombo()
+ {
+ $status_combo = [];
+ foreach (self::$core->getAllBlogStatus() as $k => $v) {
+ $status_combo[$v] = (string) $k;
+ }
+ return $status_combo;
+ }
+
+ /**
+ Returns a combo containing available comment statuses
+
+ @return array the combo box (form::combo -compatible format)
+ */
+ public static function getCommentStatusescombo()
+ {
+ $status_combo = [];
+ foreach (self::$core->blog->getAllCommentStatus() as $k => $v) {
+ $status_combo[$v] = (string) $k;
+ }
+ return $status_combo;
+ }
+}
+dcAdminCombos::$core = $GLOBALS['core'];
diff --git a/dotclear._no/inc/admin/lib.dc.adminurl.php b/dotclear._no/inc/admin/lib.dc.adminurl.php
new file mode 100644
index 0000000..3d42153
--- /dev/null
+++ b/dotclear._no/inc/admin/lib.dc.adminurl.php
@@ -0,0 +1,170 @@
+dcCore Dotclear core reference
+ */
+ public function __construct($core)
+ {
+ $this->core = $core;
+ $this->urls = new ArrayObject();
+ }
+
+ /**
+ * Registers a new url
+ * @param string $name the url name
+ * @param string $url url value
+ * @param array $params query string params (optional)
+ */
+ public function register($name, $url, $params = [])
+ {
+ $this->urls[$name] = ['url' => $url, 'qs' => $params];
+ }
+
+ /**
+ * Registers a new url as a copy of an existing one
+ * @param string $name url name
+ * @param streing $orig url to copy information from
+ * @param array $params extra parameters to add
+ * @param string $newurl new url if different from the original
+ */
+ public function registercopy($name, $orig, $params = [], $newurl = '')
+ {
+ if (!isset($this->urls[$orig])) {
+ throw new exception('Unknown URL handler for ' . $orig);
+ }
+ $url = $this->urls[$orig];
+ $url['qs'] = array_merge($url['qs'], $params);
+ if ($newurl != '') {
+ $url['url'] = $newurl;
+ }
+ $this->urls[$name] = $url;
+ }
+
+ /**
+ * retrieves a URL given its name, and optional parameters
+ *
+ * @param string $name URL Name
+ * @param array $params query string parameters, given as an associative array
+ * @param boolean $urlencode set to true if url may not be encoded
+ * @param string $separator separator to use between QS parameters
+ * @param boolean $parametric set to true if url will be used as (s)printf() format.
+ * @return string the forged url
+ */
+ public function get($name, $params = [], $separator = '&', $parametric = false)
+ {
+ if (!isset($this->urls[$name])) {
+ throw new exception('Unknown URL handler for ' . $name);
+ }
+ $url = $this->urls[$name];
+ $p = array_merge($url['qs'], $params);
+ $u = $url['url'];
+ if (!empty($p)) {
+ $u .= '?' . http_build_query($p, '', $separator);
+ }
+ if ($parametric) {
+ // Dirty hack to get back %[n$]s instead of %25[{0..9}%24]s in URLs used with (s)printf(), as http_build_query urlencode() its result.
+ $u = preg_replace('/\%25(([0-9])+?\%24)*?s/', '%$2s', $u);
+ }
+ return $u;
+ }
+
+ /**
+ * retrieves a URL given its name, and optional parameters
+ *
+ * @param string $name URL Name
+ * @param array $params query string parameters, given as an associative array
+ * @param boolean $urlencode set to true if url may not be encoded
+ * @param string $suffix suffix to be added to the QS parameters
+ * @return string the forged url
+ */
+ public function redirect($name, $params = [], $suffix = "")
+ {
+ if (!isset($this->urls[$name])) {
+ throw new exception('Unknown URL handler for ' . $name);
+ }
+ http::redirect($this->get($name, $params, '&') . $suffix);
+ }
+
+ /**
+ * retrieves a php page given its name, and optional parameters
+ * acts like get, but without the query string, should be used within forms actions
+ *
+ * @param string $name URL Name
+ * @return string the forged url
+ */
+ public function getBase($name)
+ {
+ if (!isset($this->urls[$name])) {
+ throw new exception('Unknown URL handler for ' . $name);
+ }
+ return $this->urls[$name]['url'];
+ }
+
+ /**
+ * forges form hidden fields to pass to a generated ' .
+ '' .
+ '
' .
+ '' . html::escapeHTML(__('Plugins list')) . ' ';
+
+ if (in_array('name', $cols)) {
+ $colspan = 1;
+ if (in_array('checkbox', $cols)) {
+ $colspan++;
+ }
+ if (in_array('icon', $cols)) {
+ $colspan++;
+ }
+ echo
+ ' 1 ? ' colspan="' . $colspan . '"' : '') . '>' . __('Name') . ' ';
+ }
+
+ if (in_array('score', $cols) && $this->getSearch() !== null && defined('DC_DEBUG') && DC_DEBUG) {
+ echo
+ '' . __('Score') . ' ';
+ }
+
+ if (in_array('version', $cols)) {
+ echo
+ '' . __('Version') . ' ';
+ }
+
+ if (in_array('current_version', $cols)) {
+ echo
+ '' . __('Current version') . ' ';
+ }
+
+ if (in_array('desc', $cols)) {
+ echo
+ '' . __('Details') . ' ';
+ }
+
+ if (in_array('distrib', $cols)) {
+ echo
+ ' ';
+ }
+
+ if (!empty($actions) && $this->core->auth->isSuperAdmin()) {
+ echo
+ '' . __('Action') . ' ';
+ }
+
+ echo
+ ' ';
+
+ $sort_field = $this->getSort();
+
+ # Sort modules by $sort_field (default sname)
+ $modules = $this->getSearch() === null ?
+ self::sortModules($this->data, $sort_field, $this->sort_asc) :
+ $this->data;
+
+ $count = 0;
+ foreach ($modules as $id => $module) {
+ # Show only requested modules
+ if ($nav_limit && $this->getSearch() === null) {
+ $char = substr($module[$sort_field], 0, 1);
+ if (!in_array($char, $this->nav_list)) {
+ $char = $this->nav_special;
+ }
+ if ($this->getIndex() != $char) {
+ continue;
+ }
+ }
+
+ echo
+ '';
+
+ $tds = 0;
+
+ if (in_array('checkbox', $cols)) {
+ $tds++;
+ echo
+ '' .
+ form::checkbox(['modules[' . $count . ']', html::escapeHTML($this->list_id) . '_modules_' . html::escapeHTML($id)], html::escapeHTML($id)) .
+ ' ';
+ }
+
+ if (in_array('icon', $cols)) {
+ $tds++;
+ echo
+ '' . sprintf(
+ ' ',
+ html::escapeHTML($id), file_exists($module['root'] . '/icon.png') ?
+ dcPage::getPF($id . '/icon.png') : 'images/module.png'
+ ) . ' ';
+ }
+
+ $tds++;
+ echo
+ '';
+ if (in_array('checkbox', $cols)) {
+ if (in_array('expander', $cols)) {
+ echo
+ html::escapeHTML($module['name']) . ($id != $module['name'] ? sprintf(__(' (%s)'), $id) : '');
+ } else {
+ echo
+ '' .
+ html::escapeHTML($module['name']) . ($id != $module['name'] ? sprintf(__(' (%s)'), $id) : '') .
+ ' ';
+ }
+ } else {
+ echo
+ html::escapeHTML($module['name']) . ($id != $module['name'] ? sprintf(__(' (%s)'), $id) : '') .
+ form::hidden(['modules[' . $count . ']'], html::escapeHTML($id));
+ }
+ echo
+ $this->core->formNonce() .
+ '';
+
+ # Display score only for debug purpose
+ if (in_array('score', $cols) && $this->getSearch() !== null && defined('DC_DEBUG') && DC_DEBUG) {
+ $tds++;
+ echo
+ ' ' . $module['score'] . ' ';
+ }
+
+ if (in_array('version', $cols)) {
+ $tds++;
+ echo
+ '' . html::escapeHTML($module['version']) . ' ';
+ }
+
+ if (in_array('current_version', $cols)) {
+ $tds++;
+ echo
+ '' . html::escapeHTML($module['current_version']) . ' ';
+ }
+
+ if (in_array('desc', $cols)) {
+ $tds++;
+ echo
+ '' . html::escapeHTML(__($module['desc']));
+ if (isset($module['cannot_disable']) && $module['enabled']) {
+ echo
+ '' .
+ sprintf(__('This module cannot be disabled nor deleted, since the following modules are also enabled : %s'),
+ join(',', $module['cannot_disable'])) .
+ ' ';
+ }
+ if (isset($module['cannot_enable']) && !$module['enabled']) {
+ echo
+ '' .
+ __('This module cannot be enabled, because of the following reasons :') .
+ '';
+ foreach ($module['cannot_enable'] as $m => $reason) {
+ echo '' . $reason . ' ';
+ }
+ echo ' ' .
+ ' ';
+ }
+ echo ' ';
+
+ }
+
+ if (in_array('distrib', $cols)) {
+ $tds++;
+ echo
+ '' . (self::isDistributedModule($id) ?
+ ' '
+ : '') . ' ';
+ }
+
+ if (!empty($actions) && $this->core->auth->isSuperAdmin()) {
+ $buttons = $this->getActions($id, $module, $actions);
+
+ $tds++;
+ echo
+ '' .
+
+ '' . implode(' ', $buttons) . '
' .
+
+ ' ';
+ }
+
+ echo
+ ' ';
+
+ # Other informations
+ if (in_array('expander', $cols)) {
+ echo
+ '';
+
+ if (!empty($module['author']) || !empty($module['details']) || !empty($module['support'])) {
+ echo
+ '';
+
+ if (!empty($module['author'])) {
+ echo
+ '' . __('Author:') . ' ' . html::escapeHTML($module['author']) . ' ';
+ }
+
+ $more = [];
+ if (!empty($module['details'])) {
+ $more[] = '' . __('Details') . ' ';
+ }
+
+ if (!empty($module['support'])) {
+ $more[] = '' . __('Support') . ' ';
+ }
+
+ if (!empty($more)) {
+ echo
+ '' . implode(' - ', $more) . ' ';
+ }
+
+ echo
+ ' ';
+ }
+
+ $config = !empty($module['root']) && file_exists(path::real($module['root'] . '/_config.php'));
+ $index = !empty($module['root']) && file_exists(path::real($module['root'] . '/index.php'));
+
+ if ($config || $index || !empty($module['section']) || !empty($module['tags']) || !empty($module['settings'])) {
+ echo
+ '';
+
+ if ($index && $module['enabled']) {
+ echo '' . __('Manage plugin') . ' ';
+ }
+
+ $settings = $this->getSettingsUrls($this->core, $id);
+ if (!empty($settings) && $module['enabled']) {
+ echo '' . implode(' - ', $settings) . ' ';
+ }
+
+ if (!empty($module['section'])) {
+ echo
+ '' . __('Section:') . ' ' . html::escapeHTML($module['section']) . ' ';
+ }
+
+ if (!empty($module['tags'])) {
+ echo
+ '' . __('Tags:') . ' ' . html::escapeHTML($module['tags']) . ' ';
+ }
+
+ echo
+ ' ';
+ }
+
+ echo
+ ' ';
+ }
+
+ $count++;
+ }
+ echo
+ '
';
+
+ if (!$count && $this->getSearch() === null) {
+ echo
+ '' . __('No plugins matched your search.') . '
';
+ } elseif ((in_array('checkbox', $cols) || $count > 1) && !empty($actions) && $this->core->auth->isSuperAdmin()) {
+ $buttons = $this->getGlobalActions($actions, in_array('checkbox', $cols));
+
+ if (!empty($buttons)) {
+ if (in_array('checkbox', $cols)) {
+ echo
+ '
';
+ }
+ echo
+ '' . implode(' ', $buttons) . '
';
+ }
+ }
+ echo
+ ' ';
+
+ return $this;
+ }
+
+ /**
+ * Get settings URLs if any
+ *
+ * @param object $core
+ * @param string $id module ID
+ * @param boolean $check check permission
+ * @param boolean $self include self URL (→ plugin index.php URL)
+ * @return Array of settings URLs
+ */
+ public static function getSettingsUrls($core, $id, $check = false, $self = true)
+ {
+ $st = [];
+
+ $mr = $core->plugins->moduleRoot($id);
+ $config = !empty($mr) && file_exists(path::real($mr . '/_config.php'));
+ $settings = $core->plugins->moduleInfo($id, 'settings');
+ if ($config || !empty($settings)) {
+ if ($config) {
+ if (!$check ||
+ $core->auth->isSuperAdmin() ||
+ $core->auth->check($core->plugins->moduleInfo($id, 'permissions'), $core->blog->id)) {
+ $params = ['module' => $id, 'conf' => '1'];
+ if (!$core->plugins->moduleInfo($id, 'standalone_config') && !$self) {
+ $params['redir'] = $core->adminurl->get('admin.plugin.' . $id);
+ }
+ $st[] = '' . __('Configure plugin') . ' ';
+ }
+ }
+ if (is_array($settings)) {
+ foreach ($settings as $sk => $sv) {
+ switch ($sk) {
+ case 'blog':
+ if (!$check ||
+ $core->auth->isSuperAdmin() ||
+ $core->auth->check('admin', $core->blog->id)) {
+ $st[] = '' . __('Plugin settings (in blog parameters)') . ' ';
+ }
+ break;
+ case 'pref':
+ if (!$check ||
+ $core->auth->isSuperAdmin() ||
+ $core->auth->check('usage,contentadmin', $core->blog->id)) {
+ $st[] = '' . __('Plugin settings (in user preferences)') . ' ';
+ }
+ break;
+ case 'self':
+ if ($self) {
+ if (!$check ||
+ $core->auth->isSuperAdmin() ||
+ $core->auth->check($core->plugins->moduleInfo($id, 'permissions'), $core->blog->id)) {
+ $st[] = '' . __('Plugin settings') . ' ';
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ return $st;
+ }
+
+ /**
+ * Get action buttons to add to modules list.
+ *
+ * @param string $id Module ID
+ * @param array $module Module info
+ * @param array $actions Actions keys
+ * @return Array of actions buttons
+ */
+ protected function getActions($id, $module, $actions)
+ {
+ $submits = [];
+
+ # Use loop to keep requested order
+ foreach ($actions as $action) {
+ switch ($action) {
+
+ # Deactivate
+ case 'activate':if ($this->core->auth->isSuperAdmin() && $module['root_writable'] && !isset($module['cannot_enable'])) {
+ $submits[] =
+ ' ';
+ }break;
+
+ # Activate
+ case 'deactivate':if ($this->core->auth->isSuperAdmin() && $module['root_writable'] && !isset($module['cannot_disable'])) {
+ $submits[] =
+ ' ';
+ }break;
+
+ # Delete
+ case 'delete':if ($this->core->auth->isSuperAdmin() && $this->isDeletablePath($module['root']) && !isset($module['cannot_disable'])) {
+ $dev = !preg_match('!^' . $this->path_pattern . '!', $module['root']) && defined('DC_DEV') && DC_DEV ? ' debug' : '';
+ $submits[] =
+ ' ';
+ }break;
+
+ # Install (from store)
+ case 'install':if ($this->core->auth->isSuperAdmin() && $this->path_writable) {
+ $submits[] =
+ ' ';
+ }break;
+
+ # Update (from store)
+ case 'update':if ($this->core->auth->isSuperAdmin() && $this->path_writable) {
+ $submits[] =
+ ' ';
+ }break;
+
+ # Behavior
+ case 'behavior':
+
+ # --BEHAVIOR-- adminModulesListGetActions
+ $tmp = $this->core->callBehavior('adminModulesListGetActions', $this, $id, $module);
+
+ if (!empty($tmp)) {
+ $submits[] = $tmp;
+ }
+ break;
+ }
+ }
+
+ return $submits;
+ }
+
+ /**
+ * Get global action buttons to add to modules list.
+ *
+ * @param array $actions Actions keys
+ * @param boolean $with_selection Limit action to selected modules
+ * @return Array of actions buttons
+ */
+ protected function getGlobalActions($actions, $with_selection = false)
+ {
+ $submits = [];
+
+ # Use loop to keep requested order
+ foreach ($actions as $action) {
+ switch ($action) {
+
+ # Deactivate
+ case 'activate':if ($this->core->auth->isSuperAdmin() && $this->path_writable) {
+ $submits[] =
+ ' ';
+ }break;
+
+ # Activate
+ case 'deactivate':if ($this->core->auth->isSuperAdmin() && $this->path_writable) {
+ $submits[] =
+ ' ';
+ }break;
+
+ # Update (from store)
+ case 'update':if ($this->core->auth->isSuperAdmin() && $this->path_writable) {
+ $submits[] =
+ ' ';
+ }break;
+
+ # Behavior
+ case 'behavior':
+
+ # --BEHAVIOR-- adminModulesListGetGlobalActions
+ $tmp = $this->core->callBehavior('adminModulesListGetGlobalActions', $this, $with_selection);
+
+ if (!empty($tmp)) {
+ $submits[] = $tmp;
+ }
+ break;
+ }
+ }
+
+ return $submits;
+ }
+
+ /**
+ * Execute POST action.
+ *
+ * @note Set a notice on success through dcPage::addSuccessNotice
+ * @throw Exception Module not find or command failed
+ * @return Null
+ */
+ public function doActions()
+ {
+ if (empty($_POST) || !empty($_REQUEST['conf'])
+ || !$this->isWritablePath()) {
+ return;
+ }
+
+ $modules = !empty($_POST['modules']) && is_array($_POST['modules']) ? array_values($_POST['modules']) : [];
+
+ if ($this->core->auth->isSuperAdmin() && !empty($_POST['delete'])) {
+
+ if (is_array($_POST['delete'])) {
+ $modules = array_keys($_POST['delete']);
+ }
+
+ $list = $this->modules->getDisabledModules();
+
+ $failed = false;
+ $count = 0;
+ foreach ($modules as $id) {
+ if (!isset($list[$id])) {
+
+ if (!$this->modules->moduleExists($id)) {
+ throw new Exception(__('No such plugin.'));
+ }
+
+ $module = $this->modules->getModules($id);
+ $module['id'] = $id;
+
+ if (!$this->isDeletablePath($module['root'])) {
+ $failed = true;
+ continue;
+ }
+
+ # --BEHAVIOR-- moduleBeforeDelete
+ $this->core->callBehavior('pluginBeforeDelete', $module);
+
+ $this->modules->deleteModule($id);
+
+ # --BEHAVIOR-- moduleAfterDelete
+ $this->core->callBehavior('pluginAfterDelete', $module);
+ } else {
+ $this->modules->deleteModule($id, true);
+ }
+
+ $count++;
+ }
+
+ if (!$count && $failed) {
+ throw new Exception(__("You don't have permissions to delete this plugin."));
+ } elseif ($failed) {
+ dcPage::addWarningNotice(__('Some plugins have not been delete.'));
+ } else {
+ dcPage::addSuccessNotice(
+ __('Plugin has been successfully deleted.', 'Plugins have been successuflly deleted.', $count)
+ );
+ }
+ http::redirect($this->getURL());
+ } elseif ($this->core->auth->isSuperAdmin() && !empty($_POST['install'])) {
+
+ if (is_array($_POST['install'])) {
+ $modules = array_keys($_POST['install']);
+ }
+
+ $list = $this->store->get();
+
+ if (empty($list)) {
+ throw new Exception(__('No such plugin.'));
+ }
+
+ $count = 0;
+ foreach ($list as $id => $module) {
+
+ if (!in_array($id, $modules)) {
+ continue;
+ }
+
+ $dest = $this->getPath() . '/' . basename($module['file']);
+
+ # --BEHAVIOR-- moduleBeforeAdd
+ $this->core->callBehavior('pluginBeforeAdd', $module);
+
+ $this->store->process($module['file'], $dest);
+
+ # --BEHAVIOR-- moduleAfterAdd
+ $this->core->callBehavior('pluginAfterAdd', $module);
+
+ $count++;
+ }
+
+ dcPage::addSuccessNotice(
+ __('Plugin has been successfully installed.', 'Plugins have been successuflly installed.', $count)
+ );
+ http::redirect($this->getURL());
+ } elseif ($this->core->auth->isSuperAdmin() && !empty($_POST['activate'])) {
+
+ if (is_array($_POST['activate'])) {
+ $modules = array_keys($_POST['activate']);
+ }
+
+ $list = $this->modules->getDisabledModules();
+ if (empty($list)) {
+ throw new Exception(__('No such plugin.'));
+ }
+
+ $count = 0;
+ foreach ($list as $id => $module) {
+
+ if (!in_array($id, $modules)) {
+ continue;
+ }
+
+ # --BEHAVIOR-- moduleBeforeActivate
+ $this->core->callBehavior('pluginBeforeActivate', $id);
+
+ $this->modules->activateModule($id);
+
+ # --BEHAVIOR-- moduleAfterActivate
+ $this->core->callBehavior('pluginAfterActivate', $id);
+
+ $count++;
+ }
+
+ dcPage::addSuccessNotice(
+ __('Plugin has been successfully activated.', 'Plugins have been successuflly activated.', $count)
+ );
+ http::redirect($this->getURL());
+ } elseif ($this->core->auth->isSuperAdmin() && !empty($_POST['deactivate'])) {
+
+ if (is_array($_POST['deactivate'])) {
+ $modules = array_keys($_POST['deactivate']);
+ }
+
+ $list = $this->modules->getModules();
+ if (empty($list)) {
+ throw new Exception(__('No such plugin.'));
+ }
+
+ $failed = false;
+ $count = 0;
+ foreach ($list as $id => $module) {
+
+ if (!in_array($id, $modules)) {
+ continue;
+ }
+
+ if (!$module['root_writable']) {
+ $failed = true;
+ continue;
+ }
+
+ $module[$id] = $id;
+
+ # --BEHAVIOR-- moduleBeforeDeactivate
+ $this->core->callBehavior('pluginBeforeDeactivate', $module);
+
+ $this->modules->deactivateModule($id);
+
+ # --BEHAVIOR-- moduleAfterDeactivate
+ $this->core->callBehavior('pluginAfterDeactivate', $module);
+
+ $count++;
+ }
+
+ if ($failed) {
+ dcPage::addWarningNotice(__('Some plugins have not been deactivated.'));
+ } else {
+ dcPage::addSuccessNotice(
+ __('Plugin has been successfully deactivated.', 'Plugins have been successuflly deactivated.', $count)
+ );
+ }
+ http::redirect($this->getURL());
+ } elseif ($this->core->auth->isSuperAdmin() && !empty($_POST['update'])) {
+
+ if (is_array($_POST['update'])) {
+ $modules = array_keys($_POST['update']);
+ }
+
+ $list = $this->store->get(true);
+ if (empty($list)) {
+ throw new Exception(__('No such plugin.'));
+ }
+
+ $count = 0;
+ foreach ($list as $module) {
+
+ if (!in_array($module['id'], $modules)) {
+ continue;
+ }
+
+ if (!self::$allow_multi_install) {
+ $dest = $module['root'] . '/../' . basename($module['file']);
+ } else {
+ $dest = $this->getPath() . '/' . basename($module['file']);
+ if ($module['root'] != $dest) {
+ @file_put_contents($module['root'] . '/_disabled', '');
+ }
+ }
+
+ # --BEHAVIOR-- moduleBeforeUpdate
+ $this->core->callBehavior('pluginBeforeUpdate', $module);
+
+ $this->store->process($module['file'], $dest);
+
+ # --BEHAVIOR-- moduleAfterUpdate
+ $this->core->callBehavior('pluginAfterUpdate', $module);
+
+ $count++;
+ }
+
+ $tab = $count && $count == count($list) ? '#plugins' : '#update';
+
+ dcPage::addSuccessNotice(
+ __('Plugin has been successfully updated.', 'Plugins have been successuflly updated.', $count)
+ );
+ http::redirect($this->getURL() . $tab);
+ }
+
+ # Manual actions
+ elseif (!empty($_POST['upload_pkg']) && !empty($_FILES['pkg_file'])
+ || !empty($_POST['fetch_pkg']) && !empty($_POST['pkg_url'])) {
+ if (empty($_POST['your_pwd']) || !$this->core->auth->checkPassword($_POST['your_pwd'])) {
+ throw new Exception(__('Password verification failed'));
+ }
+
+ if (!empty($_POST['upload_pkg'])) {
+ files::uploadStatus($_FILES['pkg_file']);
+
+ $dest = $this->getPath() . '/' . $_FILES['pkg_file']['name'];
+ if (!move_uploaded_file($_FILES['pkg_file']['tmp_name'], $dest)) {
+ throw new Exception(__('Unable to move uploaded file.'));
+ }
+ } else {
+ $url = urldecode($_POST['pkg_url']);
+ $dest = $this->getPath() . '/' . basename($url);
+ $this->store->download($url, $dest);
+ }
+
+ # --BEHAVIOR-- moduleBeforeAdd
+ $this->core->callBehavior('pluginBeforeAdd', null);
+
+ $ret_code = $this->store->install($dest);
+
+ # --BEHAVIOR-- moduleAfterAdd
+ $this->core->callBehavior('pluginAfterAdd', null);
+
+ dcPage::addSuccessNotice($ret_code == 2 ?
+ __('Plugin has been successfully updated.') :
+ __('Plugin has been successfully installed.')
+ );
+ http::redirect($this->getURL() . '#plugins');
+ } else {
+
+ # --BEHAVIOR-- adminModulesListDoActions
+ $this->core->callBehavior('adminModulesListDoActions', $this, $modules, 'plugin');
+
+ }
+
+ return;
+ }
+
+ /**
+ * Display tab for manual installation.
+ *
+ * @return adminModulesList self instance
+ */
+ public function displayManualForm()
+ {
+ if (!$this->core->auth->isSuperAdmin() || !$this->isWritablePath()) {
+ return;
+ }
+
+ # 'Upload module' form
+ echo
+ '' .
+ '' . __('Upload a zip file') . ' ' .
+ '* ' . __('Zip file path:') . ' ' .
+ '
' .
+ '* ' . __('Your password:') . ' ' .
+ form::password(['your_pwd', 'your_pwd1'], 20, 255,
+ [
+ 'extra_html' => 'required placeholder="' . __('Password') . '"',
+ 'autocomplete' => 'current-password'
+ ]
+ ) . '
' .
+ ' ' .
+ $this->core->formNonce() . '
' .
+ ' ';
+
+ # 'Fetch module' form
+ echo
+ '' .
+ '' . __('Download a zip file') . ' ' .
+ '* ' . __('Zip file URL:') . ' ' .
+ form::field('pkg_url', 40, 255, [
+ 'extra_html' => 'required placeholder="' . __('URL') . '"'
+ ]) .
+ '
' .
+ '* ' . __('Your password:') . ' ' .
+ form::password(['your_pwd', 'your_pwd2'], 20, 255,
+ [
+ 'extra_html' => 'required placeholder="' . __('Password') . '"',
+ 'autocomplete' => 'current-password'
+ ]
+ ) . '
' .
+ ' ' .
+ $this->core->formNonce() . '
' .
+ ' ';
+
+ return $this;
+ }
+ //@}
+
+ /// @name Module configuration methods
+ //@{
+ /**
+ * Prepare module configuration.
+ *
+ * We need to get configuration content in three steps
+ * and out of this class to keep backward compatibility.
+ *
+ * if ($xxx->setConfiguration()) {
+ * include $xxx->includeConfiguration();
+ * }
+ * $xxx->getConfiguration();
+ * ... [put here page headers and other stuff]
+ * $xxx->displayConfiguration();
+ *
+ * @param string $id Module to work on or it gather through REQUEST
+ * @return True if config set
+ */
+ public function setConfiguration($id = null)
+ {
+ if (empty($_REQUEST['conf']) || empty($_REQUEST['module']) && !$id) {
+ return false;
+ }
+
+ if (!empty($_REQUEST['module']) && empty($id)) {
+ $id = $_REQUEST['module'];
+ }
+
+ if (!$this->modules->moduleExists($id)) {
+ $this->core->error->add(__('Unknow plugin ID'));
+ return false;
+ }
+
+ $module = $this->modules->getModules($id);
+ $module = self::sanitizeModule($id, $module);
+ $file = path::real($module['root'] . '/_config.php');
+
+ if (!file_exists($file)) {
+ $this->core->error->add(__('This plugin has no configuration file.'));
+ return false;
+ }
+
+ $this->config_module = $module;
+ $this->config_file = $file;
+ $this->config_content = '';
+
+ if (!defined('DC_CONTEXT_MODULE')) {
+ define('DC_CONTEXT_MODULE', true);
+ }
+
+ return true;
+ }
+
+ /**
+ * Get path of module configuration file.
+ *
+ * @note Required previously set file info
+ * @return Full path of config file or null
+ */
+ public function includeConfiguration()
+ {
+ if (!$this->config_file) {
+ return;
+ }
+ $this->setRedir($this->getURL() . '#plugins');
+
+ ob_start();
+
+ return $this->config_file;
+ }
+
+ /**
+ * Gather module configuration file content.
+ *
+ * @note Required previously file inclusion
+ * @return True if content has been captured
+ */
+ public function getConfiguration()
+ {
+ if ($this->config_file) {
+ $this->config_content = ob_get_contents();
+ }
+
+ ob_end_clean();
+
+ return !empty($this->file_content);
+ }
+
+ /**
+ * Display module configuration form.
+ *
+ * @note Required previously gathered content
+ * @return adminModulesList self instance
+ */
+ public function displayConfiguration()
+ {
+ if ($this->config_file) {
+
+ if (!$this->config_module['standalone_config']) {
+ echo
+ '' .
+ '' . sprintf(__('Configure "%s"'), html::escapeHTML($this->config_module['name'])) . ' ' .
+ '' . __('Back') . '
';
+ }
+
+ echo $this->config_content;
+
+ if (!$this->config_module['standalone_config']) {
+ echo
+ ' ' .
+ form::hidden('module', $this->config_module['id']) .
+ form::hidden('redir', $this->getRedir()) .
+ $this->core->formNonce() . '
' .
+ ' ';
+ }
+ }
+
+ return $this;
+ }
+ //@}
+
+ /**
+ * Helper to sanitize a string.
+ *
+ * Used for search or id.
+ *
+ * @param string $str String to sanitize
+ * @return Sanitized string
+ */
+ public static function sanitizeString($str)
+ {
+ return preg_replace('/[^A-Za-z0-9\@\#+_-]/', '', strtolower($str));
+ }
+}
+
+/**
+ * @ingroup DC_CORE
+ * @brief Helper to manage list of themes.
+ * @since 2.6
+ */
+class adminThemesList extends adminModulesList
+{
+ /**
+ * Constructor.
+ *
+ * Note that this creates dcStore instance.
+ *
+ * @param object $modules dcModules instance
+ * @param string $modules_root Modules root directories
+ * @param string $xml_url URL of modules feed from repository
+ * @param boolean $force Force query repository
+ */
+ public function __construct(dcModules $modules, $modules_root, $xml_url, $force = false)
+ {
+ parent::__construct($modules, $modules_root, $xml_url, $force);
+ $this->page_url = $this->core->adminurl->get('admin.blog.theme');
+ }
+
+ public function displayModules($cols = ['name', 'config', 'version', 'desc'], $actions = [], $nav_limit = false)
+ {
+ echo
+ '' .
+ '';
+
+ $sort_field = $this->getSort();
+
+ # Sort modules by id
+ $modules = $this->getSearch() === null ?
+ self::sortModules($this->data, $sort_field, $this->sort_asc) :
+ $this->data;
+
+ $res = '';
+ $count = 0;
+ foreach ($modules as $id => $module) {
+ # Show only requested modules
+ if ($nav_limit && $this->getSearch() === null) {
+ $char = substr($module[$sort_field], 0, 1);
+ if (!in_array($char, $this->nav_list)) {
+ $char = $this->nav_special;
+ }
+ if ($this->getIndex() != $char) {
+ continue;
+ }
+ }
+
+ $current = $this->core->blog->settings->system->theme == $id && $this->modules->moduleExists($id);
+ $distrib = self::isDistributedModule($id) ? ' dc-box' : '';
+
+ $line =
+ '
';
+
+ if (in_array('name', $cols) && !$current) {
+ $line .=
+ '
';
+
+ if (in_array('checkbox', $cols)) {
+ $line .=
+ '' .
+ form::checkbox(['modules[' . $count . ']', html::escapeHTML($this->list_id) . '_modules_' . html::escapeHTML($id)], html::escapeHTML($id)) .
+ html::escapeHTML($module['name']) .
+ ' ';
+
+ } else {
+ $line .=
+ form::hidden(['modules[' . $count . ']'], html::escapeHTML($id)) .
+ html::escapeHTML($module['name']);
+ }
+
+ $line .=
+ $this->core->formNonce() .
+ ' ';
+ }
+
+ # Display score only for debug purpose
+ if (in_array('score', $cols) && $this->getSearch() !== null && defined('DC_DEBUG') && DC_DEBUG) {
+ $line .=
+ '
' . sprintf(__('Score: %s'), $module['score']) . '
';
+ }
+
+ if (in_array('sshot', $cols)) {
+ # Screenshot from url
+ if (preg_match('#^http(s)?://#', $module['sshot'])) {
+ $sshot = $module['sshot'];
+ }
+ # Screenshot from installed module
+ elseif (file_exists($this->core->blog->themes_path . '/' . $id . '/screenshot.jpg')) {
+ $sshot = $this->getURL('shot=' . rawurlencode($id));
+ }
+ # Default screenshot
+ else {
+ $sshot = 'images/noscreenshot.png';
+ }
+
+ $line .=
+ '
';
+ }
+
+ $line .=
+ '
';
+
+ if (in_array('name', $cols) && $current) {
+ $line .=
+ '
';
+
+ if (in_array('checkbox', $cols)) {
+ $line .=
+ '' .
+ form::checkbox(['modules[' . $count . ']', html::escapeHTML($this->list_id) . '_modules_' . html::escapeHTML($id)], html::escapeHTML($id)) .
+ html::escapeHTML($module['name']) .
+ ' ';
+ } else {
+ $line .=
+ form::hidden(['modules[' . $count . ']'], html::escapeHTML($id)) .
+ html::escapeHTML($module['name']);
+ }
+
+ $line .=
+ ' ';
+ }
+
+ $line .=
+ '
';
+
+ if (in_array('desc', $cols)) {
+ $line .=
+ '' . html::escapeHTML(__($module['desc'])) . ' ';
+ }
+
+ if (in_array('author', $cols)) {
+ $line .=
+ '' . sprintf(__('by %s'), html::escapeHTML($module['author'])) . ' ';
+ }
+
+ if (in_array('version', $cols)) {
+ $line .=
+ '' . sprintf(__('version %s'), html::escapeHTML($module['version'])) . ' ';
+ }
+
+ if (in_array('current_version', $cols)) {
+ $line .=
+ '' . sprintf(__('(current version %s)'), html::escapeHTML($module['current_version'])) . ' ';
+ }
+
+ if (in_array('parent', $cols) && !empty($module['parent'])) {
+ if ($this->modules->moduleExists($module['parent'])) {
+ $line .=
+ '' . sprintf(__('(built on "%s")'), html::escapeHTML($module['parent'])) . ' ';
+ } else {
+ $line .=
+ '' . sprintf(__('(requires "%s")'), html::escapeHTML($module['parent'])) . ' ';
+ }
+ }
+
+ $has_details = in_array('details', $cols) && !empty($module['details']);
+ $has_support = in_array('support', $cols) && !empty($module['support']);
+ if ($has_details || $has_support) {
+ $line .=
+ '';
+
+ if ($has_details) {
+ $line .=
+ '' . __('Details') . ' ';
+ }
+
+ if ($has_support) {
+ $line .=
+ ' - ' . __('Support') . ' ';
+ }
+
+ $line .=
+ ' ';
+ }
+
+ $line .=
+ '
' .
+ '
';
+
+ $line .=
+ '
';
+
+ # Plugins actions
+ if ($current) {
+
+ # _GET actions
+ if (file_exists(path::real($this->core->blog->themes_path . '/' . $id) . '/style.css')) {
+ $theme_url = preg_match('#^http(s)?://#', $this->core->blog->settings->system->themes_url) ?
+ http::concatURL($this->core->blog->settings->system->themes_url, '/' . $id) :
+ http::concatURL($this->core->blog->url, $this->core->blog->settings->system->themes_url . '/' . $id);
+ $line .=
+ '
' . __('View stylesheet') . '
';
+ }
+
+ $line .= '
';
+
+ if (file_exists(path::real($this->core->blog->themes_path . '/' . $id) . '/_config.php')) {
+ $line .=
+ '
' . __('Configure theme') . '
';
+ }
+
+ # --BEHAVIOR-- adminCurrentThemeDetails
+ $line .=
+ $this->core->callBehavior('adminCurrentThemeDetails', $this->core, $id, $module);
+
+ $line .= '
';
+ }
+
+ # _POST actions
+ if (!empty($actions)) {
+ $line .=
+ '
' . implode(' ', $this->getActions($id, $module, $actions)) . '
';
+ }
+
+ $line .=
+ '
';
+
+ $line .=
+ '
';
+
+ $count++;
+
+ $res = $current ? $line . $res : $res . $line;
+ }
+
+ echo
+ $res .
+ '
';
+
+ if (!$count && $this->getSearch() === null) {
+ echo
+ '' . __('No themes matched your search.') . '
';
+ } elseif ((in_array('checkbox', $cols) || $count > 1) && !empty($actions) && $this->core->auth->isSuperAdmin()) {
+ $buttons = $this->getGlobalActions($actions, in_array('checkbox', $cols));
+
+ if (!empty($buttons)) {
+ if (in_array('checkbox', $cols)) {
+ echo
+ '
';
+ }
+ echo '' . implode(' ', $buttons) . '
';
+ }
+ }
+
+ echo
+ ' ';
+
+ return $this;
+ }
+
+ protected function getActions($id, $module, $actions)
+ {
+ $submits = [];
+
+ $this->core->blog->settings->addNamespace('system');
+ if ($id != $this->core->blog->settings->system->theme) {
+
+ # Select theme to use on curent blog
+ if (in_array('select', $actions)) {
+ $submits[] =
+ ' ';
+ }
+ }
+
+ return array_merge(
+ $submits,
+ parent::getActions($id, $module, $actions)
+ );
+ }
+
+ protected function getGlobalActions($actions, $with_selection = false)
+ {
+ $submits = [];
+
+ foreach ($actions as $action) {
+ switch ($action) {
+
+ # Update (from store)
+ case 'update':if ($this->core->auth->isSuperAdmin() && $this->path_writable) {
+ $submits[] =
+ ' ' . $this->core->formNonce();
+ }break;
+
+ # Behavior
+ case 'behavior':
+
+ # --BEHAVIOR-- adminModulesListGetGlobalActions
+ $tmp = $this->core->callBehavior('adminModulesListGetGlobalActions', $this);
+
+ if (!empty($tmp)) {
+ $submits[] = $tmp;
+ }
+ break;
+ }
+ }
+
+ return $submits;
+ }
+
+ public function doActions()
+ {
+ if (empty($_POST) || !empty($_REQUEST['conf'])) {
+ return;
+ }
+
+ $modules = !empty($_POST['modules']) && is_array($_POST['modules']) ? array_values($_POST['modules']) : [];
+
+ if (!empty($_POST['select'])) {
+
+ # Can select only one theme at a time!
+ if (is_array($_POST['select'])) {
+ $modules = array_keys($_POST['select']);
+ $id = $modules[0];
+
+ if (!$this->modules->moduleExists($id)) {
+ throw new Exception(__('No such theme.'));
+ }
+
+ $this->core->blog->settings->addNamespace('system');
+ $this->core->blog->settings->system->put('theme', $id);
+ $this->core->blog->triggerBlog();
+
+ dcPage::addSuccessNotice(__('Theme has been successfully selected.'));
+ http::redirect($this->getURL() . '#themes');
+ }
+ } else {
+ if (!$this->isWritablePath()) {
+ return;
+ }
+
+ if ($this->core->auth->isSuperAdmin() && !empty($_POST['activate'])) {
+
+ if (is_array($_POST['activate'])) {
+ $modules = array_keys($_POST['activate']);
+ }
+
+ $list = $this->modules->getDisabledModules();
+ if (empty($list)) {
+ throw new Exception(__('No such theme.'));
+ }
+
+ $count = 0;
+ foreach ($list as $id => $module) {
+
+ if (!in_array($id, $modules)) {
+ continue;
+ }
+
+ # --BEHAVIOR-- themeBeforeActivate
+ $this->core->callBehavior('themeBeforeActivate', $id);
+
+ $this->modules->activateModule($id);
+
+ # --BEHAVIOR-- themeAfterActivate
+ $this->core->callBehavior('themeAfterActivate', $id);
+
+ $count++;
+ }
+
+ dcPage::addSuccessNotice(
+ __('Theme has been successfully activated.', 'Themes have been successuflly activated.', $count)
+ );
+ http::redirect($this->getURL());
+ } elseif ($this->core->auth->isSuperAdmin() && !empty($_POST['deactivate'])) {
+
+ if (is_array($_POST['deactivate'])) {
+ $modules = array_keys($_POST['deactivate']);
+ }
+
+ $list = $this->modules->getModules();
+ if (empty($list)) {
+ throw new Exception(__('No such theme.'));
+ }
+
+ $failed = false;
+ $count = 0;
+ foreach ($list as $id => $module) {
+
+ if (!in_array($id, $modules)) {
+ continue;
+ }
+
+ if (!$module['root_writable']) {
+ $failed = true;
+ continue;
+ }
+
+ $module[$id] = $id;
+
+ # --BEHAVIOR-- themeBeforeDeactivate
+ $this->core->callBehavior('themeBeforeDeactivate', $module);
+
+ $this->modules->deactivateModule($id);
+
+ # --BEHAVIOR-- themeAfterDeactivate
+ $this->core->callBehavior('themeAfterDeactivate', $module);
+
+ $count++;
+ }
+
+ if ($failed) {
+ dcPage::addWarningNotice(__('Some themes have not been deactivated.'));
+ } else {
+ dcPage::addSuccessNotice(
+ __('Theme has been successfully deactivated.', 'Themes have been successuflly deactivated.', $count)
+ );
+ }
+ http::redirect($this->getURL());
+ } elseif ($this->core->auth->isSuperAdmin() && !empty($_POST['delete'])) {
+
+ if (is_array($_POST['delete'])) {
+ $modules = array_keys($_POST['delete']);
+ }
+
+ $list = $this->modules->getDisabledModules();
+
+ $failed = false;
+ $count = 0;
+ foreach ($modules as $id) {
+ if (!isset($list[$id])) {
+
+ if (!$this->modules->moduleExists($id)) {
+ throw new Exception(__('No such theme.'));
+ }
+
+ $module = $this->modules->getModules($id);
+ $module['id'] = $id;
+
+ if (!$this->isDeletablePath($module['root'])) {
+ $failed = true;
+ continue;
+ }
+
+ # --BEHAVIOR-- themeBeforeDelete
+ $this->core->callBehavior('themeBeforeDelete', $module);
+
+ $this->modules->deleteModule($id);
+
+ # --BEHAVIOR-- themeAfterDelete
+ $this->core->callBehavior('themeAfterDelete', $module);
+ } else {
+ $this->modules->deleteModule($id, true);
+ }
+
+ $count++;
+ }
+
+ if (!$count && $failed) {
+ throw new Exception(__("You don't have permissions to delete this theme."));
+ } elseif ($failed) {
+ dcPage::addWarningNotice(__('Some themes have not been delete.'));
+ } else {
+ dcPage::addSuccessNotice(
+ __('Theme has been successfully deleted.', 'Themes have been successuflly deleted.', $count)
+ );
+ }
+ http::redirect($this->getURL());
+ } elseif ($this->core->auth->isSuperAdmin() && !empty($_POST['install'])) {
+
+ if (is_array($_POST['install'])) {
+ $modules = array_keys($_POST['install']);
+ }
+
+ $list = $this->store->get();
+
+ if (empty($list)) {
+ throw new Exception(__('No such theme.'));
+ }
+
+ $count = 0;
+ foreach ($list as $id => $module) {
+
+ if (!in_array($id, $modules)) {
+ continue;
+ }
+
+ $dest = $this->getPath() . '/' . basename($module['file']);
+
+ # --BEHAVIOR-- themeBeforeAdd
+ $this->core->callBehavior('themeBeforeAdd', $module);
+
+ $this->store->process($module['file'], $dest);
+
+ # --BEHAVIOR-- themeAfterAdd
+ $this->core->callBehavior('themeAfterAdd', $module);
+
+ $count++;
+ }
+
+ dcPage::addSuccessNotice(
+ __('Theme has been successfully installed.', 'Themes have been successuflly installed.', $count)
+ );
+ http::redirect($this->getURL());
+ } elseif ($this->core->auth->isSuperAdmin() && !empty($_POST['update'])) {
+
+ if (is_array($_POST['update'])) {
+ $modules = array_keys($_POST['update']);
+ }
+
+ $list = $this->store->get(true);
+ if (empty($list)) {
+ throw new Exception(__('No such theme.'));
+ }
+
+ $count = 0;
+ foreach ($list as $module) {
+
+ if (!in_array($module['id'], $modules)) {
+ continue;
+ }
+
+ $dest = $module['root'] . '/../' . basename($module['file']);
+
+ # --BEHAVIOR-- themeBeforeUpdate
+ $this->core->callBehavior('themeBeforeUpdate', $module);
+
+ $this->store->process($module['file'], $dest);
+
+ # --BEHAVIOR-- themeAfterUpdate
+ $this->core->callBehavior('themeAfterUpdate', $module);
+
+ $count++;
+ }
+
+ $tab = $count && $count == count($list) ? '#themes' : '#update';
+
+ dcPage::addSuccessNotice(
+ __('Theme has been successfully updated.', 'Themes have been successuflly updated.', $count)
+ );
+ http::redirect($this->getURL() . $tab);
+ }
+
+ # Manual actions
+ elseif (!empty($_POST['upload_pkg']) && !empty($_FILES['pkg_file'])
+ || !empty($_POST['fetch_pkg']) && !empty($_POST['pkg_url'])) {
+ if (empty($_POST['your_pwd']) || !$this->core->auth->checkPassword($_POST['your_pwd'])) {
+ throw new Exception(__('Password verification failed'));
+ }
+
+ if (!empty($_POST['upload_pkg'])) {
+ files::uploadStatus($_FILES['pkg_file']);
+
+ $dest = $this->getPath() . '/' . $_FILES['pkg_file']['name'];
+ if (!move_uploaded_file($_FILES['pkg_file']['tmp_name'], $dest)) {
+ throw new Exception(__('Unable to move uploaded file.'));
+ }
+ } else {
+ $url = urldecode($_POST['pkg_url']);
+ $dest = $this->getPath() . '/' . basename($url);
+ $this->store->download($url, $dest);
+ }
+
+ # --BEHAVIOR-- themeBeforeAdd
+ $this->core->callBehavior('themeBeforeAdd', null);
+
+ $ret_code = $this->store->install($dest);
+
+ # --BEHAVIOR-- themeAfterAdd
+ $this->core->callBehavior('themeAfterAdd', null);
+
+ dcPage::addSuccessNotice($ret_code == 2 ?
+ __('Theme has been successfully updated.') :
+ __('Theme has been successfully installed.')
+ );
+ http::redirect($this->getURL() . '#themes');
+ } else {
+
+ # --BEHAVIOR-- adminModulesListDoActions
+ $this->core->callBehavior('adminModulesListDoActions', $this, $modules, 'theme');
+
+ }
+ }
+
+ return;
+ }
+}
diff --git a/dotclear._no/inc/admin/lib.pager.php b/dotclear._no/inc/admin/lib.pager.php
new file mode 100644
index 0000000..47e8d81
--- /dev/null
+++ b/dotclear._no/inc/admin/lib.pager.php
@@ -0,0 +1,876 @@
+%s ';
+ return sprintf($formatter,
+ $li_class, $href, $img_src, $img_alt, $img_alt);
+ } else {
+ $formatter = ' ';
+ return sprintf($formatter,
+ $li_class, $img_src_nolink, $img_alt, $img_alt);
+ }
+ }
+ public function setURL()
+ {
+ parent::setURL();
+ $url = parse_url($_SERVER['REQUEST_URI']);
+ if (isset($url['query'])) {
+ parse_str($url['query'], $args);
+ } else {
+ $args = [];
+ }
+ # Removing session information
+ if (session_id()) {
+ if (isset($args[session_name()])) {
+ unset($args[session_name()]);
+ }
+
+ }
+ if (isset($args[$this->var_page])) {
+ unset($args[$this->var_page]);
+ }
+ if (isset($args['ok'])) {
+ unset($args['ok']);
+ }
+
+ $this->form_hidden = '';
+ foreach ($args as $k => $v) {
+ // Check parameter key (will prevent some forms of XSS)
+ if ($k === preg_replace('`[^A-Za-z0-9_-]`', '', $k)) {
+ if (is_array($v)) {
+ foreach ($v as $k2 => $v2) {
+ $this->form_hidden .= form::hidden([$k . '[]'], html::escapeHTML($v2));
+ }
+ } else {
+ $this->form_hidden .= form::hidden([$k], html::escapeHTML($v));
+ }
+ }
+ }
+ $this->form_action = $url['path'];
+ }
+
+ /**
+ * Pager Links
+ *
+ * Returns pager links
+ *
+ * @return string
+ */
+ public function getLinks()
+ {
+ $this->setURL();
+ $htmlFirst = $this->getLink(
+ "first",
+ sprintf($this->page_url, 1),
+ "images/pagination/first.png",
+ "images/pagination/no-first.png",
+ __('First page'),
+ ($this->env > 1)
+ );
+ $htmlPrev = $this->getLink(
+ "prev",
+ sprintf($this->page_url, $this->env - 1),
+ "images/pagination/previous.png",
+ "images/pagination/no-previous.png",
+ __('Previous page'),
+ ($this->env > 1)
+ );
+ $htmlNext = $this->getLink(
+ "next",
+ sprintf($this->page_url, $this->env + 1),
+ "images/pagination/next.png",
+ "images/pagination/no-next.png",
+ __('Next page'),
+ ($this->env < $this->nb_pages)
+ );
+ $htmlLast = $this->getLink(
+ "last",
+ sprintf($this->page_url, $this->nb_pages),
+ "images/pagination/last.png",
+ "images/pagination/no-last.png",
+ __('Last page'),
+ ($this->env < $this->nb_pages)
+ );
+ $htmlCurrent =
+ '' .
+ sprintf(__('Page %s / %s'), $this->env, $this->nb_pages) .
+ ' ';
+
+ $htmlDirect =
+ ($this->nb_pages > 1 ?
+ sprintf('' . __('Direct access page %s'),
+ form::number([$this->var_page], 1, $this->nb_pages)) .
+ ' ' . $this->form_hidden . ' ' : '');
+
+ $res =
+ '' .
+ '' .
+ ' '
+ ;
+
+ return $this->nb_elements > 0 ? $res : '';
+ }
+}
+
+class adminGenericList
+{
+ protected $core;
+ protected $rs;
+ protected $rs_count;
+
+ public function __construct($core, $rs, $rs_count)
+ {
+ $this->core = &$core;
+ $this->rs = &$rs;
+ $this->rs_count = $rs_count;
+ $this->html_prev = __('« prev.');
+ $this->html_next = __('next »');
+ }
+
+ public function userColumns($type, $cols)
+ {
+ $cols_user = @$this->core->auth->user_prefs->interface->cols;
+ if (is_array($cols_user)) {
+ if (isset($cols_user[$type])) {
+ foreach ($cols_user[$type] as $cn => $cd) {
+ if (!$cd && isset($cols[$cn])) {
+ unset($cols[$cn]);
+ }
+ }
+ }
+ }
+ }
+}
+
+class adminPostList extends adminGenericList
+{
+ public function display($page, $nb_per_page, $enclose_block = '', $filter = false)
+ {
+ if ($this->rs->isEmpty()) {
+ if ($filter) {
+ echo '' . __('No entry matches the filter') . '
';
+ } else {
+ echo '' . __('No entry') . '
';
+ }
+ } else {
+ $pager = new dcPager($page, $this->rs_count, $nb_per_page, 10);
+ $entries = [];
+ if (isset($_REQUEST['entries'])) {
+ foreach ($_REQUEST['entries'] as $v) {
+ $entries[(integer) $v] = true;
+ }
+ }
+ $html_block =
+ '' .
+ '
';
+
+ if ($filter) {
+ $html_block .= '' . sprintf(__('List of %s entries matching the filter.'), $this->rs_count) . ' ';
+ } else {
+ $nb_published = $this->core->blog->getPosts(['post_status' => 1], true)->f(0);
+ $nb_pending = $this->core->blog->getPosts(['post_status' => -2], true)->f(0);
+ $nb_programmed = $this->core->blog->getPosts(['post_status' => -1], true)->f(0);
+ $nb_unpublished = $this->core->blog->getPosts(['post_status' => 0], true)->f(0);
+ $html_block .= '' .
+ sprintf(__('List of entries (%s)'), $this->rs_count) .
+ ($nb_published ?
+ sprintf(
+ __(', published (1)', ', published (%s)', $nb_published),
+ $this->core->adminurl->get('admin.posts', ['status' => 1]),
+ $nb_published) : '') .
+ ($nb_pending ?
+ sprintf(
+ __(', pending (1)', ', pending (%s)', $nb_pending),
+ $this->core->adminurl->get('admin.posts', ['status' => -2]),
+ $nb_pending) : '') .
+ ($nb_programmed ?
+ sprintf(__(', programmed (1)', ', programmed (%s)', $nb_programmed),
+ $this->core->adminurl->get('admin.posts', ['status' => -1]),
+ $nb_programmed) : '') .
+ ($nb_unpublished ?
+ sprintf(__(', unpublished (1)', ', unpublished (%s)', $nb_unpublished),
+ $this->core->adminurl->get('admin.posts', ['status' => 0]),
+ $nb_unpublished) : '') .
+ ' ';
+ }
+
+ $cols = [
+ 'title' => '' . __('Title') . ' ',
+ 'date' => '' . __('Date') . ' ',
+ 'category' => '' . __('Category') . ' ',
+ 'author' => '' . __('Author') . ' ',
+ 'comments' => '' . __('Comments') . ' ',
+ 'trackbacks' => '' . __('Trackbacks') . ' ',
+ 'status' => '' . __('Status') . ' '
+ ];
+ $cols = new ArrayObject($cols);
+ $this->core->callBehavior('adminPostListHeader', $this->core, $this->rs, $cols);
+
+ // Cope with optional columns
+ $this->userColumns('posts', $cols);
+
+ $html_block .= '' . implode(iterator_to_array($cols)) . ' %s
';
+ if ($enclose_block) {
+ $html_block = sprintf($enclose_block, $html_block);
+ }
+
+ echo $pager->getLinks();
+
+ $blocks = explode('%s', $html_block);
+
+ echo $blocks[0];
+
+ while ($this->rs->fetch()) {
+ echo $this->postLine(isset($entries[$this->rs->post_id]));
+ }
+
+ echo $blocks[1];
+
+ echo $pager->getLinks();
+ }
+ }
+
+ private function postLine($checked)
+ {
+ if ($this->core->auth->check('categories', $this->core->blog->id)) {
+ $cat_link = '%s ';
+ } else {
+ $cat_link = '%2$s';
+ }
+
+ if ($this->rs->cat_title) {
+ $cat_title = sprintf($cat_link, $this->rs->cat_id,
+ html::escapeHTML($this->rs->cat_title));
+ } else {
+ $cat_title = __('(No cat)');
+ }
+
+ $img = ' ';
+ $sts_class = '';
+ switch ($this->rs->post_status) {
+ case 1:
+ $img_status = sprintf($img, __('Published'), 'check-on.png', 'published');
+ $sts_class = 'sts-online';
+ break;
+ case 0:
+ $img_status = sprintf($img, __('Unpublished'), 'check-off.png', 'unpublished');
+ $sts_class = 'sts-offline';
+ break;
+ case -1:
+ $img_status = sprintf($img, __('Scheduled'), 'scheduled.png', 'scheduled');
+ $sts_class = 'sts-scheduled';
+ break;
+ case -2:
+ $img_status = sprintf($img, __('Pending'), 'check-wrn.png', 'pending');
+ $sts_class = 'sts-pending';
+ break;
+ }
+
+ $protected = '';
+ if ($this->rs->post_password) {
+ $protected = sprintf($img, __('Protected'), 'locker.png', 'locked');
+ }
+
+ $selected = '';
+ if ($this->rs->post_selected) {
+ $selected = sprintf($img, __('Selected'), 'selected.png', 'selected');
+ }
+
+ $attach = '';
+ $nb_media = $this->rs->countMedia();
+ if ($nb_media > 0) {
+ $attach_str = $nb_media == 1 ? __('%d attachment') : __('%d attachments');
+ $attach = sprintf($img, sprintf($attach_str, $nb_media), 'attach.png', 'attach');
+ }
+
+ $res = '';
+
+ $cols = [
+ 'check' => '' .
+ form::checkbox(['entries[]'], $this->rs->post_id,
+ [
+ 'checked' => $checked,
+ 'disabled' => !$this->rs->isEditable()
+ ]) .
+ ' ',
+ 'title' => '' .
+ html::escapeHTML($this->rs->post_title) . ' ',
+ 'date' => '' . dt::dt2str(__('%Y-%m-%d %H:%M'), $this->rs->post_dt) . ' ',
+ 'category' => '' . $cat_title . ' ',
+ 'author' => '' . html::escapeHTML($this->rs->user_id) . ' ',
+ 'comments' => '' . $this->rs->nb_comment . ' ',
+ 'trackbacks' => '' . $this->rs->nb_trackback . ' ',
+ 'status' => '' . $img_status . ' ' . $selected . ' ' . $protected . ' ' . $attach . ' '
+ ];
+ $cols = new ArrayObject($cols);
+ $this->core->callBehavior('adminPostListValue', $this->core, $this->rs, $cols);
+
+ // Cope with optional columns
+ $this->userColumns('posts', $cols);
+
+ $res .= implode(iterator_to_array($cols));
+ $res .= ' ';
+
+ return $res;
+ }
+}
+
+class adminPostMiniList extends adminGenericList
+{
+ public function display($page, $nb_per_page, $enclose_block = '')
+ {
+ if ($this->rs->isEmpty()) {
+ echo '' . __('No entry') . '
';
+ } else {
+ $pager = new dcPager($page, $this->rs_count, $nb_per_page, 10);
+
+ $html_block =
+ '' .
+ '
' . __('Entries list') . ' ';
+
+ $cols = [
+ 'title' => '' . __('Title') . ' ',
+ 'date' => '' . __('Date') . ' ',
+ 'author' => '' . __('Author') . ' ',
+ 'status' => '' . __('Status') . ' '
+ ];
+
+ $cols = new ArrayObject($cols);
+ $this->core->callBehavior('adminPostMiniListHeader', $this->core, $this->rs, $cols);
+
+ // Cope with optional columns
+ $this->userColumns('posts', $cols);
+
+ $html_block .= ' ' . implode(iterator_to_array($cols)) . ' %s
';
+ if ($enclose_block) {
+ $html_block = sprintf($enclose_block, $html_block);
+ }
+
+ echo $pager->getLinks();
+
+ $blocks = explode('%s', $html_block);
+
+ echo $blocks[0];
+
+ while ($this->rs->fetch()) {
+ echo $this->postLine();
+ }
+
+ echo $blocks[1];
+
+ echo $pager->getLinks();
+ }
+ }
+
+ private function postLine()
+ {
+ $img = ' ';
+ $sts_class = '';
+ switch ($this->rs->post_status) {
+ case 1:
+ $img_status = sprintf($img, __('Published'), 'check-on.png');
+ $sts_class = 'sts-online';
+ break;
+ case 0:
+ $img_status = sprintf($img, __('Unpublished'), 'check-off.png');
+ $sts_class = 'sts-offline';
+ break;
+ case -1:
+ $img_status = sprintf($img, __('Scheduled'), 'scheduled.png');
+ $sts_class = 'sts-scheduled';
+ break;
+ case -2:
+ $img_status = sprintf($img, __('Pending'), 'check-wrn.png');
+ $sts_class = 'sts-pending';
+ break;
+ }
+
+ $protected = '';
+ if ($this->rs->post_password) {
+ $protected = sprintf($img, __('Protected'), 'locker.png');
+ }
+
+ $selected = '';
+ if ($this->rs->post_selected) {
+ $selected = sprintf($img, __('Selected'), 'selected.png');
+ }
+
+ $attach = '';
+ $nb_media = $this->rs->countMedia();
+ if ($nb_media > 0) {
+ $attach_str = $nb_media == 1 ? __('%d attachment') : __('%d attachments');
+ $attach = sprintf($img, sprintf($attach_str, $nb_media), 'attach.png');
+ }
+
+ $res = '';
+
+ $cols = [
+ 'title' => 'rs->getURL()) . '">' .
+ html::escapeHTML($this->rs->post_title) . ' ',
+ 'date' => '' . dt::dt2str(__('%Y-%m-%d %H:%M'), $this->rs->post_dt) . ' ',
+ 'author' => '' . html::escapeHTML($this->rs->user_id) . ' ',
+ 'status' => '' . $img_status . ' ' . $selected . ' ' . $protected . ' ' . $attach . ' '
+ ];
+
+ $cols = new ArrayObject($cols);
+ $this->core->callBehavior('adminPostMiniListValue', $this->core, $this->rs, $cols);
+
+ // Cope with optional columns
+ $this->userColumns('posts', $cols);
+
+ $res .= implode(iterator_to_array($cols));
+ $res .= ' ';
+
+ return $res;
+ }
+}
+
+class adminCommentList extends adminGenericList
+{
+ public function display($page, $nb_per_page, $enclose_block = '', $filter = false, $spam = false)
+ {
+ if ($this->rs->isEmpty()) {
+ if ($filter) {
+ echo '' . __('No comments or trackbacks matches the filter') . '
';
+ } else {
+ echo '' . __('No comments') . '
';
+ }
+ } else {
+ // Get antispam filters' name
+ $filters = [];
+ if ($spam) {
+ if (class_exists('dcAntispam')) {
+ dcAntispam::initFilters();
+ $fs = dcAntispam::$filters->getFilters();
+ foreach ($fs as $fid => $f) {
+ $filters[$fid] = $f->name;
+ }
+ }
+ }
+
+ $pager = new dcPager($page, $this->rs_count, $nb_per_page, 10);
+
+ $comments = [];
+ if (isset($_REQUEST['comments'])) {
+ foreach ($_REQUEST['comments'] as $v) {
+ $comments[(integer) $v] = true;
+ }
+ }
+ $html_block =
+ '' .
+ '
';
+
+ if ($filter) {
+ $html_block .= '' .
+ sprintf(__(
+ 'Comment or trackback matching the filter.',
+ 'List of %s comments or trackbacks matching the filter.',
+ $this->rs_count), $this->rs_count) .
+ ' ';
+ } else {
+ $nb_published = $this->core->blog->getComments(['comment_status' => 1], true)->f(0);
+ $nb_spam = $this->core->blog->getComments(['comment_status' => -2], true)->f(0);
+ $nb_pending = $this->core->blog->getComments(['comment_status' => -1], true)->f(0);
+ $nb_unpublished = $this->core->blog->getComments(['comment_status' => 0], true)->f(0);
+ $html_block .= '' .
+ sprintf(__('List of comments and trackbacks (%s)'), $this->rs_count) .
+ ($nb_published ?
+ sprintf(
+ __(', published (1)', ', published (%s)', $nb_published),
+ $this->core->adminurl->get('admin.comments', ['status' => 1]),
+ $nb_published) : '') .
+ ($nb_spam ?
+ sprintf(
+ __(', spam (1)', ', spam (%s)', $nb_spam),
+ $this->core->adminurl->get('admin.comments', ['status' => -2]),
+ $nb_spam) : '') .
+ ($nb_pending ?
+ sprintf(__(', pending (1)', ', pending (%s)', $nb_pending),
+ $this->core->adminurl->get('admin.comments', ['status' => -1]),
+ $nb_pending) : '') .
+ ($nb_unpublished ?
+ sprintf(__(', unpublished (1)', ', unpublished (%s)', $nb_unpublished),
+ $this->core->adminurl->get('admin.comments', ['status' => 0]),
+ $nb_unpublished) : '') .
+ ' ';
+ }
+
+ $cols = [
+ 'type' => '' . __('Type') . ' ',
+ 'author' => '' . __('Author') . ' ',
+ 'date' => '' . __('Date') . ' ',
+ 'status' => '' . __('Status') . ' '
+ ];
+ if ($spam) {
+ $cols['ip'] = '' . __('IP') . ' ';
+ $cols['spam_filter'] = '' . __('Spam filter') . ' ';
+ }
+ $cols['entry'] = '' . __('Entry') . ' ';
+
+ $cols = new ArrayObject($cols);
+ $this->core->callBehavior('adminCommentListHeader', $this->core, $this->rs, $cols);
+
+ $html_block .= '' . implode(iterator_to_array($cols)) . ' %s
';
+
+ if ($enclose_block) {
+ $html_block = sprintf($enclose_block, $html_block);
+ }
+
+ echo $pager->getLinks();
+
+ $blocks = explode('%s', $html_block);
+
+ echo $blocks[0];
+
+ while ($this->rs->fetch()) {
+ echo $this->commentLine(isset($comments[$this->rs->comment_id]), $spam, $filters);
+ }
+
+ echo $blocks[1];
+
+ echo $pager->getLinks();
+ }
+ }
+
+ private function commentLine($checked = false, $spam = false, $filters = [])
+ {
+ global $core, $author, $status, $sortby, $order, $nb_per_page;
+
+ $author_url =
+ $this->core->adminurl->get('admin.comments', [
+ 'n' => $nb_per_page,
+ 'status' => $status,
+ 'sortby' => $sortby,
+ 'order' => $order,
+ 'author' => $this->rs->comment_author
+ ]);
+
+ $post_url = $this->core->getPostAdminURL($this->rs->post_type, $this->rs->post_id);
+
+ $comment_url = $this->core->adminurl->get('admin.comment', ['id' => $this->rs->comment_id]);
+
+ $comment_dt =
+ dt::dt2str($this->core->blog->settings->system->date_format . ' - ' .
+ $this->core->blog->settings->system->time_format, $this->rs->comment_dt);
+
+ $img = ' ';
+ $sts_class = '';
+ switch ($this->rs->comment_status) {
+ case 1:
+ $img_status = sprintf($img, __('Published'), 'check-on.png');
+ $sts_class = 'sts-online';
+ break;
+ case 0:
+ $img_status = sprintf($img, __('Unpublished'), 'check-off.png');
+ $sts_class = 'sts-offline';
+ break;
+ case -1:
+ $img_status = sprintf($img, __('Pending'), 'check-wrn.png');
+ $sts_class = 'sts-pending';
+ break;
+ case -2:
+ $img_status = sprintf($img, __('Junk'), 'junk.png');
+ $sts_class = 'sts-junk';
+ break;
+ }
+
+ $post_title = html::escapeHTML($this->rs->post_title);
+ if (mb_strlen($post_title) > 70) {
+ $post_title = mb_strcut($post_title, 0, 67) . '...';
+ }
+ $comment_title = sprintf(__('Edit the %1$s from %2$s'),
+ $this->rs->comment_trackback ? __('trackback') : __('comment'),
+ html::escapeHTML($this->rs->comment_author));
+
+ $res = '';
+
+ return $res;
+ }
+}
+
+class adminBlogList extends adminGenericList
+{
+ public function display($page, $nb_per_page, $enclose_block = '', $filter = false)
+ {
+ if ($this->rs->isEmpty()) {
+ if ($filter) {
+ echo '' . __('No blog matches the filter') . '
';
+ } else {
+ echo '' . __('No blog') . '
';
+ }
+ } else {
+ $blogs = [];
+ if (isset($_REQUEST['blogs'])) {
+ foreach ($_REQUEST['blogs'] as $v) {
+ $blogs[$v] = true;
+ }
+ }
+
+ $pager = new dcPager($page, $this->rs_count, $nb_per_page, 10);
+
+ $cols = [
+ 'blog' => 'core->auth->isSuperAdmin() ? ' colspan="2"' : '') .
+ ' scope="col" abbr="comm" class="first nowrap">' . __('Blog id') . ' ',
+ 'name' => '' . __('Blog name') . ' ',
+ 'url' => '' . __('URL') . ' ',
+ 'posts' => '' . __('Entries (all types)') . ' ',
+ 'upddt' => '' . __('Last update') . ' ',
+ 'status' => '' . __('Status') . ' '
+ ];
+
+ $cols = new ArrayObject($cols);
+ $this->core->callBehavior('adminBlogListHeader', $this->core, $this->rs, $cols);
+
+ $html_block =
+ '' .
+ ($filter ?
+ '' .
+ sprintf(__('%d blog matches the filter.', '%d blogs match the filter.', $this->rs_count), $this->rs_count) .
+ ' '
+ :
+ '' . __('Blogs list') . ' '
+ ) .
+ '' . implode(iterator_to_array($cols)) . ' %s
';
+
+ if ($enclose_block) {
+ $html_block = sprintf($enclose_block, $html_block);
+ }
+
+ $blocks = explode('%s', $html_block);
+
+ echo $pager->getLinks();
+
+ echo $blocks[0];
+
+ while ($this->rs->fetch()) {
+ echo $this->blogLine(isset($blogs[$this->rs->blog_id]));
+ }
+
+ echo $blocks[1];
+
+ echo $pager->getLinks();
+ }
+ }
+
+ private function blogLine($checked = false)
+ {
+ $blog_id = html::escapeHTML($this->rs->blog_id);
+
+ $cols = [
+ 'check' =>
+ ($this->core->auth->isSuperAdmin() ?
+ '' .
+ form::checkbox(['blogs[]'], $this->rs->blog_id, $checked) .
+ ' ' : ''),
+ 'blog' =>
+ '' .
+ ($this->core->auth->isSuperAdmin() ?
+ ' $blog_id]) . '" ' .
+ 'title="' . sprintf(__('Edit blog settings for %s'), $blog_id) . '">' .
+ ' ' . $blog_id . ' ' :
+ $blog_id . ' ') .
+ ' ',
+ 'name' =>
+ '' .
+ ' $this->rs->blog_id]) . '" ' .
+ 'title="' . sprintf(__('Switch to blog %s'), $this->rs->blog_id) . '">' .
+ html::escapeHTML($this->rs->blog_name) . ' ' .
+ ' ',
+ 'url' =>
+ '' .
+ '' . html::escapeHTML($this->rs->blog_url) .
+ ' ',
+ 'posts' =>
+ '' .
+ $this->core->countBlogPosts($this->rs->blog_id) .
+ ' ',
+ 'upddt' =>
+ '' .
+ dt::str(__('%Y-%m-%d %H:%M'), strtotime($this->rs->blog_upddt) + dt::getTimeOffset($this->core->auth->getInfo('user_tz'))) .
+ ' ',
+ 'status' =>
+ '' .
+ sprintf(
+ ' ',
+ ($this->rs->blog_status == 1 ? 'check-on' : ($this->rs->blog_status == 0 ? 'check-off' : 'check-wrn')),
+ $this->core->getBlogStatus($this->rs->blog_status)
+ ) .
+ ' '
+ ];
+
+ $cols = new ArrayObject($cols);
+ $this->core->callBehavior('adminBlogListValue', $this->core, $this->rs, $cols);
+
+ return
+ '' .
+ implode(iterator_to_array($cols)) .
+ ' ';
+ }
+}
+
+class adminUserList extends adminGenericList
+{
+ public function display($page, $nb_per_page, $enclose_block = '', $filter = false)
+ {
+ if ($this->rs->isEmpty()) {
+ if ($filter) {
+ echo '' . __('No user matches the filter') . '
';
+ } else {
+ echo '' . __('No user') . '
';
+ }
+ } else {
+ $pager = new dcPager($page, $this->rs_count, $nb_per_page, 10);
+
+ $html_block =
+ '' .
+ '
';
+
+ if ($filter) {
+ $html_block .= '' . sprintf(__('List of %s users match the filter.'), $this->rs_count) . ' ';
+ } else {
+ $html_block .= '' . __('Users list') . ' ';
+ }
+
+ $cols = [
+ 'username' => '' . __('Username') . ' ',
+ 'first_name' => '' . __('First Name') . ' ',
+ 'last_name' => '' . __('Last Name') . ' ',
+ 'display_name' => '' . __('Display name') . ' ',
+ 'entries' => '' . __('Entries (all types)') . ' '
+ ];
+
+ $cols = new ArrayObject($cols);
+ $this->core->callBehavior('adminUserListHeader', $this->core, $this->rs, $cols);
+
+ $html_block .= '' . implode(iterator_to_array($cols)) . ' %s
';
+ if ($enclose_block) {
+ $html_block = sprintf($enclose_block, $html_block);
+ }
+
+ echo $pager->getLinks();
+
+ $blocks = explode('%s', $html_block);
+
+ echo $blocks[0];
+
+ while ($this->rs->fetch()) {
+ echo $this->userLine();
+ }
+
+ echo $blocks[1];
+
+ echo $pager->getLinks();
+ }
+ }
+
+ private function userLine()
+ {
+ $img = ' ';
+ $img_status = '';
+
+ $p = $this->core->getUserPermissions($this->rs->user_id);
+
+ if (isset($p[$this->core->blog->id]['p']['admin'])) {
+ $img_status = sprintf($img, __('admin'), 'admin.png');
+ }
+ if ($this->rs->user_super) {
+ $img_status = sprintf($img, __('superadmin'), 'superadmin.png');
+ }
+
+ $res = '';
+
+ $cols = [
+ 'check' => '' . form::hidden(['nb_post[]'], (integer) $this->rs->nb_post) .
+ form::checkbox(['users[]'], $this->rs->user_id) . ' ',
+ 'username' => '' .
+ $this->rs->user_id . ' ' . $img_status . ' ',
+ 'first_name' => '' . html::escapeHTML($this->rs->user_firstname) . ' ',
+ 'last_name' => '' . html::escapeHTML($this->rs->user_name) . ' ',
+ 'display_name' => '' . html::escapeHTML($this->rs->user_displayname) . ' ',
+ 'entries' => '' .
+ $this->rs->nb_post . ' '
+ ];
+
+ $cols = new ArrayObject($cols);
+ $this->core->callBehavior('adminUserListValue', $this->core, $this->rs, $cols);
+
+ $res .= implode(iterator_to_array($cols));
+ $res .= ' ';
+
+ return $res;
+ }
+}
diff --git a/dotclear._no/inc/admin/lib.themeconfig.php b/dotclear._no/inc/admin/lib.themeconfig.php
new file mode 100644
index 0000000..f93d90e
--- /dev/null
+++ b/dotclear._no/inc/admin/lib.themeconfig.php
@@ -0,0 +1,490 @@
+ $l2) {
+ $ratio = ($l1 + 0.05) / ($l2 + 0.05);
+ } else {
+ $ratio = ($l2 + 0.05) / ($l1 + 0.05);
+ }
+ return $ratio;
+ }
+
+/**
+ * Compute WCAG contrast ration level
+ *
+ * @param float $ratio computed ratio between foreground and backround color
+ * @param string $size font size as defined in CSS
+ * @param boolean $bold true if bold font
+ *
+ * @return string WCAG contrast ratio level (AAA, AA or )
+ */
+ public static function contrastRatioLevel($ratio, $size, $bold = false)
+ {
+ if ($size == '') {
+ return '';
+ }
+
+ // Eval font size in em (assume base font size in pixels equal to 16)
+ if (preg_match('/^([0-9.]+)\s*(%|pt|px|em|ex|rem)?$/', $size, $m)) {
+ if (empty($m[2])) {
+ $m[2] = 'em';
+ }
+ } else {
+ return '';
+ }
+ switch ($m[2]) {
+ case '%':
+ $s = (float) $m[1] / 100;
+ break;
+ case 'pt':
+ $s = (float) $m[1] / 12;
+ break;
+ case 'px':
+ $s = (float) $m[1] / 16;
+ break;
+ case 'em':
+ $s = (float) $m[1];
+ break;
+ case 'ex':
+ $s = (float) $m[1] / 2;
+ break;
+ default:
+ return '';
+ }
+
+ $large = ((($s > 1.5) && ($bold == false)) || (($s > 1.2) && ($bold == true)));
+
+ // Check ratio
+ if ($ratio > 7) {
+ return 'AAA';
+ } elseif (($ratio > 4.5) && $large) {
+ return 'AAA';
+ } elseif ($ratio > 4.5) {
+ return 'AA';
+ } elseif (($ratio > 3) && $large) {
+ return 'AA';
+ }
+ return '';
+ }
+
+/**
+ * Return full information about constrat ratio
+ *
+ * @param string $color text color
+ * @param string $background background color
+ * @param string $size font size
+ * @param boolean $bold bold font
+ *
+ * @return string contrast ratio including WCAG level
+ */
+ public static function contrastRatio($color, $background, $size = '', $bold = false)
+ {
+ if (($color != '') && ($background != '')) {
+ $ratio = self::computeContrastRatio($color, $background);
+ $level = self::contrastRatioLevel($ratio, $size, $bold);
+ return
+ sprintf(__('ratio %.1f'), $ratio) .
+ ($level != '' ? ' ' . sprintf(__('(%s)'), $level) : '');
+ }
+ return '';
+ }
+
+/**
+ * Check font size
+ *
+ * @param string $s font size
+ *
+ * @return string checked font size
+ */
+ public static function adjustFontSize($s)
+ {
+ if (preg_match('/^([0-9.]+)\s*(%|pt|px|em|ex|rem)?$/', $s, $m)) {
+ if (empty($m[2])) {
+ $m[2] = 'em';
+ }
+ return $m[1] . $m[2];
+ }
+ return;
+ }
+
+/**
+ * Check object position, should be x:y
+ *
+ * @param string $p position
+ *
+ * @return string checked position
+ */
+ public static function adjustPosition($p)
+ {
+ if (!preg_match('/^[0-9]+(:[0-9]+)?$/', $p)) {
+ return;
+ }
+ $p = explode(':', $p);
+
+ return $p[0] . (count($p) == 1 ? ':0' : ':' . $p[1]);
+ }
+
+/**
+ * Check a CSS color
+ *
+ * @param string $c CSS color
+ *
+ * @return string checked CSS color
+ */
+ public static function adjustColor($c)
+ {
+ if ($c === '') {
+ return '';
+ }
+
+ $c = strtoupper($c);
+
+ if (preg_match('/^[A-F0-9]{3,6}$/', $c)) {
+ $c = '#' . $c;
+ }
+ if (preg_match('/^#[A-F0-9]{6}$/', $c)) {
+ return $c;
+ }
+ if (preg_match('/^#[A-F0-9]{3,}$/', $c)) {
+ return '#' . substr($c, 1, 1) . substr($c, 1, 1) . substr($c, 2, 1) . substr($c, 2, 1) . substr($c, 3, 1) . substr($c, 3, 1);
+ }
+
+ return '';
+ }
+
+/**
+ * Check and clean CSS
+ *
+ * @param string $css CSS to be checked
+ *
+ * @return string checked CSS
+ */
+ public static function cleanCSS($css)
+ {
+ // TODO ?
+ return $css;
+ }
+
+/**
+ * Return real path of a user defined CSS
+ *
+ * @param string $folder CSS folder
+ *
+ * @return string real path of CSS
+ */
+ public static function cssPath($folder)
+ {
+ global $core;
+ return path::real($core->blog->public_path) . '/' . $folder;
+ }
+
+/**
+ * Retirn URL of a user defined CSS
+ *
+ * @param string $folder CSS folder
+ *
+ * @return string CSS URL
+ */
+ public static function cssURL($folder)
+ {
+ global $core;
+ return $core->blog->settings->system->public_url . '/' . $folder;
+ }
+
+/**
+ * Check if user defined CSS may be written
+ *
+ * @param string $folder CSS folder
+ * @param boolean $create create CSS folder if necessary
+ *
+ * @return boolean true if CSS folder exists and may be written, else false
+ */
+ public static function canWriteCss($folder, $create = false)
+ {
+ global $core;
+
+ $public = path::real($core->blog->public_path);
+ $css = self::cssPath($folder);
+
+ if (!is_dir($public)) {
+ $core->error->add(__('The \'public\' directory does not exist.'));
+ return false;
+ }
+
+ if (!is_dir($css)) {
+ if (!is_writable($public)) {
+ $core->error->add(sprintf(__('The \'%s\' directory cannot be modified.'), 'public'));
+ return false;
+ }
+ if ($create) {
+ files::makeDir($css);
+ }
+ return true;
+ }
+
+ if (!is_writable($css)) {
+ $core->error->add(sprintf(__('The \'%s\' directory cannot be modified.'), 'public/' . $folder));
+ return false;
+ }
+
+ return true;
+ }
+
+/**
+ * Store CSS property value in associated array
+ *
+ * @param array $css CSS associated array
+ * @param string $selector selector
+ * @param string $prop property
+ * @param string $value value
+ */
+ public static function prop(&$css, $selector, $prop, $value)
+ {
+ if ($value) {
+ $css[$selector][$prop] = $value;
+ }
+ }
+
+/**
+ * Store background image property in CSS associated array
+ *
+ * @param string $folder image folder
+ * @param array $css CSS associated array
+ * @param string $selector selector
+ * @param boolean $value false for default, true if image should be set
+ * @param string $image image filename
+ */
+ public static function backgroundImg($folder, &$css, $selector, $value, $image)
+ {
+ $file = self::imagesPath($folder) . '/' . $image;
+ if ($value && file_exists($file)) {
+ $css[$selector]['background-image'] = 'url(' . self::imagesURL($folder) . '/' . $image . ')';
+ }
+ }
+
+/**
+ * Write CSS file
+ *
+ * @param string $folder CSS folder
+ * @param string $theme CSS filename
+ * @param string $css CSS file content
+ */
+ public static function writeCss($folder, $theme, $css)
+ {
+ file_put_contents(self::cssPath($folder) . '/' . $theme . '.css', $css);
+ }
+
+/**
+ * Delete CSS file
+ *
+ * @param string $folder CSS folder
+ * @param string $theme CSS filename to be removed
+ */
+ public static function dropCss($folder, $theme)
+ {
+ $file = path::real(self::cssPath($folder) . '/' . $theme . '.css');
+ if (is_writable(dirname($file))) {
+ @unlink($file);
+ }
+ }
+
+/**
+ * Return public URL of user defined CSS
+ *
+ * @param string $folder CSS folder
+ *
+ * @return string CSS file URL
+ */
+ public static function publicCssUrlHelper($folder)
+ {
+ $theme = $GLOBALS['core']->blog->settings->system->theme;
+ $url = self::cssURL($folder);
+ $path = self::cssPath($folder);
+
+ if (file_exists($path . '/' . $theme . '.css')) {
+ return $url . '/' . $theme . '.css';
+ }
+
+ return;
+ }
+
+/**
+ * Return real path of folder images
+ *
+ * @param string $folder images folder
+ *
+ * @return string real path of folder
+ */
+ public static function imagesPath($folder)
+ {
+ global $core;
+ return path::real($core->blog->public_path) . '/' . $folder;
+ }
+
+/**
+ * Return URL of images folder
+ *
+ * @param string $folder images folder
+ *
+ * @return string URL of images folder
+ */
+ public static function imagesURL($folder)
+ {
+ global $core;
+ return $core->blog->settings->system->public_url . '/' . $folder;
+ }
+
+/**
+ * Check if images folder exists and may be written
+ *
+ * @param string $folder images folder
+ * @param boolean $create create the folder if not exists
+ *
+ * @return boolean true if folder exists and may be written
+ */
+ public static function canWriteImages($folder, $create = false)
+ {
+ global $core;
+
+ $public = path::real($core->blog->public_path);
+ $imgs = self::imagesPath($folder);
+
+ if (!function_exists('imagecreatetruecolor') || !function_exists('imagepng') || !function_exists('imagecreatefrompng')) {
+ $core->error->add(__('At least one of the following functions is not available: ' .
+ 'imagecreatetruecolor, imagepng & imagecreatefrompng.'));
+ return false;
+ }
+
+ if (!is_dir($public)) {
+ $core->error->add(__('The \'public\' directory does not exist.'));
+ return false;
+ }
+
+ if (!is_dir($imgs)) {
+ if (!is_writable($public)) {
+ $core->error->add(sprintf(__('The \'%s\' directory cannot be modified.'), 'public'));
+ return false;
+ }
+ if ($create) {
+ files::makeDir($imgs);
+ }
+ return true;
+ }
+
+ if (!is_writable($imgs)) {
+ $core->error->add(sprintf(__('The \'%s\' directory cannot be modified.'), 'public/' . $folder));
+ return false;
+ }
+
+ return true;
+ }
+
+/**
+ * Upload an image in images folder
+ *
+ * @param string $folder images folder
+ * @param string $f selected image file (as $_FILES[])
+ * @param int $width check accurate width of uploaded image if <> 0
+ *
+ * @return string full pathname of uploaded image
+ */
+ public static function uploadImage($folder, $f, $width = 0)
+ {
+ if (!self::canWriteImages($folder, true)) {
+ throw new Exception(__('Unable to create images.'));
+ }
+
+ $name = $f['name'];
+ $type = files::getMimeType($name);
+
+ if ($type != 'image/jpeg' && $type != 'image/png') {
+ throw new Exception(__('Invalid file type.'));
+ }
+
+ $dest = self::imagesPath($folder) . '/uploaded' . ($type == 'image/png' ? '.png' : '.jpg');
+
+ if (@move_uploaded_file($f['tmp_name'], $dest) === false) {
+ throw new Exception(__('An error occurred while writing the file.'));
+ }
+
+ if ($width) {
+ $s = getimagesize($dest);
+ if ($s[0] != $width) {
+ throw new Exception(sprintf(__('Uploaded image is not %s pixels wide.'), $width));
+ }
+ }
+
+ return $dest;
+ }
+
+/**
+ * Delete an image from images folder (with its thumbnails if any)
+ *
+ * @param string $folder images folder
+ * @param string $img image filename
+ */
+ public static function dropImage($folder, $img)
+ {
+ global $core;
+
+ $img = path::real(self::imagesPath($folder) . '/' . $img);
+ if (is_writable(dirname($img))) {
+ // Delete thumbnails if any
+ try {
+ $media = new dcMedia($core);
+ $media->imageThumbRemove($img);
+ } catch (Exception $e) {
+ $core->error->add($e->getMessage());
+ }
+ // Delete image
+ @unlink($img);
+ }
+ }
+}
diff --git a/dotclear._no/inc/admin/prepend.php b/dotclear._no/inc/admin/prepend.php
new file mode 100644
index 0000000..847ea96
--- /dev/null
+++ b/dotclear._no/inc/admin/prepend.php
@@ -0,0 +1,298 @@
+auth->getInfo('user_lang');
+ $_lang = preg_match('/^[a-z]{2}(-[a-z]{2})?$/', $_lang) ? $_lang : 'en';
+
+ l10n::lang($_lang);
+ if (l10n::set(dirname(__FILE__) . '/../../locales/' . $_lang . '/date') === false && $_lang != 'en') {
+ l10n::set(dirname(__FILE__) . '/../../locales/en/date');
+ }
+ l10n::set(dirname(__FILE__) . '/../../locales/' . $_lang . '/main');
+ l10n::set(dirname(__FILE__) . '/../../locales/' . $_lang . '/public');
+ l10n::set(dirname(__FILE__) . '/../../locales/' . $_lang . '/plugins');
+
+ // Set lexical lang
+ dcUtils::setlexicalLang('admin', $_lang);
+}
+
+function dc_admin_icon_url($img)
+{
+ global $core;
+
+ $core->auth->user_prefs->addWorkspace('interface');
+ $user_ui_iconset = @$core->auth->user_prefs->interface->iconset;
+ if (($user_ui_iconset) && ($img)) {
+ $icon = false;
+ if ((preg_match('/^images\/menu\/(.+)$/', $img, $m)) ||
+ (preg_match('/^index\.php\?pf=(.+)$/', $img, $m))) {
+ if ($m[1]) {
+ $icon = path::real(dirname(__FILE__) . '/../../admin/images/iconset/' . $user_ui_iconset . '/' . $m[1], false);
+ if ($icon !== false) {
+ $allow_types = ['png', 'jpg', 'jpeg', 'gif'];
+ if (is_file($icon) && is_readable($icon) && in_array(files::getExtension($icon), $allow_types)) {
+ return DC_ADMIN_URL . 'images/iconset/' . $user_ui_iconset . '/' . $m[1];
+ }
+ }
+ }
+ }
+ }
+ return $img;
+}
+
+function addMenuItem($section, $desc, $adminurl, $icon, $perm, $pinned = false, $strict = false)
+{
+ global $core, $_menu;
+
+ $url = $core->adminurl->get($adminurl);
+ $pattern = '@' . preg_quote($url) . ($strict ? '' : '(\?.*)?') . '$@';
+ $_menu[$section]->prependItem($desc, $url, $icon,
+ preg_match($pattern, $_SERVER['REQUEST_URI']), $perm, null, null, $pinned);
+}
+
+if (defined('DC_AUTH_SESS_ID') && defined('DC_AUTH_SESS_UID')) {
+ # We have session information in constants
+ $_COOKIE[DC_SESSION_NAME] = DC_AUTH_SESS_ID;
+
+ if (!$core->auth->checkSession(DC_AUTH_SESS_UID)) {
+ throw new Exception('Invalid session data.');
+ }
+
+ # Check nonce from POST requests
+ if (!empty($_POST)) {
+ if (empty($_POST['xd_check']) || !$core->checkNonce($_POST['xd_check'])) {
+ throw new Exception('Precondition Failed.');
+ }
+ }
+
+ if (empty($_SESSION['sess_blog_id'])) {
+ throw new Exception('Permission denied.');
+ }
+
+ # Loading locales
+ dc_load_locales();
+
+ $core->setBlog($_SESSION['sess_blog_id']);
+ if (!$core->blog->id) {
+ throw new Exception('Permission denied.');
+ }
+} elseif ($core->auth->sessionExists()) {
+ # If we have a session we launch it now
+ try {
+ if (!$core->auth->checkSession()) {
+ # Avoid loop caused by old cookie
+ $p = $core->session->getCookieParameters(false, -600);
+ $p[3] = '/';
+ call_user_func_array('setcookie', $p);
+
+ http::redirect('auth.php');
+ }
+ } catch (Exception $e) {
+ __error(__('Database error')
+ , __('There seems to be no Session table in your database. Is Dotclear completly installed?')
+ , 20);
+ }
+
+ # Check nonce from POST requests
+ if (!empty($_POST)) {
+ if (empty($_POST['xd_check']) || !$core->checkNonce($_POST['xd_check'])) {
+ http::head(412);
+ header('Content-Type: text/plain');
+ echo 'Precondition Failed';
+ exit;
+ }
+ }
+
+ if (!empty($_REQUEST['switchblog'])
+ && $core->auth->getPermissions($_REQUEST['switchblog']) !== false) {
+ $_SESSION['sess_blog_id'] = $_REQUEST['switchblog'];
+ if (isset($_SESSION['media_manager_dir'])) {
+ unset($_SESSION['media_manager_dir']);
+ }
+ if (isset($_SESSION['media_manager_page'])) {
+ unset($_SESSION['media_manager_page']);
+ }
+
+ if (!empty($_REQUEST['redir'])) {
+ # Keep context as far as possible
+ $redir = $_REQUEST['redir'];
+ } else {
+ # Removing switchblog from URL
+ $redir = $_SERVER['REQUEST_URI'];
+ $redir = preg_replace('/switchblog=(.*?)(&|$)/', '', $redir);
+ $redir = preg_replace('/\?$/', '', $redir);
+ }
+ http::redirect($redir);
+ exit;
+ }
+
+ # Check blog to use and log out if no result
+ if (isset($_SESSION['sess_blog_id'])) {
+ if ($core->auth->getPermissions($_SESSION['sess_blog_id']) === false) {
+ unset($_SESSION['sess_blog_id']);
+ }
+ } else {
+ if (($b = $core->auth->findUserBlog($core->auth->getInfo('user_default_blog'))) !== false) {
+ $_SESSION['sess_blog_id'] = $b;
+ unset($b);
+ }
+ }
+
+ # Loading locales
+ dc_load_locales();
+
+ if (isset($_SESSION['sess_blog_id'])) {
+ $core->setBlog($_SESSION['sess_blog_id']);
+ } else {
+ $core->session->destroy();
+ http::redirect('auth.php');
+ }
+}
+
+$core->adminurl = new dcAdminURL($core);
+
+$core->adminurl->register('admin.posts', 'posts.php');
+$core->adminurl->register('admin.popup_posts', 'popup_posts.php');
+$core->adminurl->register('admin.post', 'post.php');
+$core->adminurl->register('admin.post.media', 'post_media.php');
+$core->adminurl->register('admin.blog.theme', 'blog_theme.php');
+$core->adminurl->register('admin.blog.pref', 'blog_pref.php');
+$core->adminurl->register('admin.blog.del', 'blog_del.php');
+$core->adminurl->register('admin.blog', 'blog.php');
+$core->adminurl->register('admin.blogs', 'blogs.php');
+$core->adminurl->register('admin.categories', 'categories.php');
+$core->adminurl->register('admin.category', 'category.php');
+$core->adminurl->register('admin.comments', 'comments.php');
+$core->adminurl->register('admin.comment', 'comment.php');
+$core->adminurl->register('admin.help', 'help.php');
+$core->adminurl->register('admin.home', 'index.php');
+$core->adminurl->register('admin.langs', 'langs.php');
+$core->adminurl->register('admin.media', 'media.php');
+$core->adminurl->register('admin.media.item', 'media_item.php');
+$core->adminurl->register('admin.plugins', 'plugins.php');
+$core->adminurl->register('admin.plugin', 'plugin.php');
+$core->adminurl->register('admin.search', 'search.php');
+$core->adminurl->register('admin.user.preferences', 'preferences.php');
+$core->adminurl->register('admin.user', 'user.php');
+$core->adminurl->register('admin.user.actions', 'users_actions.php');
+$core->adminurl->register('admin.users', 'users.php');
+$core->adminurl->register('admin.auth', 'auth.php');
+$core->adminurl->register('admin.help', 'help.php');
+$core->adminurl->register('admin.update', 'update.php');
+
+$core->adminurl->registercopy('load.plugin.file', 'admin.home', ['pf' => 'dummy.css']);
+$core->adminurl->registercopy('load.var.file', 'admin.home', ['vf' => 'dummy.json']);
+
+if ($core->auth->userID() && $core->blog !== null) {
+ # Loading resources and help files
+ $locales_root = dirname(__FILE__) . '/../../locales/';
+ require $locales_root . '/en/resources.php';
+ if (($f = l10n::getFilePath($locales_root, 'resources.php', $_lang))) {
+ require $f;
+ }
+ unset($f);
+
+ if (($hfiles = @scandir($locales_root . $_lang . '/help')) !== false) {
+ foreach ($hfiles as $hfile) {
+ if (preg_match('/^(.*)\.html$/', $hfile, $m)) {
+ $GLOBALS['__resources']['help'][$m[1]] = $locales_root . $_lang . '/help/' . $hfile;
+ }
+ }
+ }
+ unset($hfiles, $locales_root);
+ // Contextual help flag
+ $GLOBALS['__resources']['ctxhelp'] = false;
+
+ $core->auth->user_prefs->addWorkspace('interface');
+ $user_ui_nofavmenu = $core->auth->user_prefs->interface->nofavmenu;
+
+ $core->favs = new dcFavorites($core);
+ $core->notices = new dcNotices($core);
+
+ # [] : Title, URL, small icon, large icon, permissions, id, class
+ # NB : '*' in permissions means any, null means super admin only
+
+ # Menus creation
+ $_menu = new ArrayObject();
+ $_menu['Dashboard'] = new dcMenu('dashboard-menu', null);
+ if (!$user_ui_nofavmenu) {
+ $core->favs->appendMenuTitle($_menu);
+ }
+ $_menu['Blog'] = new dcMenu('blog-menu', 'Blog');
+ $_menu['System'] = new dcMenu('system-menu', 'System');
+ $_menu['Plugins'] = new dcMenu('plugins-menu', 'Plugins');
+ # Loading plugins
+ $core->plugins->loadModules(DC_PLUGINS_ROOT, 'admin', $_lang);
+ $core->favs->setup();
+
+ if (!$user_ui_nofavmenu) {
+ $core->favs->appendMenu($_menu);
+ }
+
+ # Set menu titles
+
+ $_menu['System']->title = __('System settings');
+ $_menu['Blog']->title = __('Blog');
+ $_menu['Plugins']->title = __('Plugins');
+
+ addMenuItem('Blog', __('Blog appearance'), 'admin.blog.theme', 'images/menu/themes.png',
+ $core->auth->check('admin', $core->blog->id));
+ addMenuItem('Blog', __('Blog settings'), 'admin.blog.pref', 'images/menu/blog-pref.png',
+ $core->auth->check('admin', $core->blog->id));
+ addMenuItem('Blog', __('Media manager'), 'admin.media', 'images/menu/media.png',
+ $core->auth->check('media,media_admin', $core->blog->id));
+ addMenuItem('Blog', __('Categories'), 'admin.categories', 'images/menu/categories.png',
+ $core->auth->check('categories', $core->blog->id));
+ addMenuItem('Blog', __('Search'), 'admin.search', 'images/menu/search.png',
+ $core->auth->check('usage,contentadmin', $core->blog->id));
+ addMenuItem('Blog', __('Comments'), 'admin.comments', 'images/menu/comments.png',
+ $core->auth->check('usage,contentadmin', $core->blog->id));
+ addMenuItem('Blog', __('Posts'), 'admin.posts', 'images/menu/entries.png',
+ $core->auth->check('usage,contentadmin', $core->blog->id));
+ addMenuItem('Blog', __('New post'), 'admin.post', 'images/menu/edit.png',
+ $core->auth->check('usage,contentadmin', $core->blog->id), true, true);
+
+ addMenuItem('System', __('Update'), 'admin.update', 'images/menu/update.png',
+ $core->auth->isSuperAdmin() && is_readable(DC_DIGESTS));
+ addMenuItem('System', __('Languages'), 'admin.langs', 'images/menu/langs.png',
+ $core->auth->isSuperAdmin());
+ addMenuItem('System', __('Plugins management'), 'admin.plugins', 'images/menu/plugins.png',
+ $core->auth->isSuperAdmin());
+ addMenuItem('System', __('Users'), 'admin.users', 'images/menu/users.png',
+ $core->auth->isSuperAdmin());
+ addMenuItem('System', __('Blogs'), 'admin.blogs', 'images/menu/blogs.png',
+ $core->auth->isSuperAdmin() ||
+ $core->auth->check('usage,contentadmin', $core->blog->id) && $core->auth->getBlogCount() > 1);
+
+ if (empty($core->blog->settings->system->jquery_migrate_mute)) {
+ $core->blog->settings->system->put('jquery_migrate_mute', true, 'boolean', 'Mute warnings for jquery migrate plugin ?', false);
+ }
+ if (empty($core->blog->settings->system->jquery_allow_old_version)) {
+ $core->blog->settings->system->put('jquery_allow_old_version', false, 'boolean', 'Allow older version of jQuery', false, true);
+ }
+
+ # Admin behaviors
+ $core->addBehavior('adminPopupPosts', ['dcAdminBlogPref', 'adminPopupPosts']);
+}
diff --git a/dotclear._no/inc/config.php b/dotclear._no/inc/config.php
new file mode 100644
index 0000000..c14139d
--- /dev/null
+++ b/dotclear._no/inc/config.php
@@ -0,0 +1,93 @@
+
diff --git a/dotclear._no/inc/core/class.dc.auth.php b/dotclear._no/inc/core/class.dc.auth.php
new file mode 100644
index 0000000..99a54ef
--- /dev/null
+++ b/dotclear._no/inc/core/class.dc.auth.php
@@ -0,0 +1,674 @@
+core = &$core;
+ $this->con = &$core->con;
+ $this->blog_table = $core->prefix . 'blog';
+ $this->user_table = $core->prefix . 'user';
+ $this->perm_table = $core->prefix . 'permissions';
+
+ $this->perm_types = [
+ 'admin' => __('administrator'),
+ 'usage' => __('manage their own entries and comments'),
+ 'publish' => __('publish entries and comments'),
+ 'delete' => __('delete entries and comments'),
+ 'contentadmin' => __('manage all entries and comments'),
+ 'categories' => __('manage categories'),
+ 'media' => __('manage their own media items'),
+ 'media_admin' => __('manage all media items')
+ ];
+ }
+
+ /// @name Credentials and user permissions
+ //@{
+ /**
+ * Checks if user exists and can log in. $pwd argument is optionnal
+ * while you may need to check user without password. This method will create
+ * credentials and populate all needed object properties.
+ *
+ * @param string $user_id User ID
+ * @param string $pwd User password
+ * @param string $user_key User key check
+ * @param boolean $check_blog checks if user is associated to a blog or not.
+ * @return boolean
+ */
+ public function checkUser($user_id, $pwd = null, $user_key = null, $check_blog = true)
+ {
+ # Check user and password
+ $strReq = 'SELECT user_id, user_super, user_pwd, user_change_pwd, ' .
+ 'user_name, user_firstname, user_displayname, user_email, ' .
+ 'user_url, user_default_blog, user_options, ' .
+ 'user_lang, user_tz, user_post_status, user_creadt, user_upddt ' .
+ 'FROM ' . $this->con->escapeSystem($this->user_table) . ' ' .
+ "WHERE user_id = '" . $this->con->escape($user_id) . "' ";
+
+ try {
+ $rs = $this->con->select($strReq);
+ } catch (Exception $e) {
+ $err = $e->getMessage();
+ return false;
+ }
+
+ if ($rs->isEmpty()) {
+ sleep(rand(2, 5));
+ return false;
+ }
+
+ $rs->extend('rsExtUser');
+
+ if ($pwd != '') {
+ $rehash = false;
+ if (password_verify($pwd, $rs->user_pwd)) {
+ // User password ok
+ if (password_needs_rehash($rs->user_pwd, PASSWORD_DEFAULT)) {
+ $rs->user_pwd = $this->crypt($pwd);
+ $rehash = true;
+ }
+ } else {
+ // Check if pwd still stored in old fashion way
+ $ret = password_get_info($rs->user_pwd);
+ if (is_array($ret) && isset($ret['algo']) && $ret['algo'] == 0) {
+ // hash not done with password_hash() function, check by old fashion way
+ if (crypt::hmac(DC_MASTER_KEY, $pwd, DC_CRYPT_ALGO) == $rs->user_pwd) {
+ // Password Ok, need to store it in new fashion way
+ $rs->user_pwd = $this->crypt($pwd);
+ $rehash = true;
+ } else {
+ // Password KO
+ sleep(rand(2, 5));
+ return false;
+ }
+ } else {
+ // Password KO
+ sleep(rand(2, 5));
+ return false;
+ }
+ }
+ if ($rehash) {
+ // Store new hash in DB
+ $cur = $this->con->openCursor($this->user_table);
+ $cur->user_pwd = (string) $rs->user_pwd;
+ $cur->update("WHERE user_id = '" . $rs->user_id . "'");
+ }
+ } elseif ($user_key != '') {
+ if (http::browserUID(DC_MASTER_KEY . $rs->user_id . $this->cryptLegacy($rs->user_id)) != $user_key) {
+ return false;
+ }
+ }
+
+ $this->user_id = $rs->user_id;
+ $this->user_change_pwd = (boolean) $rs->user_change_pwd;
+ $this->user_admin = (boolean) $rs->user_super;
+
+ $this->user_info['user_pwd'] = $rs->user_pwd;
+ $this->user_info['user_name'] = $rs->user_name;
+ $this->user_info['user_firstname'] = $rs->user_firstname;
+ $this->user_info['user_displayname'] = $rs->user_displayname;
+ $this->user_info['user_email'] = $rs->user_email;
+ $this->user_info['user_url'] = $rs->user_url;
+ $this->user_info['user_default_blog'] = $rs->user_default_blog;
+ $this->user_info['user_lang'] = $rs->user_lang;
+ $this->user_info['user_tz'] = $rs->user_tz;
+ $this->user_info['user_post_status'] = $rs->user_post_status;
+ $this->user_info['user_creadt'] = $rs->user_creadt;
+ $this->user_info['user_upddt'] = $rs->user_upddt;
+
+ $this->user_info['user_cn'] = dcUtils::getUserCN($rs->user_id, $rs->user_name,
+ $rs->user_firstname, $rs->user_displayname);
+
+ $this->user_options = array_merge($this->core->userDefaults(), $rs->options());
+
+ $this->user_prefs = new dcPrefs($this->core, $this->user_id);
+
+ # Get permissions on blogs
+ if ($check_blog && ($this->findUserBlog() === false)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * This method crypt given string (password, session_id, …).
+ *
+ * @param string $pwd string to be crypted
+ * @return string crypted value
+ */
+ public function crypt($pwd)
+ {
+ return password_hash($pwd, PASSWORD_DEFAULT);
+ }
+
+ /**
+ * This method crypt given string (password, session_id, …).
+ *
+ * @param string $pwd string to be crypted
+ * @return string crypted value
+ */
+ public function cryptLegacy($pwd)
+ {
+ return crypt::hmac(DC_MASTER_KEY, $pwd, DC_CRYPT_ALGO);
+ }
+
+ /**
+ * This method only check current user password.
+ *
+ * @param string $pwd User password
+ * @return boolean
+ */
+ public function checkPassword($pwd)
+ {
+ if (!empty($this->user_info['user_pwd'])) {
+ return password_verify($pwd, $this->user_info['user_pwd']);
+ }
+
+ return false;
+ }
+
+ /**
+ * This method checks if user session cookie exists
+ *
+ * @return boolean
+ */
+ public function sessionExists()
+ {
+ return isset($_COOKIE[DC_SESSION_NAME]);
+ }
+
+ /**
+ * This method checks user session validity.
+ *
+ * @return boolean
+ */
+ public function checkSession($uid = null)
+ {
+ $this->core->session->start();
+
+ # If session does not exist, logout.
+ if (!isset($_SESSION['sess_user_id'])) {
+ $this->core->session->destroy();
+ return false;
+ }
+
+ # Check here for user and IP address
+ $this->checkUser($_SESSION['sess_user_id']);
+ $uid = $uid ?: http::browserUID(DC_MASTER_KEY);
+
+ $user_can_log = $this->userID() !== null && $uid == $_SESSION['sess_browser_uid'];
+
+ if (!$user_can_log) {
+ $this->core->session->destroy();
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Checks if user must change his password in order to login.
+ *
+ * @return boolean
+ */
+ public function mustChangePassword()
+ {
+ return $this->user_change_pwd;
+ }
+
+ /**
+ * Checks if user is super admin
+ *
+ * @return boolean
+ */
+ public function isSuperAdmin()
+ {
+ return $this->user_admin;
+ }
+
+ /**
+ * Checks if user has permissions given in $permissions for blog
+ * $blog_id . $permissions is a coma separated list of
+ * permissions.
+ *
+ * @param string $permissions Permissions list
+ * @param string $blog_id Blog ID
+ * @return boolean
+ */
+ public function check($permissions, $blog_id)
+ {
+ if ($this->user_admin) {
+ return true;
+ }
+
+ $p = array_map('trim', explode(',', $permissions));
+ $b = $this->getPermissions($blog_id);
+
+ if ($b != false) {
+ if (isset($b['admin'])) {
+ return true;
+ }
+
+ foreach ($p as $v) {
+ if (isset($b[$v])) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true if user is allowed to change its password.
+ *
+ * @return boolean
+ */
+ public function allowPassChange()
+ {
+ return $this->allow_pass_change;
+ }
+ //@}
+
+ /// @name User code handlers
+ //@{
+ public function getUserCode()
+ {
+ $code =
+ pack('a32', $this->userID()) .
+ pack('H*', $this->crypt($this->getInfo('user_pwd')));
+ return bin2hex($code);
+ }
+
+ public function checkUserCode($code)
+ {
+ $code = @pack('H*', $code);
+
+ $user_id = trim(@pack('a32', substr($code, 0, 32)));
+ $pwd = @unpack('H*hex', substr($code, 32));
+
+ if ($user_id === false || $pwd === false) {
+ return false;
+ }
+
+ $pwd = $pwd['hex'];
+
+ $strReq = 'SELECT user_id, user_pwd ' .
+ 'FROM ' . $this->user_table . ' ' .
+ "WHERE user_id = '" . $this->con->escape($user_id) . "' ";
+
+ $rs = $this->con->select($strReq);
+
+ if ($rs->isEmpty()) {
+ return false;
+ }
+
+ if ($this->crypt($rs->user_pwd) != $pwd) {
+ return false;
+ }
+
+ return $rs->user_id;
+ }
+ //@}
+
+ /// @name Sudo
+ //@{
+ /**
+ * Calls $f function with super admin rights.
+ * Returns the function result.
+ *
+ * @param callback $f Callback function
+ * @return mixed
+ */
+ public function sudo($f, ...$args)
+ {
+ if (!is_callable($f)) {
+ throw new Exception($f . ' function doest not exist');
+ }
+
+ if ($this->user_admin) {
+ $res = call_user_func_array($f, $args);
+ } else {
+ $this->user_admin = true;
+ try {
+ $res = call_user_func_array($f, $args);
+ $this->user_admin = false;
+ } catch (Exception $e) {
+ $this->user_admin = false;
+ throw $e;
+ }
+ }
+
+ return $res;
+ }
+ //@}
+
+ /// @name User information and options
+ //@{
+ /**
+ * Returns user permissions for a blog as an array which looks like:
+ *
+ * - [blog_id]
+ * - [permission] => true
+ * - ...
+ *
+ * @param string $blog_id Blog ID
+ * @return array
+ */
+ public function getPermissions($blog_id)
+ {
+ if (isset($this->blogs[$blog_id])) {
+ return $this->blogs[$blog_id];
+ }
+
+ if ($this->user_admin) {
+ $strReq = 'SELECT blog_id ' .
+ 'from ' . $this->blog_table . ' ' .
+ "WHERE blog_id = '" . $this->con->escape($blog_id) . "' ";
+ $rs = $this->con->select($strReq);
+
+ $this->blogs[$blog_id] = $rs->isEmpty() ? false : ['admin' => true];
+
+ return $this->blogs[$blog_id];
+ }
+
+ $strReq = 'SELECT permissions ' .
+ 'FROM ' . $this->perm_table . ' ' .
+ "WHERE user_id = '" . $this->con->escape($this->user_id) . "' " .
+ "AND blog_id = '" . $this->con->escape($blog_id) . "' " .
+ "AND (permissions LIKE '%|usage|%' OR permissions LIKE '%|admin|%' OR permissions LIKE '%|contentadmin|%') ";
+ $rs = $this->con->select($strReq);
+
+ $this->blogs[$blog_id] = $rs->isEmpty() ? false : $this->parsePermissions($rs->permissions);
+
+ return $this->blogs[$blog_id];
+ }
+
+ public function getBlogCount()
+ {
+ if ($this->blog_count === null) {
+ $this->blog_count = $this->core->getBlogs([], true)->f(0);
+ }
+
+ return $this->blog_count;
+ }
+
+ public function findUserBlog($blog_id = null)
+ {
+ if ($blog_id && $this->getPermissions($blog_id) !== false) {
+ return $blog_id;
+ } else {
+ if ($this->user_admin) {
+ $strReq = 'SELECT blog_id ' .
+ 'FROM ' . $this->blog_table . ' ' .
+ 'ORDER BY blog_id ASC ' .
+ $this->con->limit(1);
+ } else {
+ $strReq = 'SELECT P.blog_id ' .
+ 'FROM ' . $this->perm_table . ' P, ' . $this->blog_table . ' B ' .
+ "WHERE user_id = '" . $this->con->escape($this->user_id) . "' " .
+ "AND P.blog_id = B.blog_id " .
+ "AND (permissions LIKE '%|usage|%' OR permissions LIKE '%|admin|%' OR permissions LIKE '%|contentadmin|%') " .
+ "AND blog_status >= 0 " .
+ 'ORDER BY P.blog_id ASC ' .
+ $this->con->limit(1);
+ }
+
+ $rs = $this->con->select($strReq);
+ if (!$rs->isEmpty()) {
+ return $rs->blog_id;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns current user ID
+ *
+ * @return string
+ */
+ public function userID()
+ {
+ return $this->user_id;
+ }
+
+ /**
+ * Returns information about a user .
+ *
+ * @param string $n Information name
+ * @return string
+ */
+ public function getInfo($n)
+ {
+ if (isset($this->user_info[$n])) {
+ return $this->user_info[$n];
+ }
+
+ return;
+ }
+
+ /**
+ * Returns a specific user option
+ *
+ * @param string $n Option name
+ * @return string
+ */
+ public function getOption($n)
+ {
+ if (isset($this->user_options[$n])) {
+ return $this->user_options[$n];
+ }
+ return;
+ }
+
+ /**
+ * Returns all user options in an associative array.
+ *
+ * @return array
+ */
+ public function getOptions()
+ {
+ return $this->user_options;
+ }
+ //@}
+
+ /// @name Permissions
+ //@{
+ /**
+ * Returns an array with permissions parsed from the string $level
+ *
+ * @param string $level Permissions string
+ * @return array
+ */
+ public function parsePermissions($level)
+ {
+ $level = preg_replace('/^\|/', '', $level);
+ $level = preg_replace('/\|$/', '', $level);
+
+ $res = [];
+ foreach (explode('|', $level) as $v) {
+ $res[$v] = true;
+ }
+ return $res;
+ }
+
+ /**
+ * Returns perm_types property content.
+ *
+ * @return array
+ */
+ public function getPermissionsTypes()
+ {
+ return $this->perm_types;
+ }
+
+ /**
+ * Adds a new permission type.
+ *
+ * @param string $name Permission name
+ * @param string $title Permission title
+ */
+ public function setPermissionType($name, $title)
+ {
+ $this->perm_types[$name] = $title;
+ }
+ //@}
+
+ /// @name Password recovery
+ //@{
+ /**
+ * Add a recover key to a specific user identified by its email and
+ * password.
+ *
+ * @param string $user_id User ID
+ * @param string $user_email User Email
+ * @return string
+ */
+ public function setRecoverKey($user_id, $user_email)
+ {
+ $strReq = 'SELECT user_id ' .
+ 'FROM ' . $this->user_table . ' ' .
+ "WHERE user_id = '" . $this->con->escape($user_id) . "' " .
+ "AND user_email = '" . $this->con->escape($user_email) . "' ";
+
+ $rs = $this->con->select($strReq);
+
+ if ($rs->isEmpty()) {
+ throw new Exception(__('That user does not exist in the database.'));
+ }
+
+ $key = md5(uniqid('', true));
+
+ $cur = $this->con->openCursor($this->user_table);
+ $cur->user_recover_key = $key;
+
+ $cur->update("WHERE user_id = '" . $this->con->escape($user_id) . "'");
+
+ return $key;
+ }
+
+ /**
+ * Creates a new user password using recovery key. Returns an array:
+ *
+ * - user_email
+ * - user_id
+ * - new_pass
+ *
+ * @param string $recover_key Recovery key
+ * @return array
+ */
+ public function recoverUserPassword($recover_key)
+ {
+ $strReq = 'SELECT user_id, user_email ' .
+ 'FROM ' . $this->user_table . ' ' .
+ "WHERE user_recover_key = '" . $this->con->escape($recover_key) . "' ";
+
+ $rs = $this->con->select($strReq);
+
+ if ($rs->isEmpty()) {
+ throw new Exception(__('That key does not exist in the database.'));
+ }
+
+ $new_pass = crypt::createPassword();
+
+ $cur = $this->con->openCursor($this->user_table);
+ $cur->user_pwd = $this->crypt($new_pass);
+ $cur->user_recover_key = null;
+ $cur->user_change_pwd = 1; // User will have to change this temporary password at next login
+
+ $cur->update("WHERE user_recover_key = '" . $this->con->escape($recover_key) . "'");
+
+ return ['user_email' => $rs->user_email, 'user_id' => $rs->user_id, 'new_pass' => $new_pass];
+ }
+ //@}
+
+ /** @name User management callbacks
+ This 3 functions only matter if you extend this class and use
+ DC_AUTH_CLASS constant.
+ These are called after core user management functions.
+ Could be useful if you need to add/update/remove stuff in your
+ LDAP directory or other third party authentication database.
+ */
+ //@{
+
+ /**
+ * Called after core->addUser
+ * @see dcCore::addUser
+ * @param cursor $cur User cursor
+ */
+ public function afterAddUser($cur)
+ {}
+
+ /**
+ * Called after core->updUser
+ * @see dcCore::updUser
+ * @param string $id User ID
+ * @param cursor $cur User cursor
+ */
+ public function afterUpdUser($id, $cur)
+ {}
+
+ /**
+ * Called after core->delUser
+ * @see dcCore::delUser
+ * @param string $id User ID
+ */
+ public function afterDelUser($id)
+ {}
+ //@}
+}
diff --git a/dotclear._no/inc/core/class.dc.blog.php b/dotclear._no/inc/core/class.dc.blog.php
new file mode 100644
index 0000000..027a89e
--- /dev/null
+++ b/dotclear._no/inc/core/class.dc.blog.php
@@ -0,0 +1,2408 @@
+dcCore Dotclear core reference
+ @param id string Blog ID
+ */
+ public function __construct($core, $id)
+ {
+ $this->con = &$core->con;
+ $this->prefix = $core->prefix;
+ $this->core = &$core;
+
+ if (($b = $this->core->getBlog($id)) !== false) {
+ $this->id = $id;
+ $this->uid = $b->blog_uid;
+ $this->name = $b->blog_name;
+ $this->desc = $b->blog_desc;
+ $this->url = $b->blog_url;
+ $this->host = http::getHostFromURL($this->url);
+ $this->creadt = strtotime($b->blog_creadt);
+ $this->upddt = strtotime($b->blog_upddt);
+ $this->status = $b->blog_status;
+
+ $this->settings = new dcSettings($this->core, $this->id);
+
+ $this->themes_path = path::fullFromRoot($this->settings->system->themes_path, DC_ROOT);
+ $this->public_path = path::fullFromRoot($this->settings->system->public_path, DC_ROOT);
+
+ $this->post_status['-2'] = __('Pending');
+ $this->post_status['-1'] = __('Scheduled');
+ $this->post_status['0'] = __('Unpublished');
+ $this->post_status['1'] = __('Published');
+
+ $this->comment_status['-2'] = __('Junk');
+ $this->comment_status['-1'] = __('Pending');
+ $this->comment_status['0'] = __('Unpublished');
+ $this->comment_status['1'] = __('Published');
+
+ # --BEHAVIOR-- coreBlogConstruct
+ $this->core->callBehavior('coreBlogConstruct', $this);
+ }
+ }
+
+ /// @name Common public methods
+ //@{
+ /**
+ Returns blog URL ending with a question mark.
+ */
+ public function getQmarkURL()
+ {
+ if (substr($this->url, -1) != '?') {
+ return $this->url . '?';
+ }
+
+ return $this->url;
+ }
+
+ /**
+ Returns jQuery version selected for the blog.
+ */
+ public function getJsJQuery()
+ {
+ $version = $this->settings->system->jquery_version;
+ if ($version == '') {
+ // Version not set, use default one
+ $version = DC_DEFAULT_JQUERY; // defined in inc/prepend.php
+ } else {
+ if (!$this->settings->system->jquery_allow_old_version) {
+ // Use the blog defined version only if more recent than default
+ if (version_compare($version, DC_DEFAULT_JQUERY, '<')) {
+ $version = DC_DEFAULT_JQUERY; // defined in inc/prepend.php
+ }
+ }
+ }
+ return 'jquery/' . $version;
+ }
+
+ /**
+ Returns public URL of specified plugin file.
+ */
+ public function getPF($pf, $strip_host = true)
+ {
+ $ret = $this->getQmarkURL() . 'pf=' . $pf;
+ if ($strip_host) {
+ $ret = html::stripHostURL($ret);
+ }
+ return $ret;
+ }
+
+ /**
+ Returns public URL of specified var file.
+ */
+ public function getVF($vf, $strip_host = true)
+ {
+ $ret = $this->getQmarkURL() . 'vf=' . $vf;
+ if ($strip_host) {
+ $ret = html::stripHostURL($ret);
+ }
+ return $ret;
+ }
+
+ /**
+ Returns an entry status name given to a code. Status are translated, never
+ use it for tests. If status code does not exist, returns unpublished .
+
+ @param s integer Status code
+ @return string Blog status name
+ */
+ public function getPostStatus($s)
+ {
+ if (isset($this->post_status[$s])) {
+ return $this->post_status[$s];
+ }
+ return $this->post_status['0'];
+ }
+
+ /**
+ Returns an array of available entry status codes and names.
+
+ @return array Simple array with codes in keys and names in value
+ */
+ public function getAllPostStatus()
+ {
+ return $this->post_status;
+ }
+
+ /**
+ Returns an array of available comment status codes and names.
+
+ @return array Simple array with codes in keys and names in value
+ */
+ public function getAllCommentStatus()
+ {
+ return $this->comment_status;
+ }
+
+ /**
+ Disallows entries password protection. You need to set it to
+ false while serving a public blog.
+
+ @param v boolean
+ */
+ public function withoutPassword($v)
+ {
+ $this->without_password = (boolean) $v;
+ }
+ //@}
+
+ /// @name Triggers methods
+ //@{
+ /**
+ Updates blog last update date. Should be called every time you change
+ an element related to the blog.
+ */
+ public function triggerBlog()
+ {
+ $cur = $this->con->openCursor($this->prefix . 'blog');
+
+ $cur->blog_upddt = date('Y-m-d H:i:s');
+
+ $cur->update("WHERE blog_id = '" . $this->con->escape($this->id) . "' ");
+
+ # --BEHAVIOR-- coreBlogAfterTriggerBlog
+ $this->core->callBehavior('coreBlogAfterTriggerBlog', $cur);
+ }
+
+ /**
+ Updates comment and trackback counters in post table. Should be called
+ every time a comment or trackback is added, removed or changed its status.
+
+ @param id integer Comment ID
+ @param del boolean If comment is delete, set this to true
+ */
+ public function triggerComment($id, $del = false)
+ {
+ $this->triggerComments($id, $del);
+ }
+
+ /**
+ Updates comments and trackbacks counters in post table. Should be called
+ every time comments or trackbacks are added, removed or changed their status.
+
+ @param ids mixed Comment(s) ID(s)
+ @param del boolean If comment is delete, set this to true
+ @param affected_posts mixed Posts(s) ID(s)
+ */
+ public function triggerComments($ids, $del = false, $affected_posts = null)
+ {
+ $comments_ids = dcUtils::cleanIds($ids);
+
+ # Get posts affected by comments edition
+ if (empty($affected_posts)) {
+ $strReq =
+ 'SELECT post_id ' .
+ 'FROM ' . $this->prefix . 'comment ' .
+ 'WHERE comment_id' . $this->con->in($comments_ids) .
+ 'GROUP BY post_id';
+
+ $rs = $this->con->select($strReq);
+
+ $affected_posts = [];
+ while ($rs->fetch()) {
+ $affected_posts[] = (integer) $rs->post_id;
+ }
+ }
+
+ if (!is_array($affected_posts) || empty($affected_posts)) {
+ return;
+ }
+
+ # Count number of comments if exists for affected posts
+ $strReq =
+ 'SELECT post_id, COUNT(post_id) AS nb_comment, comment_trackback ' .
+ 'FROM ' . $this->prefix . 'comment ' .
+ 'WHERE comment_status = 1 ' .
+ 'AND post_id' . $this->con->in($affected_posts) .
+ 'GROUP BY post_id,comment_trackback';
+
+ $rs = $this->con->select($strReq);
+
+ $posts = [];
+ while ($rs->fetch()) {
+ if ($rs->comment_trackback) {
+ $posts[$rs->post_id]['trackback'] = $rs->nb_comment;
+ } else {
+ $posts[$rs->post_id]['comment'] = $rs->nb_comment;
+ }
+ }
+
+ # Update number of comments on affected posts
+ $cur = $this->con->openCursor($this->prefix . 'post');
+ foreach ($affected_posts as $post_id) {
+ $cur->clean();
+
+ if (!array_key_exists($post_id, $posts)) {
+ $cur->nb_trackback = 0;
+ $cur->nb_comment = 0;
+ } else {
+ $cur->nb_trackback = empty($posts[$post_id]['trackback']) ? 0 : $posts[$post_id]['trackback'];
+ $cur->nb_comment = empty($posts[$post_id]['comment']) ? 0 : $posts[$post_id]['comment'];
+ }
+
+ $cur->update('WHERE post_id = ' . $post_id);
+ }
+ }
+ //@}
+
+ /// @name Categories management methods
+ //@{
+ public function categories()
+ {
+ if (!($this->categories instanceof dcCategories)) {
+ $this->categories = new dcCategories($this->core);
+ }
+
+ return $this->categories;
+ }
+
+ /**
+ Retrieves categories. $params is an associative array which can
+ take the following parameters:
+
+ - post_type: Get only entries with given type (default "post")
+ - cat_url: filter on cat_url field
+ - cat_id: filter on cat_id field
+ - start: start with a given category
+ - level: categories level to retrieve
+
+ @param params array Parameters
+ @return record
+ */
+ public function getCategories($params = [])
+ {
+ $c_params = [];
+ if (isset($params['post_type'])) {
+ $c_params['post_type'] = $params['post_type'];
+ unset($params['post_type']);
+ }
+ $counter = $this->getCategoriesCounter($c_params);
+
+ if (isset($params['without_empty']) && ($params['without_empty'] == false)) {
+ $without_empty = false;
+ } else {
+ $without_empty = $this->core->auth->userID() == false; # Get all categories if in admin display
+ }
+
+ $start = isset($params['start']) ? (integer) $params['start'] : 0;
+ $l = isset($params['level']) ? (integer) $params['level'] : 0;
+
+ $rs = $this->categories()->getChildren($start, null, 'desc');
+
+ # Get each categories total posts count
+ $data = [];
+ $stack = [];
+ $level = 0;
+ $cols = $rs->columns();
+ while ($rs->fetch()) {
+ $nb_post = isset($counter[$rs->cat_id]) ? (integer) $counter[$rs->cat_id] : 0;
+
+ if ($rs->level > $level) {
+ $nb_total = $nb_post;
+ $stack[$rs->level] = (integer) $nb_post;
+ } elseif ($rs->level == $level) {
+ $nb_total = $nb_post;
+ $stack[$rs->level] += $nb_post;
+ } else {
+ $nb_total = $stack[$rs->level + 1] + $nb_post;
+ if (isset($stack[$rs->level])) {
+ $stack[$rs->level] += $nb_total;
+ } else {
+ $stack[$rs->level] = $nb_total;
+ }
+ unset($stack[$rs->level + 1]);
+ }
+
+ if ($nb_total == 0 && $without_empty) {
+ continue;
+ }
+
+ $level = $rs->level;
+
+ $t = [];
+ foreach ($cols as $c) {
+ $t[$c] = $rs->f($c);
+ }
+ $t['nb_post'] = $nb_post;
+ $t['nb_total'] = $nb_total;
+
+ if ($l == 0 || ($l > 0 && $l == $rs->level)) {
+ array_unshift($data, $t);
+ }
+ }
+
+ # We need to apply filter after counting
+ if (isset($params['cat_id']) && $params['cat_id'] !== '') {
+ $found = false;
+ foreach ($data as $v) {
+ if ($v['cat_id'] == $params['cat_id']) {
+ $found = true;
+ $data = [$v];
+ break;
+ }
+ }
+ if (!$found) {
+ $data = [];
+ }
+ }
+
+ if (isset($params['cat_url']) && ($params['cat_url'] !== '')
+ && !isset($params['cat_id'])) {
+ $found = false;
+ foreach ($data as $v) {
+ if ($v['cat_url'] == $params['cat_url']) {
+ $found = true;
+ $data = [$v];
+ break;
+ }
+ }
+ if (!$found) {
+ $data = [];
+ }
+ }
+
+ return staticRecord::newFromArray($data);
+ }
+
+ /**
+ Retrieves a category by its ID.
+
+ @param id integer Category ID
+ @return record
+ */
+ public function getCategory($id)
+ {
+ return $this->getCategories(['cat_id' => $id]);
+ }
+
+ /**
+ Retrieves parents of a given category.
+
+ @param id integer Category ID
+ @return record
+ */
+ public function getCategoryParents($id)
+ {
+ return $this->categories()->getParents($id);
+ }
+
+ /**
+ Retrieves first parent of a given category.
+
+ @param id integer Category ID
+ @return record
+ */
+ public function getCategoryParent($id)
+ {
+ return $this->categories()->getParent($id);
+ }
+
+ /**
+ Retrieves all category's first children
+
+ @param id integer Category ID
+ @return record
+ */
+ public function getCategoryFirstChildren($id)
+ {
+ return $this->getCategories(['start' => $id, 'level' => $id == 0 ? 1 : 2]);
+ }
+
+ /**
+ * Returns true if a given category if in a given category's subtree
+ *
+ * @param string $cat_url The cat url
+ * @param string $start_url The top cat url
+ *
+ * @return boolean true if cat_url is in given start_url cat subtree
+ */
+ public function IsInCatSubtree($cat_url, $start_url)
+ {
+ // Get cat_id from start_url
+ $cat = $this->getCategories(['cat_url' => $start_url]);
+ if ($cat->fetch()) {
+ // cat_id found, get cat tree list
+ $cats = $this->getCategories(['start' => $cat->cat_id]);
+ while ($cats->fetch()) {
+ // check if post category is one of the cat or sub-cats
+ if ($cats->cat_url === $cat_url) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private function getCategoriesCounter($params = [])
+ {
+ $strReq =
+ 'SELECT C.cat_id, COUNT(P.post_id) AS nb_post ' .
+ 'FROM ' . $this->prefix . 'category AS C ' .
+ 'JOIN ' . $this->prefix . "post P ON (C.cat_id = P.cat_id AND P.blog_id = '" . $this->con->escape($this->id) . "' ) " .
+ "WHERE C.blog_id = '" . $this->con->escape($this->id) . "' ";
+
+ if (!$this->core->auth->userID()) {
+ $strReq .= 'AND P.post_status = 1 ';
+ }
+
+ if (!empty($params['post_type'])) {
+ $strReq .= 'AND P.post_type ' . $this->con->in($params['post_type']);
+ }
+
+ $strReq .= 'GROUP BY C.cat_id ';
+
+ $rs = $this->con->select($strReq);
+ $counters = [];
+ while ($rs->fetch()) {
+ $counters[$rs->cat_id] = $rs->nb_post;
+ }
+
+ return $counters;
+ }
+
+ /**
+ Creates a new category. Takes a cursor as input and returns the new category
+ ID.
+
+ @param cur cursor Category cursor
+ @return integer New category ID
+ */
+ public function addCategory($cur, $parent = 0)
+ {
+ if (!$this->core->auth->check('categories', $this->id)) {
+ throw new Exception(__('You are not allowed to add categories'));
+ }
+
+ $url = [];
+ if ($parent != 0) {
+ $rs = $this->getCategory($parent);
+ if ($rs->isEmpty()) {
+ $url = [];
+ } else {
+ $url[] = $rs->cat_url;
+ }
+ }
+
+ if ($cur->cat_url == '') {
+ $url[] = text::tidyURL($cur->cat_title, false);
+ } else {
+ $url[] = $cur->cat_url;
+ }
+
+ $cur->cat_url = implode('/', $url);
+
+ $this->getCategoryCursor($cur);
+ $cur->blog_id = (string) $this->id;
+
+ # --BEHAVIOR-- coreBeforeCategoryCreate
+ $this->core->callBehavior('coreBeforeCategoryCreate', $this, $cur);
+
+ $id = $this->categories()->addNode($cur, $parent);
+ # Update category's cursor
+ $rs = $this->getCategory($id);
+ if (!$rs->isEmpty()) {
+ $cur->cat_lft = $rs->cat_lft;
+ $cur->cat_rgt = $rs->cat_rgt;
+ }
+
+ # --BEHAVIOR-- coreAfterCategoryCreate
+ $this->core->callBehavior('coreAfterCategoryCreate', $this, $cur);
+ $this->triggerBlog();
+
+ return $cur->cat_id;
+ }
+
+ /**
+ Updates an existing category.
+
+ @param id integer Category ID
+ @param cur cursor Category cursor
+ */
+ public function updCategory($id, $cur)
+ {
+ if (!$this->core->auth->check('categories', $this->id)) {
+ throw new Exception(__('You are not allowed to update categories'));
+ }
+
+ if ($cur->cat_url == '') {
+ $url = [];
+ $rs = $this->categories()->getParents($id);
+ while ($rs->fetch()) {
+ if ($rs->index() == $rs->count() - 1) {
+ $url[] = $rs->cat_url;
+ }
+ }
+
+ $url[] = text::tidyURL($cur->cat_title, false);
+ $cur->cat_url = implode('/', $url);
+ }
+
+ $this->getCategoryCursor($cur, $id);
+
+ # --BEHAVIOR-- coreBeforeCategoryUpdate
+ $this->core->callBehavior('coreBeforeCategoryUpdate', $this, $cur);
+
+ $cur->update(
+ 'WHERE cat_id = ' . (integer) $id . ' ' .
+ "AND blog_id = '" . $this->con->escape($this->id) . "' ");
+
+ # --BEHAVIOR-- coreAfterCategoryUpdate
+ $this->core->callBehavior('coreAfterCategoryUpdate', $this, $cur);
+
+ $this->triggerBlog();
+ }
+
+ /**
+ Set category position
+
+ @param id integer Category ID
+ @param left integer Category ID before
+ @param right integer Category ID after
+ */
+ public function updCategoryPosition($id, $left, $right)
+ {
+ $this->categories()->updatePosition($id, $left, $right);
+ $this->triggerBlog();
+ }
+
+ /**
+ DEPRECATED METHOD. Use dcBlog::setCategoryParent and dcBlog::moveCategory
+ instead.
+
+ @param id integer Category ID
+ @param order integer Category position
+ */
+ public function updCategoryOrder($id, $order)
+ {
+ return;
+ }
+
+ /**
+ Set a category parent
+
+ @param id integer Category ID
+ @param parent integer Parent Category ID
+ */
+ public function setCategoryParent($id, $parent)
+ {
+ $this->categories()->setNodeParent($id, $parent);
+ $this->triggerBlog();
+ }
+
+ /**
+ Set category position
+
+ @param id integer Category ID
+ @param sibling integer Sibling Category ID
+ @param move integer Order (before|after)
+ */
+ public function setCategoryPosition($id, $sibling, $move)
+ {
+ $this->categories()->setNodePosition($id, $sibling, $move);
+ $this->triggerBlog();
+ }
+
+ /**
+ Deletes a category.
+
+ @param id integer Category ID
+ */
+ public function delCategory($id)
+ {
+ if (!$this->core->auth->check('categories', $this->id)) {
+ throw new Exception(__('You are not allowed to delete categories'));
+ }
+
+ $strReq = 'SELECT COUNT(post_id) AS nb_post ' .
+ 'FROM ' . $this->prefix . 'post ' .
+ 'WHERE cat_id = ' . (integer) $id . ' ' .
+ "AND blog_id = '" . $this->con->escape($this->id) . "' ";
+
+ $rs = $this->con->select($strReq);
+
+ if ($rs->nb_post > 0) {
+ throw new Exception(__('This category is not empty.'));
+ }
+
+ $this->categories()->deleteNode($id, true);
+ $this->triggerBlog();
+ }
+
+ /**
+ Reset categories order and relocate them to first level
+ */
+ public function resetCategoriesOrder()
+ {
+ if (!$this->core->auth->check('categories', $this->id)) {
+ throw new Exception(__('You are not allowed to reset categories order'));
+ }
+
+ $this->categories()->resetOrder();
+ $this->triggerBlog();
+ }
+
+ private function checkCategory($title, $url, $id = null)
+ {
+ # Let's check if URL is taken...
+ $strReq =
+ 'SELECT cat_url FROM ' . $this->prefix . 'category ' .
+ "WHERE cat_url = '" . $this->con->escape($url) . "' " .
+ ($id ? 'AND cat_id <> ' . (integer) $id . ' ' : '') .
+ "AND blog_id = '" . $this->con->escape($this->id) . "' " .
+ 'ORDER BY cat_url DESC';
+
+ $rs = $this->con->select($strReq);
+
+ if (!$rs->isEmpty()) {
+ if ($this->con->driver() == 'mysql' || $this->con->driver() == 'mysqli' || $this->con->driver() == 'mysqlimb4') {
+ $clause = "REGEXP '^" . $this->con->escape($url) . "[0-9]+$'";
+ } elseif ($this->con->driver() == 'pgsql') {
+ $clause = "~ '^" . $this->con->escape($url) . "[0-9]+$'";
+ } else {
+ $clause = "LIKE '" . $this->con->escape($url) . "%'";
+ }
+ $strReq =
+ 'SELECT cat_url FROM ' . $this->prefix . 'category ' .
+ "WHERE cat_url " . $clause . ' ' .
+ ($id ? 'AND cat_id <> ' . (integer) $id . ' ' : '') .
+ "AND blog_id = '" . $this->con->escape($this->id) . "' " .
+ 'ORDER BY cat_url DESC ';
+
+ $rs = $this->con->select($strReq);
+ $a = [];
+ while ($rs->fetch()) {
+ $a[] = $rs->cat_url;
+ }
+
+ natsort($a);
+ $t_url = end($a);
+
+ if (preg_match('/(.*?)([0-9]+)$/', $t_url, $m)) {
+ $i = (integer) $m[2];
+ $url = $m[1];
+ } else {
+ $i = 1;
+ }
+
+ return $url . ($i + 1);
+ }
+
+ # URL is empty?
+ if ($url == '') {
+ throw new Exception(__('Empty category URL'));
+ }
+
+ return $url;
+ }
+
+ private function getCategoryCursor($cur, $id = null)
+ {
+ if ($cur->cat_title == '') {
+ throw new Exception(__('You must provide a category title'));
+ }
+
+ # If we don't have any cat_url, let's do one
+ if ($cur->cat_url == '') {
+ $cur->cat_url = text::tidyURL($cur->cat_title, false);
+ }
+
+ # Still empty ?
+ if ($cur->cat_url == '') {
+ throw new Exception(__('You must provide a category URL'));
+ } else {
+ $cur->cat_url = text::tidyURL($cur->cat_url, true);
+ }
+
+ # Check if title or url are unique
+ $cur->cat_url = $this->checkCategory($cur->cat_title, $cur->cat_url, $id);
+
+ if ($cur->cat_desc !== null) {
+ $cur->cat_desc = $this->core->HTMLfilter($cur->cat_desc);
+ }
+ }
+ //@}
+
+ /// @name Entries management methods
+ //@{
+ /**
+ Retrieves entries. $params is an array taking the following
+ optionnal parameters:
+
+ - no_content: Don't retrieve entry content (excerpt and content)
+ - post_type: Get only entries with given type (default "post", array for many types and '' for no type)
+ - post_id: (integer or array) Get entry with given post_id
+ - post_url: Get entry with given post_url field
+ - user_id: (integer) Get entries belonging to given user ID
+ - cat_id: (string or array) Get entries belonging to given category ID
+ - cat_id_not: deprecated (use cat_id with "id ?not" instead)
+ - cat_url: (string or array) Get entries belonging to given category URL
+ - cat_url_not: deprecated (use cat_url with "url ?not" instead)
+ - post_status: (integer) Get entries with given post_status
+ - post_selected: (boolean) Get select flaged entries
+ - post_year: (integer) Get entries with given year
+ - post_month: (integer) Get entries with given month
+ - post_day: (integer) Get entries with given day
+ - post_lang: Get entries with given language code
+ - search: Get entries corresponding of the following search string
+ - columns: (array) More columns to retrieve
+ - sql: Append SQL string at the end of the query
+ - from: Append SQL string after "FROM" statement in query
+ - order: Order of results (default "ORDER BY post_dt DES")
+ - limit: Limit parameter
+ - sql_only : return the sql request instead of results. Only ids are selected
+ - exclude_post_id : (integer or array) Exclude entries with given post_id
+
+ Please note that on every cat_id or cat_url, you can add ?not to exclude
+ the category and ?sub to get subcategories.
+
+ @param params array Parameters
+ @param count_only boolean Only counts results
+ @return record A record with some more capabilities or the SQL request
+ */
+ public function getPosts($params = [], $count_only = false)
+ {
+ # --BEHAVIOR-- coreBlogBeforeGetPosts
+ $params = new ArrayObject($params);
+ $this->core->callBehavior('coreBlogBeforeGetPosts', $params);
+
+ if ($count_only) {
+ $strReq = 'SELECT count(DISTINCT P.post_id) ';
+ } elseif (!empty($params['sql_only'])) {
+ $strReq = 'SELECT P.post_id ';
+ } else {
+ if (!empty($params['no_content'])) {
+ $content_req = '';
+ } else {
+ $content_req =
+ 'post_excerpt, post_excerpt_xhtml, ' .
+ 'post_content, post_content_xhtml, post_notes, ';
+ }
+
+ if (!empty($params['columns']) && is_array($params['columns'])) {
+ $content_req .= implode(', ', $params['columns']) . ', ';
+ }
+
+ $strReq =
+ 'SELECT P.post_id, P.blog_id, P.user_id, P.cat_id, post_dt, ' .
+ 'post_tz, post_creadt, post_upddt, post_format, post_password, ' .
+ 'post_url, post_lang, post_title, ' . $content_req .
+ 'post_type, post_meta, ' .
+ 'post_status, post_firstpub, post_selected, post_position, ' .
+ 'post_open_comment, post_open_tb, nb_comment, nb_trackback, ' .
+ 'U.user_name, U.user_firstname, U.user_displayname, U.user_email, ' .
+ 'U.user_url, ' .
+ 'C.cat_title, C.cat_url, C.cat_desc ';
+ }
+
+ $strReq .=
+ 'FROM ' . $this->prefix . 'post P ' .
+ 'INNER JOIN ' . $this->prefix . 'user U ON U.user_id = P.user_id ' .
+ 'LEFT OUTER JOIN ' . $this->prefix . 'category C ON P.cat_id = C.cat_id ';
+
+ if (!empty($params['from'])) {
+ $strReq .= $params['from'] . ' ';
+ }
+
+ $strReq .=
+ "WHERE P.blog_id = '" . $this->con->escape($this->id) . "' ";
+
+ if (!$this->core->auth->check('contentadmin', $this->id)) {
+ $strReq .= 'AND ((post_status = 1 ';
+
+ if ($this->without_password) {
+ $strReq .= 'AND post_password IS NULL ';
+ }
+ $strReq .= ') ';
+
+ if ($this->core->auth->userID()) {
+ $strReq .= "OR P.user_id = '" . $this->con->escape($this->core->auth->userID()) . "')";
+ } else {
+ $strReq .= ') ';
+ }
+ }
+
+ #Adding parameters
+ if (isset($params['post_type'])) {
+ if (is_array($params['post_type']) || $params['post_type'] != '') {
+ $strReq .= 'AND post_type ' . $this->con->in($params['post_type']);
+ }
+ } else {
+ $strReq .= "AND post_type = 'post' ";
+ }
+
+ if (isset($params['post_id']) && $params['post_id'] !== '') {
+ if (is_array($params['post_id'])) {
+ array_walk($params['post_id'], function (&$v, $k) {if ($v !== null) {$v = (integer) $v;}});
+ } else {
+ $params['post_id'] = [(integer) $params['post_id']];
+ }
+ $strReq .= 'AND P.post_id ' . $this->con->in($params['post_id']);
+ }
+
+ if (isset($params['exclude_post_id']) && $params['exclude_post_id'] !== '') {
+ if (is_array($params['exclude_post_id'])) {
+ array_walk($params['exclude_post_id'], function (&$v, $k) {if ($v !== null) {$v = (integer) $v;}});
+ } else {
+ $params['exclude_post_id'] = [(integer) $params['exclude_post_id']];
+ }
+ $strReq .= 'AND P.post_id NOT ' . $this->con->in($params['exclude_post_id']);
+ }
+
+ if (isset($params['post_url']) && $params['post_url'] !== '') {
+ $strReq .= "AND post_url = '" . $this->con->escape($params['post_url']) . "' ";
+ }
+
+ if (!empty($params['user_id'])) {
+ $strReq .= "AND U.user_id = '" . $this->con->escape($params['user_id']) . "' ";
+ }
+
+ if (isset($params['cat_id']) && $params['cat_id'] !== '') {
+ if (!is_array($params['cat_id'])) {
+ $params['cat_id'] = [$params['cat_id']];
+ }
+ if (!empty($params['cat_id_not'])) {
+ array_walk($params['cat_id'], function (&$v, $k) {$v = $v . " ?not";});
+ }
+ $strReq .= 'AND ' . $this->getPostsCategoryFilter($params['cat_id'], 'cat_id') . ' ';
+ } elseif (isset($params['cat_url']) && $params['cat_url'] !== '') {
+ if (!is_array($params['cat_url'])) {
+ $params['cat_url'] = [$params['cat_url']];
+ }
+ if (!empty($params['cat_url_not'])) {
+ array_walk($params['cat_url'], function (&$v, $k) {$v = $v . " ?not";});
+ }
+ $strReq .= 'AND ' . $this->getPostsCategoryFilter($params['cat_url'], 'cat_url') . ' ';
+ }
+
+ /* Other filters */
+ if (isset($params['post_status'])) {
+ $strReq .= 'AND post_status = ' . (integer) $params['post_status'] . ' ';
+ }
+
+ if (isset($params['post_firstpub'])) {
+ $strReq .= 'AND post_firstpub = ' . (integer) $params['post_firstpub'] . ' ';
+ }
+
+ if (isset($params['post_selected'])) {
+ $strReq .= 'AND post_selected = ' . (integer) $params['post_selected'] . ' ';
+ }
+
+ if (!empty($params['post_year'])) {
+ $strReq .= 'AND ' . $this->con->dateFormat('post_dt', '%Y') . ' = ' .
+ "'" . sprintf('%04d', $params['post_year']) . "' ";
+ }
+
+ if (!empty($params['post_month'])) {
+ $strReq .= 'AND ' . $this->con->dateFormat('post_dt', '%m') . ' = ' .
+ "'" . sprintf('%02d', $params['post_month']) . "' ";
+ }
+
+ if (!empty($params['post_day'])) {
+ $strReq .= 'AND ' . $this->con->dateFormat('post_dt', '%d') . ' = ' .
+ "'" . sprintf('%02d', $params['post_day']) . "' ";
+ }
+
+ if (!empty($params['post_lang'])) {
+ $strReq .= "AND P.post_lang = '" . $this->con->escape($params['post_lang']) . "' ";
+ }
+
+ if (!empty($params['search'])) {
+ $words = text::splitWords($params['search']);
+
+ if (!empty($words)) {
+ # --BEHAVIOR-- corePostSearch
+ if ($this->core->hasBehavior('corePostSearch')) {
+ $this->core->callBehavior('corePostSearch', $this->core, [&$words, &$strReq, &$params]);
+ }
+
+ if ($words) {
+ foreach ($words as $i => $w) {
+ $words[$i] = "post_words LIKE '%" . $this->con->escape($w) . "%'";
+ }
+ $strReq .= 'AND ' . implode(' AND ', $words) . ' ';
+ }
+ }
+ }
+
+ if (isset($params['media'])) {
+ if ($params['media'] == '0') {
+ $strReq .= 'AND NOT ';
+ } else {
+ $strReq .= 'AND ';
+ }
+ $strReq .= 'EXISTS (SELECT M.post_id FROM ' . $this->prefix . 'post_media M ' .
+ 'WHERE M.post_id = P.post_id ';
+ if (isset($params['link_type'])) {
+ $strReq .= " AND M.link_type " . $this->con->in($params['link_type']) . " ";
+ }
+ $strReq .= ")";
+ }
+
+ if (!empty($params['where'])) {
+ $strReq .= $params['where'] . ' ';
+ }
+
+ if (!empty($params['sql'])) {
+ $strReq .= $params['sql'] . ' ';
+ }
+
+ if (!$count_only) {
+ if (!empty($params['order'])) {
+ $strReq .= 'ORDER BY ' . $this->con->escape($params['order']) . ' ';
+ } else {
+ $strReq .= 'ORDER BY post_dt DESC ';
+ }
+ }
+
+ if (!$count_only && !empty($params['limit'])) {
+ $strReq .= $this->con->limit($params['limit']);
+ }
+
+ if (!empty($params['sql_only'])) {
+ return $strReq;
+ }
+
+ $rs = $this->con->select($strReq);
+ $rs->core = $this->core;
+ $rs->_nb_media = [];
+ $rs->extend('rsExtPost');
+
+ # --BEHAVIOR-- coreBlogGetPosts
+ $this->core->callBehavior('coreBlogGetPosts', $rs);
+
+ # --BEHAVIOR-- coreBlogAfterGetPosts
+ $alt = new arrayObject(['rs' => null, 'params' => $params, 'count_only' => $count_only]);
+ $this->core->callBehavior('coreBlogAfterGetPosts', $rs, $alt);
+ if ($alt['rs'] instanceof record) {
+ $rs = $alt['rs'];
+ }
+
+ return $rs;
+ }
+
+ /**
+ Returns a record with post id, title and date for next or previous post
+ according to the post ID.
+ $dir could be 1 (next post) or -1 (previous post).
+
+ @param post_id integer Post ID
+ @param dir integer Search direction
+ @param restrict_to_category boolean Restrict to post with same category
+ @param restrict_to_lang boolean Restrict to post with same lang
+ @return record
+ */
+ public function getNextPost($post, $dir, $restrict_to_category = false, $restrict_to_lang = false)
+ {
+ $dt = $post->post_dt;
+ $post_id = (integer) $post->post_id;
+
+ if ($dir > 0) {
+ $sign = '>';
+ $order = 'ASC';
+ } else {
+ $sign = '<';
+ $order = 'DESC';
+ }
+
+ $params['post_type'] = $post->post_type;
+ $params['limit'] = 1;
+ $params['order'] = 'post_dt ' . $order . ', P.post_id ' . $order;
+ $params['sql'] =
+ 'AND ( ' .
+ " (post_dt = '" . $this->con->escape($dt) . "' AND P.post_id " . $sign . " " . $post_id . ") " .
+ " OR post_dt " . $sign . " '" . $this->con->escape($dt) . "' " .
+ ') ';
+
+ if ($restrict_to_category) {
+ $params['sql'] .= $post->cat_id ? 'AND P.cat_id = ' . (integer) $post->cat_id . ' ' : 'AND P.cat_id IS NULL ';
+ }
+
+ if ($restrict_to_lang) {
+ $params['sql'] .= $post->post_lang ? 'AND P.post_lang = \'' . $this->con->escape($post->post_lang) . '\' ' : 'AND P.post_lang IS NULL ';
+ }
+
+ $rs = $this->getPosts($params);
+
+ if ($rs->isEmpty()) {
+ return;
+ }
+
+ return $rs;
+ }
+
+ /**
+ Retrieves different languages and post count on blog, based on post_lang
+ field. $params is an array taking the following optionnal
+ parameters:
+
+ - post_type: Get only entries with given type (default "post", '' for no type)
+ - lang: retrieve post count for selected lang
+ - order: order statement (default post_lang DESC)
+
+ @param params array Parameters
+ @return record
+ */
+ public function getLangs($params = [])
+ {
+ $strReq = 'SELECT COUNT(post_id) as nb_post, post_lang ' .
+ 'FROM ' . $this->prefix . 'post ' .
+ "WHERE blog_id = '" . $this->con->escape($this->id) . "' " .
+ "AND post_lang <> '' " .
+ "AND post_lang IS NOT NULL ";
+
+ if (!$this->core->auth->check('contentadmin', $this->id)) {
+ $strReq .= 'AND ((post_status = 1 ';
+
+ if ($this->without_password) {
+ $strReq .= 'AND post_password IS NULL ';
+ }
+ $strReq .= ') ';
+
+ if ($this->core->auth->userID()) {
+ $strReq .= "OR user_id = '" . $this->con->escape($this->core->auth->userID()) . "')";
+ } else {
+ $strReq .= ') ';
+ }
+ }
+
+ if (isset($params['post_type'])) {
+ if ($params['post_type'] != '') {
+ $strReq .= "AND post_type = '" . $this->con->escape($params['post_type']) . "' ";
+ }
+ } else {
+ $strReq .= "AND post_type = 'post' ";
+ }
+
+ if (isset($params['lang'])) {
+ $strReq .= "AND post_lang = '" . $this->con->escape($params['lang']) . "' ";
+ }
+
+ $strReq .= 'GROUP BY post_lang ';
+
+ $order = 'desc';
+ if (!empty($params['order']) && preg_match('/^(desc|asc)$/i', $params['order'])) {
+ $order = $params['order'];
+ }
+ $strReq .= 'ORDER BY post_lang ' . $order . ' ';
+
+ return $this->con->select($strReq);
+ }
+
+ /**
+ Returns a record with all distinct blog dates and post count.
+ $params is an array taking the following optionnal parameters:
+
+ - type: (day|month|year) Get days, months or years
+ - year: (integer) Get dates for given year
+ - month: (integer) Get dates for given month
+ - day: (integer) Get dates for given day
+ - cat_id: (integer) Category ID filter
+ - cat_url: Category URL filter
+ - post_lang: lang of the posts
+ - next: Get date following match
+ - previous: Get date before match
+ - order: Sort by date "ASC" or "DESC"
+
+ @param params array Parameters array
+ @return record
+ */
+ public function getDates($params = [])
+ {
+ $dt_f = '%Y-%m-%d';
+ $dt_fc = '%Y%m%d';
+ if (isset($params['type'])) {
+ if ($params['type'] == 'year') {
+ $dt_f = '%Y-01-01';
+ $dt_fc = '%Y0101';
+ } elseif ($params['type'] == 'month') {
+ $dt_f = '%Y-%m-01';
+ $dt_fc = '%Y%m01';
+ }
+ }
+ $dt_f .= ' 00:00:00';
+ $dt_fc .= '000000';
+
+ $cat_field = $catReq = $limit = '';
+
+ if (isset($params['cat_id']) && $params['cat_id'] !== '') {
+ $catReq = 'AND P.cat_id = ' . (integer) $params['cat_id'] . ' ';
+ $cat_field = ', C.cat_url ';
+ } elseif (isset($params['cat_url']) && $params['cat_url'] !== '') {
+ $catReq = "AND C.cat_url = '" . $this->con->escape($params['cat_url']) . "' ";
+ $cat_field = ', C.cat_url ';
+ }
+ if (!empty($params['post_lang'])) {
+ $catReq = 'AND P.post_lang = \'' . $params['post_lang'] . '\' ';
+ }
+
+ $strReq = 'SELECT DISTINCT(' . $this->con->dateFormat('post_dt', $dt_f) . ') AS dt ' .
+ $cat_field .
+ ',COUNT(P.post_id) AS nb_post ' .
+ 'FROM ' . $this->prefix . 'post P LEFT JOIN ' . $this->prefix . 'category C ' .
+ 'ON P.cat_id = C.cat_id ' .
+ "WHERE P.blog_id = '" . $this->con->escape($this->id) . "' " .
+ $catReq;
+
+ if (!$this->core->auth->check('contentadmin', $this->id)) {
+ $strReq .= 'AND ((post_status = 1 ';
+
+ if ($this->without_password) {
+ $strReq .= 'AND post_password IS NULL ';
+ }
+ $strReq .= ') ';
+
+ if ($this->core->auth->userID()) {
+ $strReq .= "OR P.user_id = '" . $this->con->escape($this->core->auth->userID()) . "')";
+ } else {
+ $strReq .= ') ';
+ }
+ }
+
+ if (!empty($params['post_type'])) {
+ $strReq .= "AND post_type " . $this->con->in($params['post_type']) . " ";
+ } else {
+ $strReq .= "AND post_type = 'post' ";
+ }
+
+ if (!empty($params['year'])) {
+ $strReq .= 'AND ' . $this->con->dateFormat('post_dt', '%Y') . " = '" . sprintf('%04d', $params['year']) . "' ";
+ }
+
+ if (!empty($params['month'])) {
+ $strReq .= 'AND ' . $this->con->dateFormat('post_dt', '%m') . " = '" . sprintf('%02d', $params['month']) . "' ";
+ }
+
+ if (!empty($params['day'])) {
+ $strReq .= 'AND ' . $this->con->dateFormat('post_dt', '%d') . " = '" . sprintf('%02d', $params['day']) . "' ";
+ }
+
+ # Get next or previous date
+ if (!empty($params['next']) || !empty($params['previous'])) {
+ if (!empty($params['next'])) {
+ $pdir = ' > ';
+ $params['order'] = 'asc';
+ $dt = $params['next'];
+ } else {
+ $pdir = ' < ';
+ $params['order'] = 'desc';
+ $dt = $params['previous'];
+ }
+
+ $dt = date('YmdHis', strtotime($dt));
+
+ $strReq .= 'AND ' . $this->con->dateFormat('post_dt', $dt_fc) . $pdir . "'" . $dt . "' ";
+ $limit = $this->con->limit(1);
+ }
+
+ $strReq .= 'GROUP BY dt ' . $cat_field;
+
+ $order = 'desc';
+ if (!empty($params['order']) && preg_match('/^(desc|asc)$/i', $params['order'])) {
+ $order = $params['order'];
+ }
+
+ $strReq .=
+ 'ORDER BY dt ' . $order . ' ' .
+ $limit;
+
+ $rs = $this->con->select($strReq);
+ $rs->extend('rsExtDates');
+ return $rs;
+ }
+
+ /**
+ Creates a new entry. Takes a cursor as input and returns the new entry
+ ID.
+
+ @param cur cursor Post cursor
+ @return integer New post ID
+ */
+ public function addPost($cur)
+ {
+ if (!$this->core->auth->check('usage,contentadmin', $this->id)) {
+ throw new Exception(__('You are not allowed to create an entry'));
+ }
+
+ $this->con->writeLock($this->prefix . 'post');
+ try
+ {
+ # Get ID
+ $rs = $this->con->select(
+ 'SELECT MAX(post_id) ' .
+ 'FROM ' . $this->prefix . 'post '
+ );
+
+ $cur->post_id = (integer) $rs->f(0) + 1;
+ $cur->blog_id = (string) $this->id;
+ $cur->post_creadt = date('Y-m-d H:i:s');
+ $cur->post_upddt = date('Y-m-d H:i:s');
+ $cur->post_tz = $this->core->auth->getInfo('user_tz');
+
+ # Post excerpt and content
+ $this->getPostContent($cur, $cur->post_id);
+
+ $this->getPostCursor($cur);
+
+ $cur->post_url = $this->getPostURL($cur->post_url, $cur->post_dt, $cur->post_title, $cur->post_id);
+
+ if (!$this->core->auth->check('publish,contentadmin', $this->id)) {
+ $cur->post_status = -2;
+ }
+
+ # --BEHAVIOR-- coreBeforePostCreate
+ $this->core->callBehavior('coreBeforePostCreate', $this, $cur);
+
+ $cur->insert();
+ $this->con->unlock();
+ } catch (Exception $e) {
+ $this->con->unlock();
+ throw $e;
+ }
+
+ # --BEHAVIOR-- coreAfterPostCreate
+ $this->core->callBehavior('coreAfterPostCreate', $this, $cur);
+
+ $this->triggerBlog();
+
+ $this->firstPublicationEntries($cur->post_id);
+
+ return $cur->post_id;
+ }
+
+ /**
+ Updates an existing post.
+
+ @param id integer Post ID
+ @param cur cursor Post cursor
+ */
+ public function updPost($id, $cur)
+ {
+ if (!$this->core->auth->check('usage,contentadmin', $this->id)) {
+ throw new Exception(__('You are not allowed to update entries'));
+ }
+
+ $id = (integer) $id;
+
+ if (empty($id)) {
+ throw new Exception(__('No such entry ID'));
+ }
+
+ # Post excerpt and content
+ $this->getPostContent($cur, $id);
+
+ $this->getPostCursor($cur);
+
+ if ($cur->post_url !== null) {
+ $cur->post_url = $this->getPostURL($cur->post_url, $cur->post_dt, $cur->post_title, $id);
+ }
+
+ if (!$this->core->auth->check('publish,contentadmin', $this->id)) {
+ $cur->unsetField('post_status');
+ }
+
+ $cur->post_upddt = date('Y-m-d H:i:s');
+
+ #If user is only "usage", we need to check the post's owner
+ if (!$this->core->auth->check('contentadmin', $this->id)) {
+ $strReq = 'SELECT post_id ' .
+ 'FROM ' . $this->prefix . 'post ' .
+ 'WHERE post_id = ' . $id . ' ' .
+ "AND user_id = '" . $this->con->escape($this->core->auth->userID()) . "' ";
+
+ $rs = $this->con->select($strReq);
+
+ if ($rs->isEmpty()) {
+ throw new Exception(__('You are not allowed to edit this entry'));
+ }
+ }
+
+ # --BEHAVIOR-- coreBeforePostUpdate
+ $this->core->callBehavior('coreBeforePostUpdate', $this, $cur);
+
+ $cur->update('WHERE post_id = ' . $id . ' ');
+
+ # --BEHAVIOR-- coreAfterPostUpdate
+ $this->core->callBehavior('coreAfterPostUpdate', $this, $cur);
+
+ $this->triggerBlog();
+
+ $this->firstPublicationEntries($id);
+ }
+
+ /**
+ Updates post status.
+
+ @param id integer Post ID
+ @param status integer Post status
+ */
+ public function updPostStatus($id, $status)
+ {
+ $this->updPostsStatus($id, $status);
+ }
+
+ /**
+ Updates posts status.
+
+ @param ids mixed Post(s) ID(s)
+ @param status integer Post status
+ */
+ public function updPostsStatus($ids, $status)
+ {
+ if (!$this->core->auth->check('publish,contentadmin', $this->id)) {
+ throw new Exception(__('You are not allowed to change this entry status'));
+ }
+
+ $posts_ids = dcUtils::cleanIds($ids);
+ $status = (integer) $status;
+
+ $strReq = "WHERE blog_id = '" . $this->con->escape($this->id) . "' " .
+ "AND post_id " . $this->con->in($posts_ids);
+
+ #If user can only publish, we need to check the post's owner
+ if (!$this->core->auth->check('contentadmin', $this->id)) {
+ $strReq .= "AND user_id = '" . $this->con->escape($this->core->auth->userID()) . "' ";
+ }
+
+ $cur = $this->con->openCursor($this->prefix . 'post');
+
+ $cur->post_status = $status;
+ $cur->post_upddt = date('Y-m-d H:i:s');
+
+ $cur->update($strReq);
+ $this->triggerBlog();
+
+ $this->firstPublicationEntries($posts_ids);
+ }
+
+ /**
+ Updates post selection.
+
+ @param id integer Post ID
+ @param selected integer Is selected post
+ */
+ public function updPostSelected($id, $selected)
+ {
+ $this->updPostsSelected($id, $selected);
+ }
+
+ /**
+ Updates posts selection.
+
+ @param ids mixed Post(s) ID(s)
+ @param selected integer Is selected post(s)
+ */
+ public function updPostsSelected($ids, $selected)
+ {
+ if (!$this->core->auth->check('usage,contentadmin', $this->id)) {
+ throw new Exception(__('You are not allowed to change this entry category'));
+ }
+
+ $posts_ids = dcUtils::cleanIds($ids);
+ $selected = (boolean) $selected;
+
+ $strReq = "WHERE blog_id = '" . $this->con->escape($this->id) . "' " .
+ "AND post_id " . $this->con->in($posts_ids);
+
+ # If user is only usage, we need to check the post's owner
+ if (!$this->core->auth->check('contentadmin', $this->id)) {
+ $strReq .= "AND user_id = '" . $this->con->escape($this->core->auth->userID()) . "' ";
+ }
+
+ $cur = $this->con->openCursor($this->prefix . 'post');
+
+ $cur->post_selected = (integer) $selected;
+ $cur->post_upddt = date('Y-m-d H:i:s');
+
+ $cur->update($strReq);
+ $this->triggerBlog();
+ }
+
+ /**
+ Updates post category. $cat_id can be null.
+
+ @param id integer Post ID
+ @param cat_id integer Category ID
+ */
+ public function updPostCategory($id, $cat_id)
+ {
+ $this->updPostsCategory($id, $cat_id);
+ }
+
+ /**
+ Updates posts category. $cat_id can be null.
+
+ @param ids mixed Post(s) ID(s)
+ @param cat_id integer Category ID
+ */
+ public function updPostsCategory($ids, $cat_id)
+ {
+ if (!$this->core->auth->check('usage,contentadmin', $this->id)) {
+ throw new Exception(__('You are not allowed to change this entry category'));
+ }
+
+ $posts_ids = dcUtils::cleanIds($ids);
+ $cat_id = (integer) $cat_id;
+
+ $strReq = "WHERE blog_id = '" . $this->con->escape($this->id) . "' " .
+ "AND post_id " . $this->con->in($posts_ids);
+
+ # If user is only usage, we need to check the post's owner
+ if (!$this->core->auth->check('contentadmin', $this->id)) {
+ $strReq .= "AND user_id = '" . $this->con->escape($this->core->auth->userID()) . "' ";
+ }
+
+ $cur = $this->con->openCursor($this->prefix . 'post');
+
+ $cur->cat_id = ($cat_id ?: null);
+ $cur->post_upddt = date('Y-m-d H:i:s');
+
+ $cur->update($strReq);
+ $this->triggerBlog();
+ }
+
+ /**
+ Updates posts category. $new_cat_id can be null.
+
+ @param old_cat_id integer Old category ID
+ @param new_cat_id integer New category ID
+ */
+ public function changePostsCategory($old_cat_id, $new_cat_id)
+ {
+ if (!$this->core->auth->check('contentadmin,categories', $this->id)) {
+ throw new Exception(__('You are not allowed to change entries category'));
+ }
+
+ $old_cat_id = (integer) $old_cat_id;
+ $new_cat_id = (integer) $new_cat_id;
+
+ $cur = $this->con->openCursor($this->prefix . 'post');
+
+ $cur->cat_id = ($new_cat_id ?: null);
+ $cur->post_upddt = date('Y-m-d H:i:s');
+
+ $cur->update(
+ 'WHERE cat_id = ' . $old_cat_id . ' ' .
+ "AND blog_id = '" . $this->con->escape($this->id) . "' "
+ );
+ $this->triggerBlog();
+ }
+
+ /**
+ Deletes a post.
+
+ @param id integer Post ID
+ */
+ public function delPost($id)
+ {
+ $this->delPosts($id);
+ }
+
+ /**
+ Deletes multiple posts.
+
+ @param ids mixed Post(s) ID(s)
+ */
+ public function delPosts($ids)
+ {
+ if (!$this->core->auth->check('delete,contentadmin', $this->id)) {
+ throw new Exception(__('You are not allowed to delete entries'));
+ }
+
+ $posts_ids = dcUtils::cleanIds($ids);
+
+ if (empty($posts_ids)) {
+ throw new Exception(__('No such entry ID'));
+ }
+
+ $strReq = 'DELETE FROM ' . $this->prefix . 'post ' .
+ "WHERE blog_id = '" . $this->con->escape($this->id) . "' " .
+ "AND post_id " . $this->con->in($posts_ids);
+
+ #If user can only delete, we need to check the post's owner
+ if (!$this->core->auth->check('contentadmin', $this->id)) {
+ $strReq .= "AND user_id = '" . $this->con->escape($this->core->auth->userID()) . "' ";
+ }
+
+ $this->con->execute($strReq);
+ $this->triggerBlog();
+ }
+
+ /**
+ Publishes all entries flaged as "scheduled".
+ */
+ public function publishScheduledEntries()
+ {
+ $strReq = 'SELECT post_id, post_dt, post_tz ' .
+ 'FROM ' . $this->prefix . 'post ' .
+ 'WHERE post_status = -1 ' .
+ "AND blog_id = '" . $this->con->escape($this->id) . "' ";
+
+ $rs = $this->con->select($strReq);
+
+ $now = dt::toUTC(time());
+ $to_change = new ArrayObject();
+
+ if ($rs->isEmpty()) {
+ return;
+ }
+
+ while ($rs->fetch()) {
+ # Now timestamp with post timezone
+ $now_tz = $now + dt::getTimeOffset($rs->post_tz, $now);
+
+ # Post timestamp
+ $post_ts = strtotime($rs->post_dt);
+
+ # If now_tz >= post_ts, we publish the entry
+ if ($now_tz >= $post_ts) {
+ $to_change[] = (integer) $rs->post_id;
+ }
+ }
+ if (count($to_change)) {
+ # --BEHAVIOR-- coreBeforeScheduledEntriesPublish
+ $this->core->callBehavior('coreBeforeScheduledEntriesPublish', $this, $to_change);
+
+ $strReq =
+ 'UPDATE ' . $this->prefix . 'post SET ' .
+ 'post_status = 1 ' .
+ "WHERE blog_id = '" . $this->con->escape($this->id) . "' " .
+ 'AND post_id ' . $this->con->in((array) $to_change) . ' ';
+ $this->con->execute($strReq);
+ $this->triggerBlog();
+
+ # --BEHAVIOR-- coreAfterScheduledEntriesPublish
+ $this->core->callBehavior('coreAfterScheduledEntriesPublish', $this, $to_change);
+
+ $this->firstPublicationEntries($to_change);
+ }
+ }
+
+ /**
+ First publication mecanism (on post create, update, publish, status)
+
+ @param ids mixed Post(s) ID(s)
+ */
+ public function firstPublicationEntries($ids)
+ {
+ $posts = $this->getPosts([
+ 'post_id' => dcUtils::cleanIds($ids),
+ 'post_status' => 1,
+ 'post_firstpub' => 0
+ ]);
+
+ $to_change = [];
+ while ($posts->fetch()) {
+
+ $to_change[] = $posts->post_id;
+ }
+
+ if (count($to_change)) {
+
+ $strReq =
+ 'UPDATE ' . $this->prefix . 'post ' .
+ 'SET post_firstpub = 1 ' .
+ "WHERE blog_id = '" . $this->con->escape($this->id) . "' " .
+ 'AND post_id ' . $this->con->in((array) $to_change) . ' ';
+ $this->con->execute($strReq);
+
+ # --BEHAVIOR-- coreFirstPublicationEntries
+ $this->core->callBehavior('coreFirstPublicationEntries', $this, $to_change);
+ }
+ }
+
+ /**
+ Retrieves all users having posts on current blog.
+
+ @param post_type string post_type filter (post)
+ @return record
+ */
+ public function getPostsUsers($post_type = 'post')
+ {
+ $strReq = 'SELECT P.user_id, user_name, user_firstname, ' .
+ 'user_displayname, user_email ' .
+ 'FROM ' . $this->prefix . 'post P, ' . $this->prefix . 'user U ' .
+ 'WHERE P.user_id = U.user_id ' .
+ "AND blog_id = '" . $this->con->escape($this->id) . "' ";
+
+ if ($post_type) {
+ $strReq .= "AND post_type = '" . $this->con->escape($post_type) . "' ";
+ }
+
+ $strReq .= 'GROUP BY P.user_id, user_name, user_firstname, user_displayname, user_email ';
+
+ return $this->con->select($strReq);
+ }
+
+ private function getPostsCategoryFilter($arr, $field = 'cat_id')
+ {
+ $field = $field == 'cat_id' ? 'cat_id' : 'cat_url';
+
+ $sub = [];
+ $not = [];
+ $queries = [];
+
+ foreach ($arr as $v) {
+ $v = trim($v);
+ $args = preg_split('/\s*[?]\s*/', $v, -1, PREG_SPLIT_NO_EMPTY);
+ $id = array_shift($args);
+ $args = array_flip($args);
+
+ if (isset($args['not'])) {$not[$id] = 1;}
+ if (isset($args['sub'])) {$sub[$id] = 1;}
+ if ($field == 'cat_id') {
+ if (preg_match('/^null$/i', $id)) {
+ $queries[$id] = 'P.cat_id IS NULL';
+ } else {
+ $queries[$id] = 'P.cat_id = ' . (integer) $id;
+ }
+ } else {
+ $queries[$id] = "C.cat_url = '" . $this->con->escape($id) . "' ";
+ }
+ }
+
+ if (!empty($sub)) {
+ $rs = $this->con->select(
+ 'SELECT cat_id, cat_url, cat_lft, cat_rgt FROM ' . $this->prefix . 'category ' .
+ "WHERE blog_id = '" . $this->con->escape($this->id) . "' " .
+ 'AND ' . $field . ' ' . $this->con->in(array_keys($sub))
+ );
+
+ while ($rs->fetch()) {
+ $queries[$rs->f($field)] = '(C.cat_lft BETWEEN ' . $rs->cat_lft . ' AND ' . $rs->cat_rgt . ')';
+ }
+ }
+
+ # Create queries
+ $sql = [
+ 0 => [], # wanted categories
+ 1 => [] # excluded categories
+ ];
+
+ foreach ($queries as $id => $q) {
+ $sql[(integer) isset($not[$id])][] = $q;
+ }
+
+ $sql[0] = implode(' OR ', $sql[0]);
+ $sql[1] = implode(' OR ', $sql[1]);
+
+ if ($sql[0]) {
+ $sql[0] = '(' . $sql[0] . ')';
+ } else {
+ unset($sql[0]);
+ }
+
+ if ($sql[1]) {
+ $sql[1] = '(P.cat_id IS NULL OR NOT(' . $sql[1] . '))';
+ } else {
+ unset($sql[1]);
+ }
+
+ return implode(' AND ', $sql);
+ }
+
+ private function getPostCursor($cur, $post_id = null)
+ {
+ if ($cur->post_title == '') {
+ throw new Exception(__('No entry title'));
+ }
+
+ if ($cur->post_content == '') {
+ throw new Exception(__('No entry content'));
+ }
+
+ if ($cur->post_password === '') {
+ $cur->post_password = null;
+ }
+
+ if ($cur->post_dt == '') {
+ $offset = dt::getTimeOffset($this->core->auth->getInfo('user_tz'));
+ $now = time() + $offset;
+ $cur->post_dt = date('Y-m-d H:i:00', $now);
+ }
+
+ $post_id = is_int($post_id) ? $post_id : $cur->post_id;
+
+ if ($cur->post_content_xhtml == '') {
+ throw new Exception(__('No entry content'));
+ }
+
+ # Words list
+ if ($cur->post_title !== null && $cur->post_excerpt_xhtml !== null
+ && $cur->post_content_xhtml !== null) {
+ $words =
+ $cur->post_title . ' ' .
+ $cur->post_excerpt_xhtml . ' ' .
+ $cur->post_content_xhtml;
+
+ $cur->post_words = implode(' ', text::splitWords($words));
+ }
+
+ if ($cur->isField('post_firstpub')) {
+ $cur->unsetField('post_firstpub');
+ }
+ }
+
+ private function getPostContent($cur, $post_id)
+ {
+ $post_excerpt = $cur->post_excerpt;
+ $post_excerpt_xhtml = $cur->post_excerpt_xhtml;
+ $post_content = $cur->post_content;
+ $post_content_xhtml = $cur->post_content_xhtml;
+
+ $this->setPostContent(
+ $post_id, $cur->post_format, $cur->post_lang,
+ $post_excerpt, $post_excerpt_xhtml,
+ $post_content, $post_content_xhtml
+ );
+
+ $cur->post_excerpt = $post_excerpt;
+ $cur->post_excerpt_xhtml = $post_excerpt_xhtml;
+ $cur->post_content = $post_content;
+ $cur->post_content_xhtml = $post_content_xhtml;
+ }
+
+ /**
+ Creates post HTML content, taking format and lang into account.
+
+ @param post_id integer Post ID
+ @param format string Post format
+ @param lang string Post lang
+ @param excerpt string Post excerpt
+ @param[out] excerpt_xhtml string Post excerpt HTML
+ @param content string Post content
+ @param[out] content_xhtml string Post content HTML
+ */
+ public function setPostContent($post_id, $format, $lang, &$excerpt, &$excerpt_xhtml, &$content, &$content_xhtml)
+ {
+ if ($format == 'wiki') {
+ $this->core->initWikiPost();
+ $this->core->wiki2xhtml->setOpt('note_prefix', 'pnote-' . $post_id);
+ switch ($this->settings->system->note_title_tag) {
+ case 1:
+ $tag = 'h3';
+ break;
+ case 2:
+ $tag = 'p';
+ break;
+ default:
+ $tag = 'h4';
+ break;
+ }
+ $this->core->wiki2xhtml->setOpt('note_str', '');
+ $this->core->wiki2xhtml->setOpt('note_str_single', '');
+ if (strpos($lang, 'fr') === 0) {
+ $this->core->wiki2xhtml->setOpt('active_fr_syntax', 1);
+ }
+ }
+
+ if ($excerpt) {
+ $excerpt_xhtml = $this->core->callFormater($format, $excerpt);
+ $excerpt_xhtml = $this->core->HTMLfilter($excerpt_xhtml);
+ } else {
+ $excerpt_xhtml = '';
+ }
+
+ if ($content) {
+ $content_xhtml = $this->core->callFormater($format, $content);
+ $content_xhtml = $this->core->HTMLfilter($content_xhtml);
+ } else {
+ $content_xhtml = '';
+ }
+
+ # --BEHAVIOR-- coreAfterPostContentFormat
+ $this->core->callBehavior('coreAfterPostContentFormat', [
+ 'excerpt' => &$excerpt,
+ 'content' => &$content,
+ 'excerpt_xhtml' => &$excerpt_xhtml,
+ 'content_xhtml' => &$content_xhtml
+ ]);
+ }
+
+ /**
+ Returns URL for a post according to blog setting post_url_format .
+ It will try to guess URL and append some figures if needed.
+
+ @param url string Origin URL, could be empty
+ @param post_dt string Post date (in YYYY-MM-DD HH:mm:ss)
+ @param post_title string Post title
+ @param post_id integer Post ID
+ @return string result URL
+ */
+ public function getPostURL($url, $post_dt, $post_title, $post_id)
+ {
+ $url = trim($url);
+
+ $url_patterns = [
+ '{y}' => date('Y', strtotime($post_dt)),
+ '{m}' => date('m', strtotime($post_dt)),
+ '{d}' => date('d', strtotime($post_dt)),
+ '{t}' => text::tidyURL($post_title),
+ '{id}' => (integer) $post_id
+ ];
+
+ # If URL is empty, we create a new one
+ if ($url == '') {
+ # Transform with format
+ $url = str_replace(
+ array_keys($url_patterns),
+ array_values($url_patterns),
+ $this->settings->system->post_url_format
+ );
+ } else {
+ $url = text::tidyURL($url);
+ }
+
+ # Let's check if URL is taken...
+ $strReq = 'SELECT post_url FROM ' . $this->prefix . 'post ' .
+ "WHERE post_url = '" . $this->con->escape($url) . "' " .
+ 'AND post_id <> ' . (integer) $post_id . ' ' .
+ "AND blog_id = '" . $this->con->escape($this->id) . "' " .
+ 'ORDER BY post_url DESC';
+
+ $rs = $this->con->select($strReq);
+
+ if (!$rs->isEmpty()) {
+ if ($this->con->driver() == 'mysql' || $this->con->driver() == 'mysqli' || $this->con->driver() == 'mysqlimb4') {
+ $clause = "REGEXP '^" . $this->con->escape(preg_quote($url)) . "[0-9]+$'";
+ } elseif ($this->con->driver() == 'pgsql') {
+ $clause = "~ '^" . $this->con->escape(preg_quote($url)) . "[0-9]+$'";
+ } else {
+ $clause = "LIKE '" .
+ $this->con->escape(preg_replace(['%', '_', '!'], ['!%', '!_', '!!'], $url)) .
+ "%' ESCAPE '!'";
+ }
+ $strReq = 'SELECT post_url FROM ' . $this->prefix . 'post ' .
+ "WHERE post_url " . $clause . ' ' .
+ 'AND post_id <> ' . (integer) $post_id . ' ' .
+ "AND blog_id = '" . $this->con->escape($this->id) . "' " .
+ 'ORDER BY post_url DESC ';
+
+ $rs = $this->con->select($strReq);
+ $a = [];
+ while ($rs->fetch()) {
+ $a[] = $rs->post_url;
+ }
+
+ natsort($a);
+ $t_url = end($a);
+
+ if (preg_match('/(.*?)([0-9]+)$/', $t_url, $m)) {
+ $i = (integer) $m[2];
+ $url = $m[1];
+ } else {
+ $i = 1;
+ }
+
+ return $url . ($i + 1);
+ }
+
+ # URL is empty?
+ if ($url == '') {
+ throw new Exception(__('Empty entry URL'));
+ }
+
+ return $url;
+ }
+ //@}
+
+ /// @name Comments management methods
+ //@{
+ /**
+ Retrieves comments. $params is an array taking the following
+ optionnal parameters:
+
+ - no_content: Don't retrieve comment content
+ - post_type: Get only entries with given type (default no type, array for many types)
+ - post_id: (integer) Get comments belonging to given post_id
+ - cat_id: (integer or array) Get comments belonging to entries of given category ID
+ - comment_id: (integer or array) Get comment with given ID (or IDs)
+ - comment_site: (string) Get comments with given comment_site
+ - comment_status: (integer) Get comments with given comment_status
+ - comment_trackback: (integer) Get only comments (0) or trackbacks (1)
+ - comment_ip: (string) Get comments with given IP address
+ - post_url: Get entry with given post_url field
+ - user_id: (integer) Get entries belonging to given user ID
+ - q_author: Search comments by author
+ - sql: Append SQL string at the end of the query
+ - from: Append SQL string after "FROM" statement in query
+ - order: Order of results (default "ORDER BY comment_dt DES")
+ - limit: Limit parameter
+ - sql_only : return the sql request instead of results. Only ids are selected
+
+ @param params array Parameters
+ @param count_only boolean Only counts results
+ @return record A record with some more capabilities
+ */
+ public function getComments($params = [], $count_only = false)
+ {
+ if ($count_only) {
+ $strReq = 'SELECT count(comment_id) ';
+ } elseif (!empty($params['sql_only'])) {
+ $strReq = 'SELECT P.post_id ';
+ } else {
+ if (!empty($params['no_content'])) {
+ $content_req = '';
+ } else {
+ $content_req = 'comment_content, ';
+ }
+
+ if (!empty($params['columns']) && is_array($params['columns'])) {
+ $content_req .= implode(', ', $params['columns']) . ', ';
+ }
+
+ $strReq =
+ 'SELECT C.comment_id, comment_dt, comment_tz, comment_upddt, ' .
+ 'comment_author, comment_email, comment_site, ' .
+ $content_req . ' comment_trackback, comment_status, ' .
+ 'comment_spam_status, comment_spam_filter, comment_ip, ' .
+ 'P.post_title, P.post_url, P.post_id, P.post_password, P.post_type, ' .
+ 'P.post_dt, P.user_id, U.user_email, U.user_url ';
+ }
+
+ $strReq .=
+ 'FROM ' . $this->prefix . 'comment C ' .
+ 'INNER JOIN ' . $this->prefix . 'post P ON C.post_id = P.post_id ' .
+ 'INNER JOIN ' . $this->prefix . 'user U ON P.user_id = U.user_id ';
+
+ if (!empty($params['from'])) {
+ $strReq .= $params['from'] . ' ';
+ }
+
+ $strReq .=
+ "WHERE P.blog_id = '" . $this->con->escape($this->id) . "' ";
+
+ if (!$this->core->auth->check('contentadmin', $this->id)) {
+ $strReq .= 'AND ((comment_status = 1 AND P.post_status = 1 ';
+
+ if ($this->without_password) {
+ $strReq .= 'AND post_password IS NULL ';
+ }
+ $strReq .= ') ';
+
+ if ($this->core->auth->userID()) {
+ $strReq .= "OR P.user_id = '" . $this->con->escape($this->core->auth->userID()) . "')";
+ } else {
+ $strReq .= ') ';
+ }
+ }
+
+ if (!empty($params['post_type'])) {
+ $strReq .= 'AND post_type ' . $this->con->in($params['post_type']);
+ }
+
+ if (isset($params['post_id']) && $params['post_id'] !== '') {
+ $strReq .= 'AND P.post_id = ' . (integer) $params['post_id'] . ' ';
+ }
+
+ if (isset($params['cat_id']) && $params['cat_id'] !== '') {
+ $strReq .= 'AND P.cat_id = ' . (integer) $params['cat_id'] . ' ';
+ }
+
+ if (isset($params['comment_id']) && $params['comment_id'] !== '') {
+ if (is_array($params['comment_id'])) {
+ array_walk($params['comment_id'], function (&$v, $k) {if ($v !== null) {$v = (integer) $v;}});
+ } else {
+ $params['comment_id'] = [(integer) $params['comment_id']];
+ }
+ $strReq .= 'AND comment_id ' . $this->con->in($params['comment_id']);
+ }
+
+ if (isset($params['comment_email'])) {
+ $comment_email = $this->con->escape(str_replace('*', '%', $params['comment_email']));
+ $strReq .= "AND comment_email LIKE '" . $comment_email . "' ";
+ }
+
+ if (isset($params['comment_site'])) {
+ $comment_site = $this->con->escape(str_replace('*', '%', $params['comment_site']));
+ $strReq .= "AND comment_site LIKE '" . $comment_site . "' ";
+ }
+
+ if (isset($params['comment_status'])) {
+ $strReq .= 'AND comment_status = ' . (integer) $params['comment_status'] . ' ';
+ }
+
+ if (!empty($params['comment_status_not'])) {
+ $strReq .= 'AND comment_status <> ' . (integer) $params['comment_status_not'] . ' ';
+ }
+
+ if (isset($params['comment_trackback'])) {
+ $strReq .= 'AND comment_trackback = ' . (integer) (boolean) $params['comment_trackback'] . ' ';
+ }
+
+ if (isset($params['comment_ip'])) {
+ $comment_ip = $this->con->escape(str_replace('*', '%', $params['comment_ip']));
+ $strReq .= "AND comment_ip LIKE '" . $comment_ip . "' ";
+ }
+
+ if (isset($params['q_author'])) {
+ $q_author = $this->con->escape(str_replace('*', '%', strtolower($params['q_author'])));
+ $strReq .= "AND LOWER(comment_author) LIKE '" . $q_author . "' ";
+ }
+
+ if (!empty($params['search'])) {
+ $words = text::splitWords($params['search']);
+
+ if (!empty($words)) {
+ # --BEHAVIOR coreCommentSearch
+ if ($this->core->hasBehavior('coreCommentSearch')) {
+ $this->core->callBehavior('coreCommentSearch', $this->core, [&$words, &$strReq, &$params]);
+ }
+
+ if ($words) {
+ foreach ($words as $i => $w) {
+ $words[$i] = "comment_words LIKE '%" . $this->con->escape($w) . "%'";
+ }
+ $strReq .= 'AND ' . implode(' AND ', $words) . ' ';
+ }
+ }
+ }
+
+ if (!empty($params['sql'])) {
+ $strReq .= $params['sql'] . ' ';
+ }
+
+ if (!$count_only) {
+ if (!empty($params['order'])) {
+ $strReq .= 'ORDER BY ' . $this->con->escape($params['order']) . ' ';
+ } else {
+ $strReq .= 'ORDER BY comment_dt DESC ';
+ }
+ }
+
+ if (!$count_only && !empty($params['limit'])) {
+ $strReq .= $this->con->limit($params['limit']);
+ }
+
+ if (!empty($params['sql_only'])) {
+ return $strReq;
+ }
+
+ $rs = $this->con->select($strReq);
+ $rs->core = $this->core;
+ $rs->extend('rsExtComment');
+
+ # --BEHAVIOR-- coreBlogGetComments
+ $this->core->callBehavior('coreBlogGetComments', $rs);
+
+ return $rs;
+ }
+
+ /**
+ Creates a new comment. Takes a cursor as input and returns the new comment
+ ID.
+
+ @param cur cursor Comment cursor
+ @return integer New comment ID
+ */
+ public function addComment($cur)
+ {
+ $this->con->writeLock($this->prefix . 'comment');
+ try
+ {
+ # Get ID
+ $rs = $this->con->select(
+ 'SELECT MAX(comment_id) ' .
+ 'FROM ' . $this->prefix . 'comment '
+ );
+
+ $cur->comment_id = (integer) $rs->f(0) + 1;
+ $cur->comment_upddt = date('Y-m-d H:i:s');
+
+ $offset = dt::getTimeOffset($this->settings->system->blog_timezone);
+ $cur->comment_dt = date('Y-m-d H:i:s', time() + $offset);
+ $cur->comment_tz = $this->settings->system->blog_timezone;
+
+ $this->getCommentCursor($cur);
+
+ if ($cur->comment_ip === null) {
+ $cur->comment_ip = http::realIP();
+ }
+
+ # --BEHAVIOR-- coreBeforeCommentCreate
+ $this->core->callBehavior('coreBeforeCommentCreate', $this, $cur);
+
+ $cur->insert();
+ $this->con->unlock();
+ } catch (Exception $e) {
+ $this->con->unlock();
+ throw $e;
+ }
+
+ # --BEHAVIOR-- coreAfterCommentCreate
+ $this->core->callBehavior('coreAfterCommentCreate', $this, $cur);
+
+ $this->triggerComment($cur->comment_id);
+ if ($cur->comment_status != -2) {
+ $this->triggerBlog();
+ }
+ return $cur->comment_id;
+ }
+
+ /**
+ Updates an existing comment.
+
+ @param id integer Comment ID
+ @param cur cursor Comment cursor
+ */
+ public function updComment($id, $cur)
+ {
+ if (!$this->core->auth->check('usage,contentadmin', $this->id)) {
+ throw new Exception(__('You are not allowed to update comments'));
+ }
+
+ $id = (integer) $id;
+
+ if (empty($id)) {
+ throw new Exception(__('No such comment ID'));
+ }
+
+ $rs = $this->getComments(['comment_id' => $id]);
+
+ if ($rs->isEmpty()) {
+ throw new Exception(__('No such comment ID'));
+ }
+
+ #If user is only usage, we need to check the post's owner
+ if (!$this->core->auth->check('contentadmin', $this->id)) {
+ if ($rs->user_id != $this->core->auth->userID()) {
+ throw new Exception(__('You are not allowed to update this comment'));
+ }
+ }
+
+ $this->getCommentCursor($cur);
+
+ $cur->comment_upddt = date('Y-m-d H:i:s');
+
+ if (!$this->core->auth->check('publish,contentadmin', $this->id)) {
+ $cur->unsetField('comment_status');
+ }
+
+ # --BEHAVIOR-- coreBeforeCommentUpdate
+ $this->core->callBehavior('coreBeforeCommentUpdate', $this, $cur, $rs);
+
+ $cur->update('WHERE comment_id = ' . $id . ' ');
+
+ # --BEHAVIOR-- coreAfterCommentUpdate
+ $this->core->callBehavior('coreAfterCommentUpdate', $this, $cur, $rs);
+
+ $this->triggerComment($id);
+ $this->triggerBlog();
+ }
+
+ /**
+ Updates comment status.
+
+ @param id integer Comment ID
+ @param status integer Comment status
+ */
+ public function updCommentStatus($id, $status)
+ {
+ $this->updCommentsStatus($id, $status);
+ }
+
+ /**
+ Updates comments status.
+
+ @param ids mixed Comment(s) ID(s)
+ @param status integer Comment status
+ */
+ public function updCommentsStatus($ids, $status)
+ {
+ if (!$this->core->auth->check('publish,contentadmin', $this->id)) {
+ throw new Exception(__("You are not allowed to change this comment's status"));
+ }
+
+ $co_ids = dcUtils::cleanIds($ids);
+ $status = (integer) $status;
+
+ $strReq =
+ 'UPDATE ' . $this->prefix . 'comment ' .
+ 'SET comment_status = ' . $status . ' ';
+ $strReq .=
+ 'WHERE comment_id' . $this->con->in($co_ids) .
+ 'AND post_id in (SELECT tp.post_id ' .
+ 'FROM ' . $this->prefix . 'post tp ' .
+ "WHERE tp.blog_id = '" . $this->con->escape($this->id) . "' ";
+ if (!$this->core->auth->check('contentadmin', $this->id)) {
+ $strReq .=
+ "AND user_id = '" . $this->con->escape($this->core->auth->userID()) . "' ";
+ }
+ $strReq .= ')';
+ $this->con->execute($strReq);
+ $this->triggerComments($co_ids);
+ $this->triggerBlog();
+ }
+
+ /**
+ Delete a comment
+
+ @param id integer Comment ID
+ */
+ public function delComment($id)
+ {
+ $this->delComments($id);
+ }
+
+ /**
+ Delete comments
+
+ @param ids mixed Comment(s) ID(s)
+ */
+ public function delComments($ids)
+ {
+ if (!$this->core->auth->check('delete,contentadmin', $this->id)) {
+ throw new Exception(__('You are not allowed to delete comments'));
+ }
+
+ $co_ids = dcUtils::cleanIds($ids);
+
+ if (empty($co_ids)) {
+ throw new Exception(__('No such comment ID'));
+ }
+
+ # Retrieve posts affected by comments edition
+ $affected_posts = [];
+ $strReq =
+ 'SELECT post_id ' .
+ 'FROM ' . $this->prefix . 'comment ' .
+ 'WHERE comment_id' . $this->con->in($co_ids) .
+ 'GROUP BY post_id';
+
+ $rs = $this->con->select($strReq);
+
+ while ($rs->fetch()) {
+ $affected_posts[] = (integer) $rs->post_id;
+ }
+
+ $strReq =
+ 'DELETE FROM ' . $this->prefix . 'comment ' .
+ 'WHERE comment_id' . $this->con->in($co_ids) . ' ' .
+ 'AND post_id in (SELECT tp.post_id ' .
+ 'FROM ' . $this->prefix . 'post tp ' .
+ "WHERE tp.blog_id = '" . $this->con->escape($this->id) . "' ";
+ #If user can only delete, we need to check the post's owner
+ if (!$this->core->auth->check('contentadmin', $this->id)) {
+ $strReq .=
+ "AND tp.user_id = '" . $this->con->escape($this->core->auth->userID()) . "' ";
+ }
+ $strReq .= ")";
+ $this->con->execute($strReq);
+ $this->triggerComments($co_ids, true, $affected_posts);
+ $this->triggerBlog();
+ }
+
+ public function delJunkComments()
+ {
+ if (!$this->core->auth->check('delete,contentadmin', $this->id)) {
+ throw new Exception(__('You are not allowed to delete comments'));
+ }
+
+ $strReq =
+ 'DELETE FROM ' . $this->prefix . 'comment ' .
+ 'WHERE comment_status = -2 ' .
+ 'AND post_id in (SELECT tp.post_id ' .
+ 'FROM ' . $this->prefix . 'post tp ' .
+ "WHERE tp.blog_id = '" . $this->con->escape($this->id) . "' ";
+ #If user can only delete, we need to check the post's owner
+ if (!$this->core->auth->check('contentadmin', $this->id)) {
+ $strReq .=
+ "AND tp.user_id = '" . $this->con->escape($this->core->auth->userID()) . "' ";
+ }
+ $strReq .= ")";
+ $this->con->execute($strReq);
+ $this->triggerBlog();
+ }
+
+ private function getCommentCursor($cur)
+ {
+ if ($cur->comment_content !== null && $cur->comment_content == '') {
+ throw new Exception(__('You must provide a comment'));
+ }
+
+ if ($cur->comment_author !== null && $cur->comment_author == '') {
+ throw new Exception(__('You must provide an author name'));
+ }
+
+ if ($cur->comment_email != '' && !text::isEmail($cur->comment_email)) {
+ throw new Exception(__('Email address is not valid.'));
+ }
+
+ if ($cur->comment_site !== null && $cur->comment_site != '') {
+ if (!preg_match('|^http(s?)://|i', $cur->comment_site, $matches)) {
+ $cur->comment_site = 'http://' . $cur->comment_site;
+ } else {
+ $cur->comment_site = strtolower($matches[0]) . substr($cur->comment_site, strlen($matches[0]));
+ }
+ }
+
+ if ($cur->comment_status === null) {
+ $cur->comment_status = (integer) $this->settings->system->comments_pub;
+ }
+
+ # Words list
+ if ($cur->comment_content !== null) {
+ $cur->comment_words = implode(' ', text::splitWords($cur->comment_content));
+ }
+ }
+ //@}
+}
diff --git a/dotclear._no/inc/core/class.dc.categories.php b/dotclear._no/inc/core/class.dc.categories.php
new file mode 100644
index 0000000..3b23ae8
--- /dev/null
+++ b/dotclear._no/inc/core/class.dc.categories.php
@@ -0,0 +1,480 @@
+core = &$core;
+ $this->con = &$core->con;
+ $this->blog_id = $core->blog->id;
+ $this->table = $core->prefix . 'category';
+ $this->add_condition = ['blog_id' => "'" . $this->con->escape($this->blog_id) . "'"];
+ }
+
+ public function getChildren($start = 0, $id = null, $sort = 'asc', $fields = [])
+ {
+ $fields = array_merge(['cat_title', 'cat_url', 'cat_desc'], $fields);
+ return parent::getChildren($start, $id, $sort, $fields);
+ }
+
+ public function getParents($id, $fields = [])
+ {
+ $fields = array_merge(['cat_title', 'cat_url', 'cat_desc'], $fields);
+ return parent::getParents($id, $fields);
+ }
+
+ public function getParent($id, $fields = [])
+ {
+ $fields = array_merge(['cat_title', 'cat_url', 'cat_desc'], $fields);
+ return parent::getParent($id, $fields);
+ }
+}
+
+abstract class nestedTree
+{
+ protected $con;
+
+ protected $table;
+ protected $f_left;
+ protected $f_right;
+ protected $f_id;
+
+ protected $add_condition = [];
+
+ protected $parents;
+
+ public function __construct($con)
+ {
+ $this->con = &$con;
+ }
+
+ public function getChildren($start = 0, $id = null, $sort = 'asc', $fields = [])
+ {
+ $fields = count($fields) > 0 ? ', C2.' . implode(', C2.', $fields) : '';
+
+ $sql = 'SELECT C2.' . $this->f_id . ', C2.' . $this->f_left . ', C2.' . $this->f_right . ', COUNT(C1.' . $this->f_id . ') AS level '
+ . $fields . ' '
+ . 'FROM ' . $this->table . ' AS C1, ' . $this->table . ' AS C2 %s '
+ . 'WHERE C2.' . $this->f_left . ' BETWEEN C1.' . $this->f_left . ' AND C1.' . $this->f_right . ' '
+ . ' %s '
+ . $this->getCondition('AND', 'C2.')
+ . $this->getCondition('AND', 'C1.')
+ . 'GROUP BY C2.' . $this->f_id . ', C2.' . $this->f_left . ', C2.' . $this->f_right . ' ' . $fields . ' '
+ . ' %s '
+ . 'ORDER BY C2.' . $this->f_left . ' ' . ($sort == 'asc' ? 'ASC' : 'DESC') . ' ';
+
+ $from = $where = '';
+ if ($start > 0) {
+ $from = ', ' . $this->table . ' AS C3';
+ $where = 'AND C3.' . $this->f_id . ' = ' . (integer) $start . ' AND C1.' . $this->f_left . ' >= C3.' . $this->f_left . ' AND C1.' . $this->f_right . ' <= C3.' . $this->f_right;
+ $where .= $this->getCondition('AND', 'C3.');
+ }
+
+ $having = '';
+ if ($id !== null) {
+ $having = ' HAVING C2.' . $this->f_id . ' = ' . (integer) $id;
+ }
+
+ $sql = sprintf($sql, $from, $where, $having);
+
+ return $this->con->select($sql);
+ }
+
+ public function getParents($id, $fields = [])
+ {
+ $fields = count($fields) > 0 ? ', C1.' . implode(', C1.', $fields) : '';
+
+ return $this->con->select(
+ 'SELECT C1.' . $this->f_id . ' ' . $fields . ' '
+ . 'FROM ' . $this->table . ' C1, ' . $this->table . ' C2 '
+ . 'WHERE C2.' . $this->f_id . ' = ' . (integer) $id . ' '
+ . 'AND C1.' . $this->f_left . ' < C2.' . $this->f_left . ' '
+ . 'AND C1.' . $this->f_right . ' > C2.' . $this->f_right . ' '
+ . $this->getCondition('AND', 'C2.')
+ . $this->getCondition('AND', 'C1.')
+ . 'ORDER BY C1.' . $this->f_left . ' ASC '
+ );
+ }
+
+ public function getParent($id, $fields = [])
+ {
+ $fields = count($fields) > 0 ? ', C1.' . implode(', C1.', $fields) : '';
+
+ return $this->con->select(
+ 'SELECT C1.' . $this->f_id . ' ' . $fields . ' '
+ . 'FROM ' . $this->table . ' C1, ' . $this->table . ' C2 '
+ . 'WHERE C2.' . $this->f_id . ' = ' . (integer) $id . ' '
+ . 'AND C1.' . $this->f_left . ' < C2.' . $this->f_left . ' '
+ . 'AND C1.' . $this->f_right . ' > C2.' . $this->f_right . ' '
+ . $this->getCondition('AND', 'C2.')
+ . $this->getCondition('AND', 'C1.')
+ . 'ORDER BY C1.' . $this->f_left . ' DESC '
+ . $this->con->limit(1)
+ );
+ }
+
+ /* ------------------------------------------------
+ * Tree manipulations
+ * ---------------------------------------------- */
+ public function addNode($data, $target = 0)
+ {
+ if (!is_array($data) && !($data instanceof cursor)) {
+ throw new Exception('Invalid data block');
+ }
+
+ if (is_array($data)) {
+ $D = $data;
+ $data = $this->con->openCursor($this->table);
+ foreach ($D as $k => $v) {
+ $data->{$k} = $v;
+ }
+ unset($D);
+ }
+
+ # We want to put it at the end
+ $this->con->writeLock($this->table);
+ try
+ {
+ $rs = $this->con->select('SELECT MAX(' . $this->f_id . ') as n_id FROM ' . $this->table);
+ $id = $rs->n_id;
+
+ $rs = $this->con->select(
+ 'SELECT MAX(' . $this->f_right . ') as n_r ' .
+ 'FROM ' . $this->table .
+ $this->getCondition('WHERE')
+ );
+ $last = $rs->n_r == 0 ? 1 : $rs->n_r;
+
+ $data->{$this->f_id} = $id + 1;
+ $data->{$this->f_left} = $last + 1;
+ $data->{$this->f_right} = $last + 2;
+
+ $data->insert();
+ $this->con->unlock();
+ try {
+ $this->setNodeParent($id + 1, $target);
+ return $data->{$this->f_id};
+ } catch (Exception $e) {} # We don't mind error in this case
+ } catch (Exception $e) {
+ $this->con->unlock();
+ throw $e;
+ }
+ }
+
+ public function updatePosition($id, $left, $right)
+ {
+ $node_left = (integer) $left;
+ $node_right = (integer) $right;
+ $node_id = (integer) $id;
+ $sql = 'UPDATE ' . $this->table . ' SET '
+ . $this->f_left . ' = ' . $node_left . ', '
+ . $this->f_right . ' = ' . $node_right
+ . ' WHERE ' . $this->f_id . ' = ' . $node_id
+ . $this->getCondition();
+
+ $this->con->begin();
+ try {
+ $this->con->execute($sql);
+ $this->con->commit();
+ } catch (Exception $e) {
+ $this->con->rollback();
+ throw $e;
+ }
+ }
+
+ public function deleteNode($node, $keep_children = true)
+ {
+ $node = (integer) $node;
+
+ $rs = $this->getChildren(0, $node);
+ if ($rs->isEmpty()) {
+ throw new Exception('Node does not exist.');
+ }
+ $node_left = (integer) $rs->{$this->f_left};
+ $node_right = (integer) $rs->{$this->f_right};
+
+ try
+ {
+ $this->con->begin();
+
+ if ($keep_children) {
+ $this->con->execute('DELETE FROM ' . $this->table . ' WHERE ' . $this->f_id . ' = ' . $node);
+
+ $sql = 'UPDATE ' . $this->table . ' SET '
+ . $this->f_right . ' = CASE '
+ . 'WHEN ' . $this->f_right . ' BETWEEN ' . $node_left . ' AND ' . $node_right . ' '
+ . 'THEN ' . $this->f_right . ' - 1 '
+ . 'WHEN ' . $this->f_right . ' > ' . $node_right . ' '
+ . 'THEN ' . $this->f_right . ' - 2 '
+ . 'ELSE ' . $this->f_right . ' '
+ . 'END, '
+ . $this->f_left . ' = CASE '
+ . 'WHEN ' . $this->f_left . ' BETWEEN ' . $node_left . ' AND ' . $node_right . ' '
+ . 'THEN ' . $this->f_left . ' - 1 '
+ . 'WHEN ' . $this->f_left . ' > ' . $node_right . ' '
+ . 'THEN ' . $this->f_left . ' - 2 '
+ . 'ELSE ' . $this->f_left . ' '
+ . 'END '
+ . 'WHERE ' . $this->f_right . ' > ' . $node_left
+ . $this->getCondition();
+
+ $this->con->execute($sql);
+ } else {
+ $this->con->execute('DELETE FROM ' . $this->table . ' WHERE ' . $this->f_left . ' BETWEEN ' . $node_left . ' AND ' . $node_right);
+
+ $node_delta = $node_right - $node_left + 1;
+ $sql = 'UPDATE ' . $this->table . ' SET '
+ . $this->f_left . ' = CASE '
+ . 'WHEN ' . $this->f_left . ' > ' . $node_left . ' '
+ . 'THEN ' . $this->f_left . ' - (' . $node_delta . ') '
+ . 'ELSE ' . $this->f_left . ' '
+ . 'END, '
+ . $this->f_right . ' = CASE '
+ . 'WHEN ' . $this->f_right . ' > ' . $node_left . ' '
+ . 'THEN ' . $this->f_right . ' - (' . $node_delta . ') '
+ . 'ELSE ' . $this->f_right . ' '
+ . 'END '
+ . 'WHERE ' . $this->f_right . ' > ' . $node_right
+ . $this->getCondition();
+ }
+
+ $this->con->commit();
+ } catch (Exception $e) {
+ $this->con->rollback();
+ throw $e;
+ }
+ }
+
+ public function resetOrder()
+ {
+ $rs = $this->con->select(
+ 'SELECT ' . $this->f_id . ' '
+ . 'FROM ' . $this->table . ' '
+ . $this->getCondition('WHERE')
+ . 'ORDER BY ' . $this->f_left . ' ASC '
+ );
+
+ $lft = 2;
+ $this->con->begin();
+ try
+ {
+ while ($rs->fetch()) {
+ $this->con->execute(
+ 'UPDATE ' . $this->table . ' SET '
+ . $this->f_left . ' = ' . ($lft++) . ', '
+ . $this->f_right . ' = ' . ($lft++) . ' '
+ . 'WHERE ' . $this->f_id . ' = ' . (integer) $rs->{$this->f_id} . ' '
+ . $this->getCondition()
+ );
+ }
+ $this->con->commit();
+ } catch (Exception $e) {
+ $this->con->rollback();
+ throw $e;
+ }
+ }
+
+ public function setNodeParent($node, $target = 0)
+ {
+ if ($node == $target) {
+ return;
+ }
+ $node = (integer) $node;
+ $target = (integer) $target;
+
+ $rs = $this->getChildren(0, $node);
+ if ($rs->isEmpty()) {
+ throw new Exception('Node does not exist.');
+ }
+ $node_left = (integer) $rs->{$this->f_left};
+ $node_right = (integer) $rs->{$this->f_right};
+ $node_level = (integer) $rs->level;
+
+ if ($target > 0) {
+ $rs = $this->getChildren(0, $target);
+ } else {
+ $rs = $this->con->select(
+ 'SELECT MIN(' . $this->f_left . ')-1 AS ' . $this->f_left . ', MAX(' . $this->f_right . ')+1 AS ' . $this->f_right . ', 0 AS level ' .
+ 'FROM ' . $this->table . ' ' .
+ $this->getCondition('WHERE')
+ );
+ }
+ $target_left = (integer) $rs->{$this->f_left};
+ $target_right = (integer) $rs->{$this->f_right};
+ $target_level = (integer) $rs->level;
+
+ if ($node_left == $target_left
+ || ($target_left >= $node_left && $target_left <= $node_right)
+ || ($node_level == $target_level + 1 && $node_left > $target_left && $node_right < $target_right)
+ ) {
+ throw new Exception('Cannot move tree');
+ }
+
+ if ($target_left < $node_left && $target_right > $node_right && $target_level < $node_level - 1) {
+ $sql = 'UPDATE ' . $this->table . ' SET '
+ . $this->f_right . ' = CASE '
+ . 'WHEN ' . $this->f_right . ' BETWEEN ' . ($node_right + 1) . ' AND ' . ($target_right - 1) . ' '
+ . 'THEN ' . $this->f_right . '-(' . ($node_right - $node_left + 1) . ') '
+ . 'WHEN ' . $this->f_left . ' BETWEEN ' . $node_left . ' AND ' . $node_right . ' '
+ . 'THEN ' . $this->f_right . '+' . ((($target_right - $node_right - $node_level + $target_level) / 2) * 2 + $node_level - $target_level - 1) . ' '
+ . 'ELSE '
+ . $this->f_right . ' '
+ . 'END, '
+ . $this->f_left . ' = CASE '
+ . 'WHEN ' . $this->f_left . ' BETWEEN ' . ($node_right + 1) . ' AND ' . ($target_right - 1) . ' '
+ . 'THEN ' . $this->f_left . '-(' . ($node_right - $node_left + 1) . ') '
+ . 'WHEN ' . $this->f_left . ' BETWEEN ' . $node_left . ' AND ' . $node_right . ' '
+ . 'THEN ' . $this->f_left . '+' . ((($target_right - $node_right - $node_level + $target_level) / 2) * 2 + $node_level - $target_level - 1) . ' '
+ . 'ELSE ' . $this->f_left . ' '
+ . 'END '
+ . 'WHERE ' . $this->f_left . ' BETWEEN ' . ($target_left + 1) . ' AND ' . ($target_right - 1) . '';
+ } elseif ($target_left < $node_left) {
+ $sql = 'UPDATE ' . $this->table . ' SET '
+ . $this->f_left . ' = CASE '
+ . 'WHEN ' . $this->f_left . ' BETWEEN ' . $target_right . ' AND ' . ($node_left - 1) . ' '
+ . 'THEN ' . $this->f_left . '+' . ($node_right - $node_left + 1) . ' '
+ . 'WHEN ' . $this->f_left . ' BETWEEN ' . $node_left . ' AND ' . $node_right . ' '
+ . 'THEN ' . $this->f_left . '-(' . ($node_left - $target_right) . ') '
+ . 'ELSE ' . $this->f_left . ' '
+ . 'END, '
+ . $this->f_right . ' = CASE '
+ . 'WHEN ' . $this->f_right . ' BETWEEN ' . $target_right . ' AND ' . $node_left . ' '
+ . 'THEN ' . $this->f_right . '+' . ($node_right - $node_left + 1) . ' '
+ . 'WHEN ' . $this->f_right . ' BETWEEN ' . $node_left . ' AND ' . $node_right . ' '
+ . 'THEN ' . $this->f_right . '-(' . ($node_left - $target_right) . ') '
+ . 'ELSE ' . $this->f_right . ' '
+ . 'END '
+ . 'WHERE (' . $this->f_left . ' BETWEEN ' . $target_left . ' AND ' . $node_right . ' '
+ . 'OR ' . $this->f_right . ' BETWEEN ' . $target_left . ' AND ' . $node_right . ')';
+ } else {
+ $sql = 'UPDATE ' . $this->table . ' SET '
+ . $this->f_left . ' = CASE '
+ . 'WHEN ' . $this->f_left . ' BETWEEN ' . $node_right . ' AND ' . $target_right . ' '
+ . 'THEN ' . $this->f_left . '-' . ($node_right - $node_left + 1) . ' '
+ . 'WHEN ' . $this->f_left . ' BETWEEN ' . $node_left . ' AND ' . $node_right . ' '
+ . 'THEN ' . $this->f_left . '+' . ($target_right - 1 - $node_right) . ' '
+ . 'ELSE ' . $this->f_left . ' '
+ . 'END, '
+ . $this->f_right . ' = CASE '
+ . 'WHEN ' . $this->f_right . ' BETWEEN ' . ($node_right + 1) . ' AND ' . ($target_right - 1) . ' '
+ . 'THEN ' . $this->f_right . '-' . ($node_right - $node_left + 1) . ' '
+ . 'WHEN ' . $this->f_right . ' BETWEEN ' . $node_left . ' AND ' . $node_right . ' '
+ . 'THEN ' . $this->f_right . '+' . ($target_right - 1 - $node_right) . ' '
+ . 'ELSE ' . $this->f_right . ' '
+ . 'END '
+ . 'WHERE (' . $this->f_left . ' BETWEEN ' . $node_left . ' AND ' . $target_right . ' '
+ . 'OR ' . $this->f_right . ' BETWEEN ' . $node_left . ' AND ' . $target_right . ')';
+ }
+
+ $sql .= ' ' . $this->getCondition();
+
+ $this->con->execute($sql);
+ }
+
+ public function setNodePosition($nodeA, $nodeB, $position = 'after')
+ {
+ $nodeA = (integer) $nodeA;
+ $nodeB = (integer) $nodeB;
+
+ $rs = $this->getChildren(0, $nodeA);
+ if ($rs->isEmpty()) {
+ throw new Exception('Node does not exist.');
+ }
+ $A_left = $rs->{$this->f_left};
+ $A_right = $rs->{$this->f_right};
+ $A_level = $rs->level;
+
+ $rs = $this->getChildren(0, $nodeB);
+ if ($rs->isEmpty()) {
+ throw new Exception('Node does not exist.');
+ }
+ $B_left = $rs->{$this->f_left};
+ $B_right = $rs->{$this->f_right};
+ $B_level = $rs->level;
+
+ if ($A_level != $B_level) {
+ throw new Exception('Cannot change position');
+ }
+
+ $rs = $this->getParents($nodeA);
+ $parentA = $rs->isEmpty() ? 0 : $rs->{$this->f_id};
+ $rs = $this->getParents($nodeB);
+ $parentB = $rs->isEmpty() ? 0 : $rs->{$this->f_id};
+
+ if ($parentA != $parentB) {
+ throw new Exception('Cannot change position');
+ }
+
+ if ($position == 'before') {
+ if ($A_left > $B_left) {
+ $sql = 'UPDATE ' . $this->table . ' SET '
+ . $this->f_right . ' = CASE WHEN ' . $this->f_left . ' BETWEEN ' . $A_left . ' AND ' . $A_right . ' THEN ' . $this->f_right . ' - (' . ($A_left - $B_left) . ') '
+ . 'WHEN ' . $this->f_left . ' BETWEEN ' . $B_left . ' AND ' . ($A_left - 1) . ' THEN ' . $this->f_right . ' + ' . ($A_right - $A_left + 1) . ' ELSE ' . $this->f_right . ' END, '
+ . $this->f_left . ' = CASE WHEN ' . $this->f_left . ' BETWEEN ' . $A_left . ' AND ' . $A_right . ' THEN ' . $this->f_left . ' - (' . ($A_left - $B_left) . ') '
+ . 'WHEN ' . $this->f_left . ' BETWEEN ' . $B_left . ' AND ' . ($A_left - 1) . ' THEN ' . $this->f_left . ' + ' . ($A_right - $A_left + 1) . ' ELSE ' . $this->f_left . ' END '
+ . 'WHERE ' . $this->f_left . ' BETWEEN ' . $B_left . ' AND ' . $A_right;
+ } else {
+ $sql = 'UPDATE ' . $this->table . ' SET '
+ . $this->f_right . ' = CASE WHEN ' . $this->f_left . ' BETWEEN ' . $A_left . ' AND ' . $A_right . ' THEN ' . $this->f_right . ' + ' . (($B_left - $A_left) - ($A_right - $A_left + 1)) . ' '
+ . 'WHEN ' . $this->f_left . ' BETWEEN ' . ($A_right + 1) . ' AND ' . ($B_left - 1) . ' THEN ' . $this->f_right . ' - (' . (($A_right - $A_left + 1)) . ') ELSE ' . $this->f_right . ' END, '
+ . $this->f_left . ' = CASE WHEN ' . $this->f_left . ' BETWEEN ' . $A_left . ' AND ' . $A_right . ' THEN ' . $this->f_left . ' + ' . (($B_left - $A_left) - ($A_right - $A_left + 1)) . ' '
+ . 'WHEN ' . $this->f_left . ' BETWEEN ' . ($A_right + 1) . ' AND ' . ($B_left - 1) . ' THEN ' . $this->f_left . ' - (' . ($A_right - $A_left + 1) . ') ELSE ' . $this->f_left . ' END '
+ . 'WHERE ' . $this->f_left . ' BETWEEN ' . $A_left . ' AND ' . ($B_left - 1);
+ }
+ } else {
+ if ($A_left > $B_left) {
+ $sql = 'UPDATE ' . $this->table . ' SET '
+ . $this->f_right . ' = CASE WHEN ' . $this->f_left . ' BETWEEN ' . $A_left . ' AND ' . $A_right . ' THEN ' . $this->f_right . ' - (' . ($A_left - $B_left - ($B_right - $B_left + 1)) . ') '
+ . 'WHEN ' . $this->f_left . ' BETWEEN ' . ($B_right + 1) . ' AND ' . ($A_left - 1) . ' THEN ' . $this->f_right . ' + ' . ($A_right - $A_left + 1) . ' ELSE ' . $this->f_right . ' END, '
+ . $this->f_left . ' = CASE WHEN ' . $this->f_left . ' BETWEEN ' . $A_left . ' AND ' . $A_right . ' THEN ' . $this->f_left . ' - (' . ($A_left - $B_left - ($B_right - $B_left + 1)) . ') '
+ . 'WHEN ' . $this->f_left . ' BETWEEN ' . ($B_right + 1) . ' AND ' . ($A_left - 1) . ' THEN ' . $this->f_left . ' + ' . ($A_right - $A_left + 1) . ' ELSE ' . $this->f_left . ' END '
+ . 'WHERE ' . $this->f_left . ' BETWEEN ' . ($B_right + 1) . ' AND ' . $A_right;
+ } else {
+ $sql = 'UPDATE ' . $this->table . ' SET '
+ . $this->f_right . ' = CASE WHEN ' . $this->f_left . ' BETWEEN ' . $A_left . ' AND ' . $A_right . ' THEN ' . $this->f_right . ' + ' . ($B_right - $A_right) . ' '
+ . 'WHEN ' . $this->f_left . ' BETWEEN ' . ($A_right + 1) . ' AND ' . $B_right . ' THEN ' . $this->f_right . ' - (' . (($A_right - $A_left + 1)) . ') ELSE ' . $this->f_right . ' END, '
+ . $this->f_left . ' = CASE WHEN ' . $this->f_left . ' BETWEEN ' . $A_left . ' AND ' . $A_right . ' THEN ' . $this->f_left . ' + ' . ($B_right - $A_right) . ' '
+ . 'WHEN ' . $this->f_left . ' BETWEEN ' . ($A_right + 1) . ' AND ' . $B_right . ' THEN ' . $this->f_left . ' - (' . ($A_right - $A_left + 1) . ') ELSE ' . $this->f_left . ' END '
+ . 'WHERE ' . $this->f_left . ' BETWEEN ' . $A_left . ' AND ' . $B_right;
+ }
+ }
+
+ $sql .= $this->getCondition();
+ $this->con->execute($sql);
+ }
+
+ protected function getCondition($start = 'AND', $prefix = '')
+ {
+ if (empty($this->add_condition)) {
+ return '';
+ }
+
+ $w = [];
+ foreach ($this->add_condition as $c => $n) {
+ $w[] = $prefix . $c . ' = ' . $n;
+ }
+ return ' ' . $start . ' ' . implode(' AND ', $w) . ' ';
+ }
+}
diff --git a/dotclear._no/inc/core/class.dc.core.php b/dotclear._no/inc/core/class.dc.core.php
new file mode 100644
index 0000000..dcb2f3e
--- /dev/null
+++ b/dotclear._no/inc/core/class.dc.core.php
@@ -0,0 +1,1601 @@
+connection Database connection object
+ public $prefix; ///< string Database tables prefix
+ public $blog; ///< dcBlog dcBlog object
+ public $error; ///< dcError dcError object
+ public $auth; ///< dcAuth dcAuth object
+ public $session; ///< sessionDB sessionDB object
+ public $url; ///< urlHandler urlHandler object
+ public $wiki2xhtml; ///< wiki2xhtml wiki2xhtml object
+ public $plugins; ///< dcModules dcModules object
+ public $media; ///< dcMedia dcMedia object
+ public $postmedia; ///< dcPostMedia dcPostMedia object
+ public $rest; ///< dcRestServer dcRestServer object
+ public $log; ///< dcLog dcLog object
+ public $stime; ///< float starting time
+
+ private $versions = null;
+ private $formaters = [];
+ private $behaviors = [];
+ private $post_types = [];
+
+ /**
+ dcCore constructor inits everything related to Dotclear. It takes arguments
+ to init database connection.
+
+ @param driver string Database driver name
+ @param host string Database hostname
+ @param db string Database name
+ @param user string Database username
+ @param password string Database password
+ @param prefix string DotClear tables prefix
+ @param persist boolean Persistent database connection
+ */
+ public function __construct($driver, $host, $db, $user, $password, $prefix, $persist)
+ {
+ if (defined('DC_START_TIME')) {
+ $this->stime = DC_START_TIME;
+ } else {
+ $this->stime = microtime(true);
+ }
+
+ $this->con = dbLayer::init($driver, $host, $db, $user, $password, $persist);
+
+ # define weak_locks for mysql
+ if ($this->con instanceof mysqlConnection) {
+ mysqlConnection::$weak_locks = true;
+ } elseif ($this->con instanceof mysqliConnection) {
+ mysqliConnection::$weak_locks = true;
+ } elseif ($this->con instanceof mysqlimb4Connection) {
+ mysqlimb4Connection::$weak_locks = true;
+ }
+
+ # define searchpath for postgresql
+ if ($this->con instanceof pgsqlConnection) {
+ $searchpath = explode('.', $prefix, 2);
+ if (count($searchpath) > 1) {
+ $prefix = $searchpath[1];
+ $sql = 'SET search_path TO ' . $searchpath[0] . ',public;';
+ $this->con->execute($sql);
+ }
+ }
+
+ $this->prefix = $prefix;
+
+ $ttl = DC_SESSION_TTL;
+ if (!is_null($ttl)) {
+ if (substr(trim($ttl), 0, 1) != '-') {
+ // Clearbricks requires negative session TTL
+ $ttl = '-' . trim($ttl);
+ }
+ }
+
+ $this->error = new dcError();
+ $this->auth = $this->authInstance();
+ $this->session = new sessionDB($this->con, $this->prefix . 'session', DC_SESSION_NAME, '', null, DC_ADMIN_SSL, $ttl);
+ $this->url = new dcUrlHandlers();
+
+ $this->plugins = new dcPlugins($this);
+
+ $this->rest = new dcRestServer($this);
+
+ $this->meta = new dcMeta($this);
+
+ $this->log = new dcLog($this);
+ }
+
+ private function authInstance()
+ {
+ # You can set DC_AUTH_CLASS to whatever you want.
+ # Your new class *should* inherits dcAuth.
+ if (!defined('DC_AUTH_CLASS')) {
+ $c = 'dcAuth';
+ } else {
+ $c = DC_AUTH_CLASS;
+ }
+
+ if (!class_exists($c)) {
+ throw new Exception('Authentication class ' . $c . ' does not exist.');
+ }
+
+ if ($c != 'dcAuth' && !is_subclass_of($c, 'dcAuth')) {
+ throw new Exception('Authentication class ' . $c . ' does not inherit dcAuth.');
+ }
+
+ return new $c($this);
+ }
+
+ /// @name Blog init methods
+ //@{
+ /**
+ Sets a blog to use in blog property.
+
+ @param id string Blog ID
+ */
+ public function setBlog($id)
+ {
+ $this->blog = new dcBlog($this, $id);
+ }
+
+ /**
+ Unsets blog property.
+ */
+ public function unsetBlog()
+ {
+ $this->blog = null;
+ }
+ //@}
+
+ /// @name Blog status methods
+ //@{
+ /**
+ Returns an array of available blog status codes and names.
+
+ @return array Simple array with codes in keys and names in value
+ */
+ public function getAllBlogStatus()
+ {
+ return [
+ 1 => __('online'),
+ 0 => __('offline'),
+ -1 => __('removed')
+ ];
+ }
+
+ /**
+ Returns a blog status name given to a code. This is intended to be
+ human-readable and will be translated, so never use it for tests.
+ If status code does not exist, returns offline .
+
+ @param s integer Status code
+ @return string Blog status name
+ */
+ public function getBlogStatus($s)
+ {
+ $r = $this->getAllBlogStatus();
+ if (isset($r[$s])) {
+ return $r[$s];
+ }
+ return $r[0];
+ }
+ //@}
+
+ /// @name Admin nonce secret methods
+ //@{
+
+ public function getNonce()
+ {
+ return $this->auth->cryptLegacy(session_id());
+ }
+
+ public function checkNonce($secret)
+ {
+ // 40 alphanumeric characters min
+ if (!preg_match('/^([0-9a-f]{40,})$/i', $secret)) {
+ return false;
+ }
+
+ return $secret == $this->auth->cryptLegacy(session_id());
+ }
+
+ public function formNonce()
+ {
+ if (!session_id()) {
+ return;
+ }
+
+ return form::hidden(['xd_check'], $this->getNonce());
+ }
+ //@}
+
+ /// @name Text Formatters methods
+ //@{
+ /**
+ Adds a new text formater which will call the function $func to
+ transform text. The function must be a valid callback and takes one
+ argument: the string to transform. It returns the transformed string.
+
+ @param editor_id string Editor id (dcLegacyEditor, dcCKEditor, ...)
+ @param name string Formater name
+ @param func callback Function to use, must be a valid and callable callback
+ */
+ public function addEditorFormater($editor_id, $name, $func)
+ {
+ if (is_callable($func)) {
+ $this->formaters[$editor_id][$name] = $func;
+ }
+ }
+
+ /// @name Text Formatters methods
+ //@{
+ /**
+ Adds a new text formater which will call the function $func to
+ transform text. The function must be a valid callback and takes one
+ argument: the string to transform. It returns the transformed string.
+
+ @param name string Formater name
+ @param func callback Function to use, must be a valid and callable callback
+ */
+ public function addFormater($name, $func)
+ {
+ $this->addEditorFormater('dcLegacyEditor', $name, $func);
+ }
+
+ /**
+ Returns editors list
+
+ @return array An array of editors values.
+ */
+ public function getEditors()
+ {
+ $editors = [];
+
+ foreach (array_keys($this->formaters) as $editor_id) {
+ $editors[$editor_id] = $this->plugins->moduleInfo($editor_id, 'name');
+ }
+
+ return $editors;
+ }
+
+ /**
+ Returns formaters list by editor
+
+ @param editor_id string Editor id (dcLegacyEditor, dcCKEditor, ...)
+ @return array An array of formaters names in values.
+
+ /**
+ if @param editor_id is empty:
+ return all formaters sorted by actives editors
+
+ if @param editor_id is not empty
+ return formaters for an editor if editor is active
+ return empty() array if editor is not active.
+ It can happens when a user choose an editor and admin deactivate that editor later
+ */
+ public function getFormaters($editor_id = '')
+ {
+ $formaters_list = [];
+
+ if (!empty($editor_id)) {
+ if (isset($this->formaters[$editor_id])) {
+ $formaters_list = array_keys($this->formaters[$editor_id]);
+ }
+ } else {
+ foreach ($this->formaters as $editor => $formaters) {
+ $formaters_list[$editor] = array_keys($formaters);
+ }
+ }
+
+ return $formaters_list;
+ }
+
+ /**
+ If $name is a valid formater, it returns $str
+ transformed using that formater.
+
+ @param editor_id string Editor id (dcLegacyEditor, dcCKEditor, ...)
+ @param name string Formater name
+ @param str string String to transform
+ @return string String transformed
+ */
+ public function callEditorFormater($editor_id, $name, $str)
+ {
+ if (isset($this->formaters[$editor_id]) && isset($this->formaters[$editor_id][$name])) {
+ return call_user_func($this->formaters[$editor_id][$name], $str);
+ }
+
+ return $str;
+ }
+ //@}
+
+ /**
+ If $name is a valid formater, it returns $str
+ transformed using that formater.
+
+ @param name string Formater name
+ @param str string String to transform
+ @return string String transformed
+ */
+ public function callFormater($name, $str)
+ {
+ return $this->callEditorFormater('dcLegacyEditor', $name, $str);
+ }
+ //@}
+
+ /// @name Behaviors methods
+ //@{
+ /**
+ Adds a new behavior to behaviors stack. $func must be a valid
+ and callable callback.
+
+ @param behavior string Behavior name
+ @param func callback Function to call
+ */
+ public function addBehavior($behavior, $func)
+ {
+ if (is_callable($func)) {
+ $this->behaviors[$behavior][] = $func;
+ }
+ }
+
+ /**
+ Tests if a particular behavior exists in behaviors stack.
+
+ @param behavior string Behavior name
+ @return boolean
+ */
+ public function hasBehavior($behavior)
+ {
+ return isset($this->behaviors[$behavior]);
+ }
+
+ /**
+ Get behaviors stack (or part of).
+
+ @param behavior string Behavior name
+ @return array
+ */
+ public function getBehaviors($behavior = '')
+ {
+ if (empty($this->behaviors)) {
+ return;
+ }
+
+ if ($behavior == '') {
+ return $this->behaviors;
+ } elseif (isset($this->behaviors[$behavior])) {
+ return $this->behaviors[$behavior];
+ }
+
+ return [];
+ }
+
+ /**
+ Calls every function in behaviors stack for a given behavior and returns
+ concatened result of each function.
+
+ Every parameters added after $behavior will be pass to
+ behavior calls.
+
+ @param behavior string Behavior name
+ @return string Behavior concatened result
+ */
+ public function callBehavior($behavior, ...$args)
+ {
+ if (isset($this->behaviors[$behavior])) {
+ $res = '';
+
+ foreach ($this->behaviors[$behavior] as $f) {
+ $res .= call_user_func_array($f, $args);
+ }
+
+ return $res;
+ }
+ }
+ //@}
+
+ /// @name Post types URLs management
+ //@{
+ public function getPostAdminURL($type, $post_id, $escaped = true)
+ {
+ if (!isset($this->post_types[$type])) {
+ $type = 'post';
+ }
+
+ $url = sprintf($this->post_types[$type]['admin_url'], $post_id);
+ return $escaped ? html::escapeURL($url) : $url;
+ }
+
+ public function getPostPublicURL($type, $post_url, $escaped = true)
+ {
+ if (!isset($this->post_types[$type])) {
+ $type = 'post';
+ }
+
+ $url = sprintf($this->post_types[$type]['public_url'], $post_url);
+ return $escaped ? html::escapeURL($url) : $url;
+ }
+
+ public function setPostType($type, $admin_url, $public_url, $label = '')
+ {
+ $this->post_types[$type] = [
+ 'admin_url' => $admin_url,
+ 'public_url' => $public_url,
+ 'label' => ($label != '' ? $label : $type)
+ ];
+ }
+
+ public function getPostTypes()
+ {
+ return $this->post_types;
+ }
+ //@}
+
+ /// @name Versions management methods
+ //@{
+ /**
+ Returns a given $module version.
+
+ @param module string Module name
+ @return string Module version
+ */
+ public function getVersion($module = 'core')
+ {
+ # Fetch versions if needed
+ if (!is_array($this->versions)) {
+ $strReq = 'SELECT module, version FROM ' . $this->prefix . 'version';
+ $rs = $this->con->select($strReq);
+
+ while ($rs->fetch()) {
+ $this->versions[$rs->module] = $rs->version;
+ }
+ }
+
+ if (isset($this->versions[$module])) {
+ return $this->versions[$module];
+ } else {
+ return;
+ }
+ }
+
+ /**
+ Sets $version to given $module.
+
+ @param module string Module name
+ @param version string Module version
+ */
+ public function setVersion($module, $version)
+ {
+ $cur_version = $this->getVersion($module);
+
+ $cur = $this->con->openCursor($this->prefix . 'version');
+ $cur->module = (string) $module;
+ $cur->version = (string) $version;
+
+ if ($cur_version === null) {
+ $cur->insert();
+ } else {
+ $cur->update("WHERE module='" . $this->con->escape($module) . "'");
+ }
+
+ $this->versions[$module] = $version;
+ }
+
+ /**
+ Removes given $module version entry.
+
+ @param module string Module name
+ */
+ public function delVersion($module)
+ {
+ $strReq =
+ 'DELETE FROM ' . $this->prefix . 'version ' .
+ "WHERE module = '" . $this->con->escape($module) . "' ";
+
+ $this->con->execute($strReq);
+
+ if (is_array($this->versions)) {
+ unset($this->versions[$module]);
+ }
+ }
+
+ //@}
+
+ /// @name Users management methods
+ //@{
+ /**
+ Returns a user by its ID.
+
+ @param id string User ID
+ @return record
+ */
+ public function getUser($id)
+ {
+ $params['user_id'] = $id;
+
+ return $this->getUsers($params);
+ }
+
+ /**
+ Returns a users list. $params is an array with the following
+ optionnal parameters:
+
+ - q : search string (on user_id, user_name, user_firstname)
+ - user_id : user ID
+ - order : ORDER BY clause (default: user_id ASC)
+ - limit : LIMIT clause (should be an array ![limit,offset])
+
+ @param params array Parameters
+ @param count_only boolean Only counts results
+ @return record
+ */
+ public function getUsers($params = [], $count_only = false)
+ {
+ if ($count_only) {
+ $strReq =
+ 'SELECT count(U.user_id) ' .
+ 'FROM ' . $this->prefix . 'user U ' .
+ 'WHERE NULL IS NULL ';
+ } else {
+ $strReq =
+ 'SELECT U.user_id,user_super,user_status,user_pwd,user_change_pwd,' .
+ 'user_name,user_firstname,user_displayname,user_email,user_url,' .
+ 'user_desc, user_lang,user_tz, user_post_status,user_options, ' .
+ 'count(P.post_id) AS nb_post ' .
+ 'FROM ' . $this->prefix . 'user U ' .
+ 'LEFT JOIN ' . $this->prefix . 'post P ON U.user_id = P.user_id ' .
+ 'WHERE NULL IS NULL ';
+ }
+
+ if (!empty($params['q'])) {
+ $q = $this->con->escape(str_replace('*', '%', strtolower($params['q'])));
+ $strReq .= 'AND (' .
+ "LOWER(U.user_id) LIKE '" . $q . "' " .
+ "OR LOWER(user_name) LIKE '" . $q . "' " .
+ "OR LOWER(user_firstname) LIKE '" . $q . "' " .
+ ') ';
+ }
+
+ if (!empty($params['user_id'])) {
+ $strReq .= "AND U.user_id = '" . $this->con->escape($params['user_id']) . "' ";
+ }
+
+ if (!$count_only) {
+ $strReq .= 'GROUP BY U.user_id,user_super,user_status,user_pwd,user_change_pwd,' .
+ 'user_name,user_firstname,user_displayname,user_email,user_url,' .
+ 'user_desc, user_lang,user_tz,user_post_status,user_options ';
+
+ if (!empty($params['order']) && !$count_only) {
+ if (preg_match('`^([^. ]+) (?:asc|desc)`i', $params['order'], $matches)) {
+ if (in_array($matches[1], ['user_id', 'user_name', 'user_firstname', 'user_displayname'])) {
+ $table_prefix = 'U.';
+ } else {
+ $table_prefix = ''; // order = nb_post (asc|desc)
+ }
+ $strReq .= 'ORDER BY ' . $table_prefix . $this->con->escape($params['order']) . ' ';
+ } else {
+ $strReq .= 'ORDER BY ' . $this->con->escape($params['order']) . ' ';
+ }
+ } else {
+ $strReq .= 'ORDER BY U.user_id ASC ';
+ }
+ }
+
+ if (!$count_only && !empty($params['limit'])) {
+ $strReq .= $this->con->limit($params['limit']);
+ }
+ $rs = $this->con->select($strReq);
+ $rs->extend('rsExtUser');
+ return $rs;
+ }
+
+ /**
+ Create a new user. Takes a cursor as input and returns the new user ID.
+
+ @param cur cursor User cursor
+ @return string
+ */
+ public function addUser($cur)
+ {
+ if (!$this->auth->isSuperAdmin()) {
+ throw new Exception(__('You are not an administrator'));
+ }
+
+ if ($cur->user_id == '') {
+ throw new Exception(__('No user ID given'));
+ }
+
+ if ($cur->user_pwd == '') {
+ throw new Exception(__('No password given'));
+ }
+
+ $this->getUserCursor($cur);
+
+ if ($cur->user_creadt === null) {
+ $cur->user_creadt = date('Y-m-d H:i:s');
+ }
+
+ $cur->insert();
+
+ $this->auth->afterAddUser($cur);
+
+ return $cur->user_id;
+ }
+
+ /**
+ Updates an existing user. Returns the user ID.
+
+ @param id string User ID
+ @param cur cursor User cursor
+ @return string
+ */
+ public function updUser($id, $cur)
+ {
+ $this->getUserCursor($cur);
+
+ if (($cur->user_id !== null || $id != $this->auth->userID()) &&
+ !$this->auth->isSuperAdmin()) {
+ throw new Exception(__('You are not an administrator'));
+ }
+
+ $cur->update("WHERE user_id = '" . $this->con->escape($id) . "' ");
+
+ $this->auth->afterUpdUser($id, $cur);
+
+ if ($cur->user_id !== null) {
+ $id = $cur->user_id;
+ }
+
+ # Updating all user's blogs
+ $rs = $this->con->select(
+ 'SELECT DISTINCT(blog_id) FROM ' . $this->prefix . 'post ' .
+ "WHERE user_id = '" . $this->con->escape($id) . "' "
+ );
+
+ while ($rs->fetch()) {
+ $b = new dcBlog($this, $rs->blog_id);
+ $b->triggerBlog();
+ unset($b);
+ }
+
+ return $id;
+ }
+
+ /**
+ Deletes a user.
+
+ @param id string User ID
+ */
+ public function delUser($id)
+ {
+ if (!$this->auth->isSuperAdmin()) {
+ throw new Exception(__('You are not an administrator'));
+ }
+
+ if ($id == $this->auth->userID()) {
+ return;
+ }
+
+ $rs = $this->getUser($id);
+
+ if ($rs->nb_post > 0) {
+ return;
+ }
+
+ $strReq = 'DELETE FROM ' . $this->prefix . 'user ' .
+ "WHERE user_id = '" . $this->con->escape($id) . "' ";
+
+ $this->con->execute($strReq);
+
+ $this->auth->afterDelUser($id);
+ }
+
+ /**
+ Checks whether a user exists.
+
+ @param id string User ID
+ @return boolean
+ */
+ public function userExists($id)
+ {
+ $strReq = 'SELECT user_id ' .
+ 'FROM ' . $this->prefix . 'user ' .
+ "WHERE user_id = '" . $this->con->escape($id) . "' ";
+
+ $rs = $this->con->select($strReq);
+
+ return !$rs->isEmpty();
+ }
+
+ /**
+ Returns all user permissions as an array which looks like:
+
+ - [blog_id]
+ - [name] => Blog name
+ - [url] => Blog URL
+ - [p]
+ - [permission] => true
+ - ...
+
+ @param id string User ID
+ @return array
+ */
+ public function getUserPermissions($id)
+ {
+ $strReq = 'SELECT B.blog_id, blog_name, blog_url, permissions ' .
+ 'FROM ' . $this->prefix . 'permissions P ' .
+ 'INNER JOIN ' . $this->prefix . 'blog B ON P.blog_id = B.blog_id ' .
+ "WHERE user_id = '" . $this->con->escape($id) . "' ";
+
+ $rs = $this->con->select($strReq);
+
+ $res = [];
+
+ while ($rs->fetch()) {
+ $res[$rs->blog_id] = [
+ 'name' => $rs->blog_name,
+ 'url' => $rs->blog_url,
+ 'p' => $this->auth->parsePermissions($rs->permissions)
+ ];
+ }
+
+ return $res;
+ }
+
+ /**
+ Sets user permissions. The $perms array looks like:
+
+ - [blog_id] => '|perm1|perm2|'
+ - ...
+
+ @param id string User ID
+ @param perms array Permissions array
+ */
+ public function setUserPermissions($id, $perms)
+ {
+ if (!$this->auth->isSuperAdmin()) {
+ throw new Exception(__('You are not an administrator'));
+ }
+
+ $strReq = 'DELETE FROM ' . $this->prefix . 'permissions ' .
+ "WHERE user_id = '" . $this->con->escape($id) . "' ";
+
+ $this->con->execute($strReq);
+
+ foreach ($perms as $blog_id => $p) {
+ $this->setUserBlogPermissions($id, $blog_id, $p, false);
+ }
+ }
+
+ /**
+ Sets user permissions for a given blog. $perms is an array with
+ permissions in values
+
+ @param id string User ID
+ @param blog_id string Blog ID
+ @param perms array Permissions
+ @param delete_first boolean Delete permissions before
+ */
+ public function setUserBlogPermissions($id, $blog_id, $perms, $delete_first = true)
+ {
+ if (!$this->auth->isSuperAdmin()) {
+ throw new Exception(__('You are not an administrator'));
+ }
+
+ $no_perm = empty($perms);
+
+ $perms = '|' . implode('|', array_keys($perms)) . '|';
+
+ $cur = $this->con->openCursor($this->prefix . 'permissions');
+
+ $cur->user_id = (string) $id;
+ $cur->blog_id = (string) $blog_id;
+ $cur->permissions = $perms;
+
+ if ($delete_first || $no_perm) {
+ $strReq = 'DELETE FROM ' . $this->prefix . 'permissions ' .
+ "WHERE blog_id = '" . $this->con->escape($blog_id) . "' " .
+ "AND user_id = '" . $this->con->escape($id) . "' ";
+
+ $this->con->execute($strReq);
+ }
+
+ if (!$no_perm) {
+ $cur->insert();
+ }
+ }
+
+ /**
+ Sets a user default blog. This blog will be selected when user log in.
+
+ @param id string User ID
+ @param blog_id string Blog ID
+ */
+ public function setUserDefaultBlog($id, $blog_id)
+ {
+ $cur = $this->con->openCursor($this->prefix . 'user');
+
+ $cur->user_default_blog = (string) $blog_id;
+
+ $cur->update("WHERE user_id = '" . $this->con->escape($id) . "'");
+ }
+
+ private function getUserCursor($cur)
+ {
+ if ($cur->isField('user_id')
+ && !preg_match('/^[A-Za-z0-9@._-]{2,}$/', $cur->user_id)) {
+ throw new Exception(__('User ID must contain at least 2 characters using letters, numbers or symbols.'));
+ }
+
+ if ($cur->user_url !== null && $cur->user_url != '') {
+ if (!preg_match('|^http(s?)://|', $cur->user_url)) {
+ $cur->user_url = 'http://' . $cur->user_url;
+ }
+ }
+
+ if ($cur->isField('user_pwd')) {
+ if (strlen($cur->user_pwd) < 6) {
+ throw new Exception(__('Password must contain at least 6 characters.'));
+ }
+ $cur->user_pwd = $this->auth->crypt($cur->user_pwd);
+ }
+
+ if ($cur->user_lang !== null && !preg_match('/^[a-z]{2}(-[a-z]{2})?$/', $cur->user_lang)) {
+ throw new Exception(__('Invalid user language code'));
+ }
+
+ if ($cur->user_upddt === null) {
+ $cur->user_upddt = date('Y-m-d H:i:s');
+ }
+
+ if ($cur->user_options !== null) {
+ $cur->user_options = serialize((array) $cur->user_options);
+ }
+ }
+
+ /**
+ Returns user default settings in an associative array with setting names in
+ keys.
+
+ @return array
+ */
+ public function userDefaults()
+ {
+ return [
+ 'edit_size' => 24,
+ 'enable_wysiwyg' => true,
+ 'toolbar_bottom' => false,
+ 'editor' => ['xhtml' => 'dcCKEditor', 'wiki' => 'dcLegacyEditor'],
+ 'post_format' => 'wiki'
+ ];
+ }
+ //@}
+
+ /// @name Blog management methods
+ //@{
+ /**
+ Returns all blog permissions (users) as an array which looks like:
+
+ - [user_id]
+ - [name] => User name
+ - [firstname] => User firstname
+ - [displayname] => User displayname
+ - [super] => (true|false) super admin
+ - [p]
+ - [permission] => true
+ - ...
+
+ @param id string Blog ID
+ @param with_super boolean Includes super admins in result
+ @return array
+ */
+ public function getBlogPermissions($id, $with_super = true)
+ {
+ $strReq =
+ 'SELECT U.user_id AS user_id, user_super, user_name, user_firstname, ' .
+ 'user_displayname, user_email, permissions ' .
+ 'FROM ' . $this->prefix . 'user U ' .
+ 'JOIN ' . $this->prefix . 'permissions P ON U.user_id = P.user_id ' .
+ "WHERE blog_id = '" . $this->con->escape($id) . "' ";
+
+ if ($with_super) {
+ $strReq .=
+ 'UNION ' .
+ 'SELECT U.user_id AS user_id, user_super, user_name, user_firstname, ' .
+ "user_displayname, user_email, NULL AS permissions " .
+ 'FROM ' . $this->prefix . 'user U ' .
+ 'WHERE user_super = 1 ';
+ }
+
+ $rs = $this->con->select($strReq);
+
+ $res = [];
+
+ while ($rs->fetch()) {
+ $res[$rs->user_id] = [
+ 'name' => $rs->user_name,
+ 'firstname' => $rs->user_firstname,
+ 'displayname' => $rs->user_displayname,
+ 'email' => $rs->user_email,
+ 'super' => (boolean) $rs->user_super,
+ 'p' => $this->auth->parsePermissions($rs->permissions)
+ ];
+ }
+
+ return $res;
+ }
+
+ /**
+ Returns a blog of given ID.
+
+ @param id string Blog ID
+ @return record
+ */
+ public function getBlog($id)
+ {
+ $blog = $this->getBlogs(['blog_id' => $id]);
+
+ if ($blog->isEmpty()) {
+ return false;
+ }
+
+ return $blog;
+ }
+
+ /**
+ Returns a record of blogs. $params is an array with the following
+ optionnal parameters:
+
+ - blog_id : Blog ID
+ - q : Search string on blog_id, blog_name and blog_url
+ - limit : limit results
+
+ @param params array Parameters
+ @param count_only boolean Count only results
+ @return record
+ */
+ public function getBlogs($params = [], $count_only = false)
+ {
+ $join = ''; // %1$s
+ $where = ''; // %2$s
+
+ if ($count_only) {
+ $strReq = 'SELECT count(B.blog_id) ' .
+ 'FROM ' . $this->prefix . 'blog B ' .
+ '%1$s ' .
+ 'WHERE NULL IS NULL ' .
+ '%2$s ';
+ } else {
+ $strReq =
+ 'SELECT B.blog_id, blog_uid, blog_url, blog_name, blog_desc, blog_creadt, ' .
+ 'blog_upddt, blog_status ' .
+ 'FROM ' . $this->prefix . 'blog B ' .
+ '%1$s ' .
+ 'WHERE NULL IS NULL ' .
+ '%2$s ';
+
+ if (!empty($params['order'])) {
+ $strReq .= 'ORDER BY ' . $this->con->escape($params['order']) . ' ';
+ } else {
+ $strReq .= 'ORDER BY B.blog_id ASC ';
+ }
+
+ if (!empty($params['limit'])) {
+ $strReq .= $this->con->limit($params['limit']);
+ }
+ }
+
+ if ($this->auth->userID() && !$this->auth->isSuperAdmin()) {
+ $join = 'INNER JOIN ' . $this->prefix . 'permissions PE ON B.blog_id = PE.blog_id ';
+ $where =
+ "AND PE.user_id = '" . $this->con->escape($this->auth->userID()) . "' " .
+ "AND (permissions LIKE '%|usage|%' OR permissions LIKE '%|admin|%' OR permissions LIKE '%|contentadmin|%') " .
+ "AND blog_status IN (1,0) ";
+ } elseif (!$this->auth->userID()) {
+ $where = 'AND blog_status IN (1,0) ';
+ }
+
+ if (isset($params['blog_status']) && $params['blog_status'] !== '' && $this->auth->isSuperAdmin()) {
+ $where .= 'AND blog_status = ' . (integer) $params['blog_status'] . ' ';
+ }
+
+ if (isset($params['blog_id']) && $params['blog_id'] !== '') {
+ if (!is_array($params['blog_id'])) {
+ $params['blog_id'] = [$params['blog_id']];
+ }
+ $where .= 'AND B.blog_id ' . $this->con->in($params['blog_id']);
+ }
+
+ if (!empty($params['q'])) {
+ $params['q'] = strtolower(str_replace('*', '%', $params['q']));
+ $where .=
+ 'AND (' .
+ "LOWER(B.blog_id) LIKE '" . $this->con->escape($params['q']) . "' " .
+ "OR LOWER(B.blog_name) LIKE '" . $this->con->escape($params['q']) . "' " .
+ "OR LOWER(B.blog_url) LIKE '" . $this->con->escape($params['q']) . "' " .
+ ') ';
+ }
+
+ $strReq = sprintf($strReq, $join, $where);
+ return $this->con->select($strReq);
+ }
+
+ /**
+ Creates a new blog.
+
+ @param cur cursor Blog cursor
+ */
+ public function addBlog($cur)
+ {
+ if (!$this->auth->isSuperAdmin()) {
+ throw new Exception(__('You are not an administrator'));
+ }
+
+ $this->getBlogCursor($cur);
+
+ $cur->blog_creadt = date('Y-m-d H:i:s');
+ $cur->blog_upddt = date('Y-m-d H:i:s');
+ $cur->blog_uid = md5(uniqid());
+
+ $cur->insert();
+ }
+
+ /**
+ Updates a given blog.
+
+ @param id string Blog ID
+ @param cur cursor Blog cursor
+ */
+ public function updBlog($id, $cur)
+ {
+ $this->getBlogCursor($cur);
+
+ $cur->blog_upddt = date('Y-m-d H:i:s');
+
+ $cur->update("WHERE blog_id = '" . $this->con->escape($id) . "'");
+ }
+
+ private function getBlogCursor($cur)
+ {
+ if (($cur->blog_id !== null
+ && !preg_match('/^[A-Za-z0-9._-]{2,}$/', $cur->blog_id)) ||
+ (!$cur->blog_id)) {
+ throw new Exception(__('Blog ID must contain at least 2 characters using letters, numbers or symbols.'));
+ }
+
+ if (($cur->blog_name !== null && $cur->blog_name == '') ||
+ (!$cur->blog_name)) {
+ throw new Exception(__('No blog name'));
+ }
+
+ if (($cur->blog_url !== null && $cur->blog_url == '') ||
+ (!$cur->blog_url)) {
+ throw new Exception(__('No blog URL'));
+ }
+
+ if ($cur->blog_desc !== null) {
+ $cur->blog_desc = html::clean($cur->blog_desc);
+ }
+ }
+
+ /**
+ Removes a given blog.
+ @warning This will remove everything related to the blog (posts,
+ categories, comments, links...)
+
+ @param id string Blog ID
+ */
+ public function delBlog($id)
+ {
+ if (!$this->auth->isSuperAdmin()) {
+ throw new Exception(__('You are not an administrator'));
+ }
+
+ $strReq = 'DELETE FROM ' . $this->prefix . 'blog ' .
+ "WHERE blog_id = '" . $this->con->escape($id) . "' ";
+
+ $this->con->execute($strReq);
+ }
+
+ /**
+ Checks if a blog exist.
+
+ @param id string Blog ID
+ @return boolean
+ */
+ public function blogExists($id)
+ {
+ $strReq = 'SELECT blog_id ' .
+ 'FROM ' . $this->prefix . 'blog ' .
+ "WHERE blog_id = '" . $this->con->escape($id) . "' ";
+
+ $rs = $this->con->select($strReq);
+
+ return !$rs->isEmpty();
+ }
+
+ /**
+ Count posts on a blog
+
+ @param id string Blog ID
+ @param type string Post type
+ @return boolean
+ */
+ public function countBlogPosts($id, $type = null)
+ {
+ $strReq = 'SELECT COUNT(post_id) ' .
+ 'FROM ' . $this->prefix . 'post ' .
+ "WHERE blog_id = '" . $this->con->escape($id) . "' ";
+
+ if ($type) {
+ $strReq .= "AND post_type = '" . $this->con->escape($type) . "' ";
+ }
+
+ return $this->con->select($strReq)->f(0);
+ }
+ //@}
+
+ /// @name HTML Filter methods
+ //@{
+ /**
+ Calls HTML filter to drop bad tags and produce valid XHTML output (if
+ tidy extension is present). If enable_html_filter blog setting is
+ false, returns not filtered string.
+
+ @param str string String to filter
+ @return string Filtered string.
+ */
+ public function HTMLfilter($str)
+ {
+ if ($this->blog instanceof dcBlog && !$this->blog->settings->system->enable_html_filter) {
+ return $str;
+ }
+
+ $filter = new htmlFilter;
+ $str = trim($filter->apply($str));
+ return $str;
+ }
+ //@}
+
+ /// @name wiki2xhtml methods
+ //@{
+ private function initWiki()
+ {
+ $this->wiki2xhtml = new wiki2xhtml;
+ }
+
+ /**
+ Returns a transformed string with wiki2xhtml.
+
+ @param str string String to transform
+ @return string Transformed string
+ */
+ public function wikiTransform($str)
+ {
+ if (!($this->wiki2xhtml instanceof wiki2xhtml)) {
+ $this->initWiki();
+ }
+ return $this->wiki2xhtml->transform($str);
+ }
+
+ /**
+ Inits wiki2xhtml property for blog post.
+ */
+ public function initWikiPost()
+ {
+ $this->initWiki();
+
+ $this->wiki2xhtml->setOpts([
+ 'active_title' => 1,
+ 'active_setext_title' => 0,
+ 'active_hr' => 1,
+ 'active_lists' => 1,
+ 'active_defl' => 1,
+ 'active_quote' => 1,
+ 'active_pre' => 1,
+ 'active_empty' => 1,
+ 'active_auto_urls' => 0,
+ 'active_auto_br' => 0,
+ 'active_antispam' => 1,
+ 'active_urls' => 1,
+ 'active_auto_img' => 0,
+ 'active_img' => 1,
+ 'active_anchor' => 1,
+ 'active_em' => 1,
+ 'active_strong' => 1,
+ 'active_br' => 1,
+ 'active_q' => 1,
+ 'active_code' => 1,
+ 'active_acronym' => 1,
+ 'active_ins' => 1,
+ 'active_del' => 1,
+ 'active_footnotes' => 1,
+ 'active_wikiwords' => 0,
+ 'active_macros' => 1,
+ 'active_mark' => 1,
+ 'active_aside' => 1,
+ 'active_sup' => 1,
+ 'active_sub' => 1,
+ 'active_i' => 1,
+ 'parse_pre' => 1,
+ 'active_fr_syntax' => 0,
+ 'first_title_level' => 3,
+ 'note_prefix' => 'wiki-footnote',
+ 'note_str' => '',
+ 'img_style_center' => 'display:table; margin:0 auto;'
+ ]);
+
+ $this->wiki2xhtml->registerFunction('url:post', [$this, 'wikiPostLink']);
+
+ # --BEHAVIOR-- coreWikiPostInit
+ $this->callBehavior('coreInitWikiPost', $this->wiki2xhtml);
+ }
+
+ /**
+ Inits wiki2xhtml property for simple blog comment (basic syntax).
+ */
+ public function initWikiSimpleComment()
+ {
+ $this->initWiki();
+
+ $this->wiki2xhtml->setOpts([
+ 'active_title' => 0,
+ 'active_setext_title' => 0,
+ 'active_hr' => 0,
+ 'active_lists' => 0,
+ 'active_defl' => 0,
+ 'active_quote' => 0,
+ 'active_pre' => 0,
+ 'active_empty' => 0,
+ 'active_auto_urls' => 1,
+ 'active_auto_br' => 1,
+ 'active_antispam' => 1,
+ 'active_urls' => 0,
+ 'active_auto_img' => 0,
+ 'active_img' => 0,
+ 'active_anchor' => 0,
+ 'active_em' => 0,
+ 'active_strong' => 0,
+ 'active_br' => 0,
+ 'active_q' => 0,
+ 'active_code' => 0,
+ 'active_acronym' => 0,
+ 'active_ins' => 0,
+ 'active_del' => 0,
+ 'active_inline_html' => 0,
+ 'active_footnotes' => 0,
+ 'active_wikiwords' => 0,
+ 'active_macros' => 0,
+ 'active_mark' => 0,
+ 'active_aside' => 0,
+ 'active_sup' => 0,
+ 'active_sub' => 0,
+ 'active_i' => 0,
+ 'parse_pre' => 0,
+ 'active_fr_syntax' => 0
+ ]);
+
+ # --BEHAVIOR-- coreInitWikiSimpleComment
+ $this->callBehavior('coreInitWikiSimpleComment', $this->wiki2xhtml);
+ }
+
+ /**
+ Inits wiki2xhtml property for blog comment.
+ */
+ public function initWikiComment()
+ {
+ $this->initWiki();
+
+ $this->wiki2xhtml->setOpts([
+ 'active_title' => 0,
+ 'active_setext_title' => 0,
+ 'active_hr' => 0,
+ 'active_lists' => 1,
+ 'active_defl' => 0,
+ 'active_quote' => 1,
+ 'active_pre' => 1,
+ 'active_aside' => 0,
+ 'active_empty' => 0,
+ 'active_auto_br' => 1,
+ 'active_auto_urls' => 1,
+ 'active_urls' => 1,
+ 'active_auto_img' => 0,
+ 'active_img' => 0,
+ 'active_anchor' => 0,
+ 'active_em' => 1,
+ 'active_strong' => 1,
+ 'active_br' => 1,
+ 'active_q' => 1,
+ 'active_code' => 1,
+ 'active_acronym' => 1,
+ 'active_ins' => 1,
+ 'active_del' => 1,
+ 'active_footnotes' => 0,
+ 'active_inline_html' => 0,
+ 'active_wikiwords' => 0,
+ 'active_macros' => 0,
+ 'active_mark' => 1,
+ 'active_aside' => 0,
+ 'active_sup' => 1,
+ 'active_sub' => 1,
+ 'active_i' => 1,
+ 'parse_pre' => 0,
+ 'active_fr_syntax' => 0
+ ]);
+
+ # --BEHAVIOR-- coreInitWikiComment
+ $this->callBehavior('coreInitWikiComment', $this->wiki2xhtml);
+ }
+
+ public function wikiPostLink($url, $content)
+ {
+ if (!($this->blog instanceof dcBlog)) {
+ return [];
+ }
+
+ $post_id = abs((integer) substr($url, 5));
+ if (!$post_id) {
+ return [];
+ }
+
+ $post = $this->blog->getPosts(['post_id' => $post_id]);
+ if ($post->isEmpty()) {
+ return [];
+ }
+
+ $res = ['url' => $post->getURL()];
+ $post_title = $post->post_title;
+
+ if ($content != $url) {
+ $res['title'] = html::escapeHTML($post->post_title);
+ }
+
+ if ($content == '' || $content == $url) {
+ $res['content'] = html::escapeHTML($post->post_title);
+ }
+
+ if ($post->post_lang) {
+ $res['lang'] = $post->post_lang;
+ }
+
+ return $res;
+ }
+ //@}
+
+ /// @name Maintenance methods
+ //@{
+ /**
+ Creates default settings for active blog. Optionnal parameter
+ defaults replaces default params while needed.
+
+ @param defaults array Default parameters
+ */
+ public function blogDefaults($defaults = null)
+ {
+ if (!is_array($defaults)) {
+ $defaults = [
+ ['allow_comments', 'boolean', true,
+ 'Allow comments on blog'],
+ ['allow_trackbacks', 'boolean', true,
+ 'Allow trackbacks on blog'],
+ ['blog_timezone', 'string', 'Europe/London',
+ 'Blog timezone'],
+ ['comments_nofollow', 'boolean', true,
+ 'Add rel="nofollow" to comments URLs'],
+ ['comments_pub', 'boolean', true,
+ 'Publish comments immediately'],
+ ['comments_ttl', 'integer', 0,
+ 'Number of days to keep comments open (0 means no ttl)'],
+ ['copyright_notice', 'string', '', 'Copyright notice (simple text)'],
+ ['date_format', 'string', '%A, %B %e %Y',
+ 'Date format. See PHP strftime function for patterns'],
+ ['editor', 'string', '',
+ 'Person responsible of the content'],
+ ['enable_html_filter', 'boolean', 0,
+ 'Enable HTML filter'],
+ ['enable_xmlrpc', 'boolean', 0,
+ 'Enable XML/RPC interface'],
+ ['lang', 'string', 'en',
+ 'Default blog language'],
+ ['media_exclusion', 'string', '/\.(phps?|pht(ml)?|phl|.?html?|xml|js|htaccess)[0-9]*$/i',
+ 'File name exclusion pattern in media manager. (PCRE value)'],
+ ['media_img_m_size', 'integer', 448,
+ 'Image medium size in media manager'],
+ ['media_img_s_size', 'integer', 240,
+ 'Image small size in media manager'],
+ ['media_img_t_size', 'integer', 100,
+ 'Image thumbnail size in media manager'],
+ ['media_img_title_pattern', 'string', 'Title ;; Date(%b %Y) ;; separator(, )',
+ 'Pattern to set image title when you insert it in a post'],
+ ['media_video_width', 'integer', 400,
+ 'Video width in media manager'],
+ ['media_video_height', 'integer', 300,
+ 'Video height in media manager'],
+ ['nb_post_for_home', 'integer', 20,
+ 'Number of entries on first home page'],
+ ['nb_post_per_page', 'integer', 20,
+ 'Number of entries on home pages and category pages'],
+ ['nb_post_per_feed', 'integer', 20,
+ 'Number of entries on feeds'],
+ ['nb_comment_per_feed', 'integer', 20,
+ 'Number of comments on feeds'],
+ ['post_url_format', 'string', '{y}/{m}/{d}/{t}',
+ 'Post URL format. {y}: year, {m}: month, {d}: day, {id}: post id, {t}: entry title'],
+ ['public_path', 'string', 'public',
+ 'Path to public directory, begins with a / for a full system path'],
+ ['public_url', 'string', '/public',
+ 'URL to public directory'],
+ ['robots_policy', 'string', 'INDEX,FOLLOW',
+ 'Search engines robots policy'],
+ ['short_feed_items', 'boolean', false,
+ 'Display short feed items'],
+ ['theme', 'string', 'berlin',
+ 'Blog theme'],
+ ['themes_path', 'string', 'themes',
+ 'Themes root path'],
+ ['themes_url', 'string', '/themes',
+ 'Themes root URL'],
+ ['time_format', 'string', '%H:%M',
+ 'Time format. See PHP strftime function for patterns'],
+ ['tpl_allow_php', 'boolean', false,
+ 'Allow PHP code in templates'],
+ ['tpl_use_cache', 'boolean', true,
+ 'Use template caching'],
+ ['trackbacks_pub', 'boolean', true,
+ 'Publish trackbacks immediately'],
+ ['trackbacks_ttl', 'integer', 0,
+ 'Number of days to keep trackbacks open (0 means no ttl)'],
+ ['url_scan', 'string', 'query_string',
+ 'URL handle mode (path_info or query_string)'],
+ ['use_smilies', 'boolean', false,
+ 'Show smilies on entries and comments'],
+ ['no_search', 'boolean', false,
+ 'Disable search'],
+ ['inc_subcats', 'boolean', false,
+ 'Include sub-categories in category page and category posts feed'],
+ ['wiki_comments', 'boolean', false,
+ 'Allow commenters to use a subset of wiki syntax'],
+ ['import_feed_url_control', 'boolean', true,
+ 'Control feed URL before import'],
+ ['import_feed_no_private_ip', 'boolean', true,
+ 'Prevent import feed from private IP'],
+ ['import_feed_ip_regexp', 'string', '',
+ 'Authorize import feed only from this IP regexp'],
+ ['import_feed_port_regexp', 'string', '/^(80|443)$/',
+ 'Authorize import feed only from this port regexp'],
+ ['jquery_needed', 'boolean', true,
+ 'Load jQuery library'],
+ ];
+ }
+
+ $settings = new dcSettings($this, null);
+ $settings->addNamespace('system');
+
+ foreach ($defaults as $v) {
+ $settings->system->put($v[0], $v[2], $v[1], $v[3], false, true);
+ }
+ }
+
+ /**
+ Recreates entries search engine index.
+
+ @param start integer Start entry index
+ @param limit integer Number of entry to index
+
+ @return integer $start and $limit sum
+ */
+ public function indexAllPosts($start = null, $limit = null)
+ {
+ $strReq = 'SELECT COUNT(post_id) ' .
+ 'FROM ' . $this->prefix . 'post';
+ $rs = $this->con->select($strReq);
+ $count = $rs->f(0);
+
+ $strReq = 'SELECT post_id, post_title, post_excerpt_xhtml, post_content_xhtml ' .
+ 'FROM ' . $this->prefix . 'post ';
+
+ if ($start !== null && $limit !== null) {
+ $strReq .= $this->con->limit($start, $limit);
+ }
+
+ $rs = $this->con->select($strReq, true);
+
+ $cur = $this->con->openCursor($this->prefix . 'post');
+
+ while ($rs->fetch()) {
+ $words = $rs->post_title . ' ' . $rs->post_excerpt_xhtml . ' ' .
+ $rs->post_content_xhtml;
+
+ $cur->post_words = implode(' ', text::splitWords($words));
+ $cur->update('WHERE post_id = ' . (integer) $rs->post_id);
+ $cur->clean();
+ }
+
+ if ($start + $limit > $count) {
+ return;
+ }
+ return $start + $limit;
+ }
+
+ /**
+ Recreates comments search engine index.
+
+ @param start integer Start comment index
+ @param limit integer Number of comments to index
+
+ @return integer $start and $limit sum
+ */
+ public function indexAllComments($start = null, $limit = null)
+ {
+ $strReq = 'SELECT COUNT(comment_id) ' .
+ 'FROM ' . $this->prefix . 'comment';
+ $rs = $this->con->select($strReq);
+ $count = $rs->f(0);
+
+ $strReq = 'SELECT comment_id, comment_content ' .
+ 'FROM ' . $this->prefix . 'comment ';
+
+ if ($start !== null && $limit !== null) {
+ $strReq .= $this->con->limit($start, $limit);
+ }
+
+ $rs = $this->con->select($strReq);
+
+ $cur = $this->con->openCursor($this->prefix . 'comment');
+
+ while ($rs->fetch()) {
+ $cur->comment_words = implode(' ', text::splitWords($rs->comment_content));
+ $cur->update('WHERE comment_id = ' . (integer) $rs->comment_id);
+ $cur->clean();
+ }
+
+ if ($start + $limit > $count) {
+ return;
+ }
+ return $start + $limit;
+ }
+
+ /**
+ Reinits nb_comment and nb_trackback in post table.
+ */
+ public function countAllComments()
+ {
+
+ $updCommentReq = 'UPDATE ' . $this->prefix . 'post P ' .
+ 'SET nb_comment = (' .
+ 'SELECT COUNT(C.comment_id) from ' . $this->prefix . 'comment C ' .
+ 'WHERE C.post_id = P.post_id AND C.comment_trackback <> 1 ' .
+ 'AND C.comment_status = 1 ' .
+ ')';
+ $updTrackbackReq = 'UPDATE ' . $this->prefix . 'post P ' .
+ 'SET nb_trackback = (' .
+ 'SELECT COUNT(C.comment_id) from ' . $this->prefix . 'comment C ' .
+ 'WHERE C.post_id = P.post_id AND C.comment_trackback = 1 ' .
+ 'AND C.comment_status = 1 ' .
+ ')';
+ $this->con->execute($updCommentReq);
+ $this->con->execute($updTrackbackReq);
+ }
+
+ /**
+ Empty templates cache directory
+ */
+ public function emptyTemplatesCache()
+ {
+ if (is_dir(DC_TPL_CACHE . '/cbtpl')) {
+ files::deltree(DC_TPL_CACHE . '/cbtpl');
+ }
+ }
+
+ /**
+ Return elapsed time since script has been started
+ @param $mtime float timestamp (microtime format) to evaluate delta from
+ current time is taken if null
+ @return float elapsed time
+ */
+ public function getElapsedTime($mtime = null)
+ {
+ if ($mtime !== null) {
+ return $mtime - $this->stime;
+ } else {
+ return microtime(true) - $this->stime;
+ }
+ }
+ //@}
+
+}
diff --git a/dotclear._no/inc/core/class.dc.error.php b/dotclear._no/inc/core/class.dc.error.php
new file mode 100644
index 0000000..54b022c
--- /dev/null
+++ b/dotclear._no/inc/core/class.dc.error.php
@@ -0,0 +1,124 @@
+\n%s\n";
+ /** @var string HTML error item pattern */
+ protected $html_item = "%s \n";
+ /** @var string HTML error single pattern */
+ protected $html_single = "%s
\n";
+
+ /**
+ * Object string representation. Returns errors stack.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ $res = '';
+
+ foreach ($this->errors as $msg) {
+ $res .= $msg . "\n";
+ }
+
+ return $res;
+ }
+
+ /**
+ * Adds an error to stack.
+ *
+ * @param string $msg Error message
+ */
+ public function add($msg)
+ {
+ $this->flag = true;
+ $this->errors[] = $msg;
+ }
+
+ /**
+ * Returns the value of flag property. True if errors stack is not empty
+ *
+ * @return boolean
+ */
+ public function flag()
+ {
+ return $this->flag;
+ }
+
+ /**
+ * Resets errors stack.
+ */
+ public function reset()
+ {
+ $this->flag = false;
+ $this->errors = [];
+ }
+
+ /**
+ * Returns errors property.
+ *
+ * @return array
+ */
+ public function getErrors()
+ {
+ return $this->errors;
+ }
+
+ /**
+ * Sets list and item properties.
+ *
+ * @param string $list HTML errors list pattern
+ * @param string $item HTML error item pattern
+ */
+ public function setHTMLFormat($list, $item, $single = null)
+ {
+ $this->html_list = $list;
+ $this->html_item = $item;
+ if ($single) {
+ $this->html_single = $single;
+ }
+ }
+
+ /**
+ * Returns errors stack as HTML.
+ *
+ * @return string
+ */
+ public function toHTML()
+ {
+ $res = '';
+
+ if ($this->flag) {
+ if (count($this->errors) == 1) {
+ $res = sprintf($this->html_single, $this->errors[0]);
+ } else {
+ foreach ($this->errors as $msg) {
+ $res .= sprintf($this->html_item, $msg);
+ }
+ $res = sprintf($this->html_list, $res);
+ }
+ }
+
+ return $res;
+ }
+}
diff --git a/dotclear._no/inc/core/class.dc.log.php b/dotclear._no/inc/core/class.dc.log.php
new file mode 100644
index 0000000..cc8612c
--- /dev/null
+++ b/dotclear._no/inc/core/class.dc.log.php
@@ -0,0 +1,194 @@
+dcCore dcCore instance
+ */
+ public function __construct($core)
+ {
+ $this->core = &$core;
+ $this->prefix = $core->prefix;
+ }
+
+ /**
+ Retrieves logs. $params is an array taking the following
+ optionnal parameters:
+
+ - blog_id: Get logs belonging to given blog ID
+ - user_id: Get logs belonging to given user ID
+ - log_ip: Get logs belonging to given IP address
+ - log_table: Get logs belonging to given log table
+ - order: Order of results (default "ORDER BY log_dt DESC")
+ - limit: Limit parameter
+
+ @param params array Parameters
+ @param count_only boolean Only counts results
+ @return record A record with some more capabilities
+ */
+ public function getLogs($params = [], $count_only = false)
+ {
+ if ($count_only) {
+ $f = 'COUNT(log_id)';
+ } else {
+ $f =
+ 'L.log_id, L.user_id, L.log_table, L.log_dt, ' .
+ 'L.log_ip, L.log_msg, L.blog_id, U.user_name, ' .
+ 'U.user_firstname, U.user_displayname, U.user_url';
+ }
+
+ $strReq = 'SELECT ' . $f . ' FROM ' . $this->prefix . 'log L ';
+
+ if (!$count_only) {
+ $strReq .=
+ 'LEFT JOIN ' . $this->prefix . 'user U ' .
+ 'ON U.user_id = L.user_id ';
+ }
+
+ if (!empty($params['blog_id'])) {
+ if ($params['blog_id'] === 'all') {
+ $strReq .= "WHERE NULL IS NULL ";
+ } else {
+ $strReq .= "WHERE L.blog_id = '" . $this->core->con->escape($params['blog_id']) . "' ";
+ }
+ } else {
+ $strReq .= "WHERE L.blog_id = '" . $this->core->blog->id . "' ";
+ }
+
+ if (!empty($params['user_id'])) {
+ $strReq .= 'AND L.user_id' . $this->core->con->in($params['user_id']);
+ }
+ if (!empty($params['log_ip'])) {
+ $strReq .= 'AND log_ip' . $this->core->con->in($params['log_ip']);
+ }
+ if (!empty($params['log_table'])) {
+ $strReq .= 'AND log_table' . $this->core->con->in($params['log_table']);
+ }
+
+ if (!$count_only) {
+ if (!empty($params['order'])) {
+ $strReq .= 'ORDER BY ' . $this->core->con->escape($params['order']) . ' ';
+ } else {
+ $strReq .= 'ORDER BY log_dt DESC ';
+ }
+ }
+
+ if (!empty($params['limit'])) {
+ $strReq .= $this->core->con->limit($params['limit']);
+ }
+
+ $rs = $this->core->con->select($strReq);
+ $rs->extend('rsExtLog');
+
+ return $rs;
+ }
+
+ /**
+ Creates a new log. Takes a cursor as input and returns the new log
+ ID.
+
+ @param cur cursor Log cursor
+ @return integer New log ID
+ */
+ public function addLog($cur)
+ {
+ $this->core->con->writeLock($this->prefix . 'log');
+
+ try
+ {
+ # Get ID
+ $rs = $this->core->con->select(
+ 'SELECT MAX(log_id) ' .
+ 'FROM ' . $this->prefix . 'log '
+ );
+
+ $cur->log_id = (integer) $rs->f(0) + 1;
+ $cur->blog_id = (string) $this->core->blog->id;
+ $cur->log_dt = date('Y-m-d H:i:s');
+
+ $this->getLogCursor($cur, $cur->log_id);
+
+ # --BEHAVIOR-- coreBeforeLogCreate
+ $this->core->callBehavior('coreBeforeLogCreate', $this, $cur);
+
+ $cur->insert();
+ $this->core->con->unlock();
+ } catch (Exception $e) {
+ $this->core->con->unlock();
+ throw $e;
+ }
+
+ # --BEHAVIOR-- coreAfterLogCreate
+ $this->core->callBehavior('coreAfterLogCreate', $this, $cur);
+
+ return $cur->log_id;
+ }
+
+ /**
+ Deletes a log.
+
+ @param id integer Log ID
+ */
+ public function delLogs($id, $all = false)
+ {
+ $strReq = $all ?
+ 'TRUNCATE TABLE ' . $this->prefix . 'log' :
+ 'DELETE FROM ' . $this->prefix . 'log WHERE log_id' . $this->core->con->in($id);
+
+ $this->core->con->execute($strReq);
+ }
+
+ private function getLogCursor($cur, $log_id = null)
+ {
+ if ($cur->log_msg === '') {
+ throw new Exception(__('No log message'));
+ }
+
+ if ($cur->log_table === null) {
+ $cur->log_table = 'none';
+ }
+
+ if ($cur->user_id === null) {
+ $cur->user_id = 'unknown';
+ }
+
+ if ($cur->log_dt === '' || $cur->log_dt === null) {
+ $cur->log_dt = date('Y-m-d H:i:s');
+ }
+
+ if ($cur->log_ip === null) {
+ $cur->log_ip = http::realIP();
+ }
+
+ $log_id = is_int($log_id) ? $log_id : $cur->log_id;
+ }
+}
+
+class rsExtLog
+{
+ public static function getUserCN($rs)
+ {
+ $user = dcUtils::getUserCN($rs->user_id, $rs->user_name,
+ $rs->user_firstname, $rs->user_displayname);
+
+ if ($user === 'unknown') {
+ $user = __('unknown');
+ }
+
+ return $user;
+ }
+}
diff --git a/dotclear._no/inc/core/class.dc.media.php b/dotclear._no/inc/core/class.dc.media.php
new file mode 100644
index 0000000..0cb2a98
--- /dev/null
+++ b/dotclear._no/inc/core/class.dc.media.php
@@ -0,0 +1,1209 @@
+dcCore dcCore instance
+ protected $con; ///< connection Database connection
+ protected $table; ///< string Media table name
+ protected $type; ///< string Media type filter
+ protected $postmedia;
+ protected $file_sort = 'name-asc';
+
+ protected $file_handler = []; ///< array Array of callbacks
+
+ public $thumb_tp = '%s/.%s_%s.jpg'; ///< string Thumbnail file pattern
+ public $thumb_tp_alpha = '%s/.%s_%s.png'; ///< string Thumbnail file pattern (with alpha layer)
+ public $thumb_tp_webp = '%s/.%s_%s.webp'; ///< string Thumbnail file pattern (webp)
+ /**
+ array Tubmnail sizes:
+ - m: medium image
+ - s: small image
+ - t: thumbnail image
+ - sq: square image
+ */
+ public $thumb_sizes = [
+ 'm' => [448, 'ratio', 'medium'],
+ 's' => [240, 'ratio', 'small'],
+ 't' => [100, 'ratio', 'thumbnail'],
+ 'sq' => [48, 'crop', 'square']
+ ];
+
+ public $icon_img = 'images/media/%s.png'; ///< string Icon file pattern
+
+ /**
+ Object constructor.
+
+ @param core dcCore dcCore instance
+ @param type string Media type filter
+ */
+ public function __construct($core, $type = '')
+ {
+ $this->core = &$core;
+ $this->con = &$core->con;
+ $this->postmedia = new dcPostMedia($core);
+
+ if ($this->core->blog == null) {
+ throw new Exception(__('No blog defined.'));
+ }
+
+ $this->table = $this->core->prefix . 'media';
+ $root = $this->core->blog->public_path;
+
+ if (preg_match('#^http(s)?://#', $this->core->blog->settings->system->public_url)) {
+ $root_url = rawurldecode($this->core->blog->settings->system->public_url);
+ } else {
+ $root_url = rawurldecode($this->core->blog->host . path::clean($this->core->blog->settings->system->public_url));
+ }
+
+ if (!is_dir($root)) {
+ # Check public directory
+ if ($core->auth->isSuperAdmin()) {
+ throw new Exception(__("There is no writable directory /public/ at the location set in about:config \"public_path\". You must create this directory with sufficient rights (or change this setting)."));
+ } else {
+ throw new Exception(__("There is no writable root directory for the media manager. You should contact your administrator."));
+ }
+ }
+
+ $this->type = $type;
+
+ parent::__construct($root, $root_url);
+ $this->chdir('');
+
+ $this->path = $this->core->blog->settings->system->public_path;
+
+ $this->addExclusion(DC_RC_PATH);
+ $this->addExclusion(dirname(__FILE__) . '/../');
+
+ $this->exclude_pattern = $core->blog->settings->system->media_exclusion;
+
+ # Event handlers
+ $this->addFileHandler('image/jpeg', 'create', [$this, 'imageThumbCreate']);
+ $this->addFileHandler('image/png', 'create', [$this, 'imageThumbCreate']);
+ $this->addFileHandler('image/gif', 'create', [$this, 'imageThumbCreate']);
+ $this->addFileHandler('image/webp', 'create', [$this, 'imageThumbCreate']);
+
+ $this->addFileHandler('image/png', 'update', [$this, 'imageThumbUpdate']);
+ $this->addFileHandler('image/jpeg', 'update', [$this, 'imageThumbUpdate']);
+ $this->addFileHandler('image/gif', 'update', [$this, 'imageThumbUpdate']);
+ $this->addFileHandler('image/webp', 'update', [$this, 'imageThumbUpdate']);
+
+ $this->addFileHandler('image/png', 'remove', [$this, 'imageThumbRemove']);
+ $this->addFileHandler('image/jpeg', 'remove', [$this, 'imageThumbRemove']);
+ $this->addFileHandler('image/gif', 'remove', [$this, 'imageThumbRemove']);
+ $this->addFileHandler('image/webp', 'remove', [$this, 'imageThumbRemove']);
+
+ $this->addFileHandler('image/jpeg', 'create', [$this, 'imageMetaCreate']);
+ $this->addFileHandler('image/webp', 'create', [$this, 'imageMetaCreate']);
+
+ $this->addFileHandler('image/jpeg', 'recreate', [$this, 'imageThumbCreate']);
+ $this->addFileHandler('image/png', 'recreate', [$this, 'imageThumbCreate']);
+ $this->addFileHandler('image/gif', 'recreate', [$this, 'imageThumbCreate']);
+ $this->addFileHandler('image/webp', 'recreate', [$this, 'imageThumbCreate']);
+
+ # Thumbnails sizes
+ $this->thumb_sizes['m'][0] = abs($core->blog->settings->system->media_img_m_size);
+ $this->thumb_sizes['s'][0] = abs($core->blog->settings->system->media_img_s_size);
+ $this->thumb_sizes['t'][0] = abs($core->blog->settings->system->media_img_t_size);
+
+ # Thumbnails sizes names
+ $this->thumb_sizes['m'][2] = __($this->thumb_sizes['m'][2]);
+ $this->thumb_sizes['s'][2] = __($this->thumb_sizes['s'][2]);
+ $this->thumb_sizes['t'][2] = __($this->thumb_sizes['t'][2]);
+ $this->thumb_sizes['sq'][2] = __($this->thumb_sizes['sq'][2]);
+
+ # --BEHAVIOR-- coreMediaConstruct
+ $this->core->callBehavior('coreMediaConstruct', $this);
+ }
+
+ /**
+ Changes working directory.
+
+ @param dir string Directory name.
+ */
+ public function chdir($dir)
+ {
+ parent::chdir($dir);
+ $this->relpwd = preg_replace('/^' . preg_quote($this->root, '/') . '\/?/', '', $this->pwd);
+ }
+
+ /**
+ Adds a new file handler for a given media type and event.
+
+ Available events are:
+ - create: file creation
+ - update: file update
+ - remove: file deletion
+
+ @param type string Media type
+ @param event string Event
+ @param function callback
+ */
+ public function addFileHandler($type, $event, $function)
+ {
+ if (is_callable($function)) {
+ $this->file_handler[$type][$event][] = $function;
+ }
+ }
+
+ protected function callFileHandler($type, $event, ...$args)
+ {
+ if (!empty($this->file_handler[$type][$event])) {
+ foreach ($this->file_handler[$type][$event] as $f) {
+ call_user_func_array($f, $args);
+ }
+ }
+ }
+
+ /**
+ Returns HTML breadCrumb for media manager navigation.
+
+ @param href string URL pattern
+ @param last string Last item pattern
+ @return string HTML code
+ */
+ public function breadCrumb($href, $last = '')
+ {
+ $res = '';
+ if ($this->relpwd && $this->relpwd != '.') {
+ $pwd = '';
+ $arr = explode('/', $this->relpwd);
+ $count = count($arr);
+ foreach ($arr as $v) {
+ if (($last != '') && (0 === --$count)) {
+ $res .= sprintf($last, $v);
+ } else {
+ $pwd .= rawurlencode($v) . '/';
+ $res .= '' . $v . ' / ';
+ }
+ }
+ }
+ return $res;
+
+ }
+
+ protected function fileRecord($rs)
+ {
+ if ($rs->isEmpty()) {return;}
+
+ if (!$this->isFileExclude($this->root . '/' . $rs->media_file) && is_file($this->root . '/' . $rs->media_file)) {
+ $f = new fileItem($this->root . '/' . $rs->media_file, $this->root, $this->root_url);
+
+ if ($this->type && $f->type_prefix != $this->type) {
+ return;
+ }
+
+ $meta = @simplexml_load_string($rs->media_meta);
+
+ $f->editable = true;
+ $f->media_id = $rs->media_id;
+ $f->media_title = $rs->media_title;
+ $f->media_meta = $meta instanceof SimpleXMLElement ? $meta : simplexml_load_string(' ');
+ $f->media_user = $rs->user_id;
+ $f->media_priv = (boolean) $rs->media_private;
+ $f->media_dt = strtotime($rs->media_dt);
+ $f->media_dtstr = dt::str('%Y-%m-%d %H:%M', $f->media_dt);
+
+ $f->media_image = false;
+
+ if (!$this->core->auth->check('media_admin', $this->core->blog->id)
+ && $this->core->auth->userID() != $f->media_user) {
+ $f->del = false;
+ $f->editable = false;
+ }
+
+ $type_prefix = explode('/', $f->type);
+ $type_prefix = $type_prefix[0];
+
+ switch ($type_prefix) {
+ case 'image':
+ $f->media_image = true;
+ $f->media_icon = 'image';
+ break;
+ case 'audio':
+ $f->media_icon = 'audio';
+ break;
+ case 'text':
+ $f->media_icon = 'text';
+ break;
+ case 'video':
+ $f->media_icon = 'video';
+ break;
+ default:
+ $f->media_icon = 'blank';
+ }
+ switch ($f->type) {
+ case 'application/msword':
+ case 'application/vnd.oasis.opendocument.text':
+ case 'application/vnd.sun.xml.writer':
+ case 'application/pdf':
+ case 'application/postscript':
+ $f->media_icon = 'document';
+ break;
+ case 'application/msexcel':
+ case 'application/vnd.oasis.opendocument.spreadsheet':
+ case 'application/vnd.sun.xml.calc':
+ $f->media_icon = 'spreadsheet';
+ break;
+ case 'application/mspowerpoint':
+ case 'application/vnd.oasis.opendocument.presentation':
+ case 'application/vnd.sun.xml.impress':
+ $f->media_icon = 'presentation';
+ break;
+ case 'application/x-debian-package':
+ case 'application/x-bzip':
+ case 'application/x-gzip':
+ case 'application/x-java-archive':
+ case 'application/rar':
+ case 'application/x-redhat-package-manager':
+ case 'application/x-tar':
+ case 'application/x-gtar':
+ case 'application/zip':
+ $f->media_icon = 'package';
+ break;
+ case 'application/octet-stream':
+ $f->media_icon = 'executable';
+ break;
+ case 'application/x-shockwave-flash':
+ $f->media_icon = 'video';
+ break;
+ case 'application/ogg':
+ $f->media_icon = 'audio';
+ break;
+ case 'text/html':
+ $f->media_icon = 'html';
+ break;
+ }
+
+ $f->media_type = $f->media_icon;
+ $f->media_icon = sprintf($this->icon_img, $f->media_icon);
+
+ # Thumbnails
+ $f->media_thumb = [];
+ $p = path::info($f->relname);
+
+ $alpha = strtolower($p['extension']) === 'png';
+ $webp = strtolower($p['extension']) === 'webp';
+
+ $thumb = sprintf(
+ ($alpha ? $this->thumb_tp_alpha :
+ ($webp ? $this->thumb_tp_webp : $this->thumb_tp)),
+ $this->root . '/' . $p['dirname'], $p['base'], '%s');
+ $thumb_url = sprintf(
+ ($alpha ? $this->thumb_tp_alpha :
+ ($webp ? $this->thumb_tp_webp : $this->thumb_tp)),
+ $this->root_url . $p['dirname'], $p['base'], '%s');
+
+ # Cleaner URLs
+ $thumb_url = preg_replace('#\./#', '/', $thumb_url);
+ $thumb_url = preg_replace('#(?thumb_tp, $this->root . '/' . $p['dirname'], $p['base'], '%s');
+ $thumb_url_alt = sprintf($this->thumb_tp, $this->root_url . $p['dirname'], $p['base'], '%s');
+ # Cleaner URLs
+ $thumb_url_alt = preg_replace('#\./#', '/', $thumb_url_alt);
+ $thumb_url_alt = preg_replace('#(?thumb_sizes as $suffix => $s) {
+ if (file_exists(sprintf($thumb, $suffix))) {
+ $f->media_thumb[$suffix] = sprintf($thumb_url, $suffix);
+ } elseif (($alpha || $webp) && file_exists(sprintf($thumb_alt, $suffix))) {
+ $f->media_thumb[$suffix] = sprintf($thumb_url_alt, $suffix);
+ }
+ }
+
+ if (isset($f->media_thumb['sq']) && $f->media_type == 'image') {
+ $f->media_icon = $f->media_thumb['sq'];
+ }
+
+ return $f;
+ }
+
+ return;
+ }
+
+ public function setFileSort($type = 'name')
+ {
+ if (in_array($type, ['name-asc', 'name-desc', 'date-asc', 'date-desc'])) {
+ $this->file_sort = $type;
+ }
+ }
+
+ protected function sortFileHandler($a, $b)
+ {
+ if (is_null($a) || is_null($b)) {
+ return (is_null($a) ? 1 : -1);
+ }
+ switch ($this->file_sort) {
+ case 'date-asc':
+ if ($a->media_dt == $b->media_dt) {
+ return 0;
+ }
+ return ($a->media_dt < $b->media_dt) ? -1 : 1;
+ case 'date-desc':
+ if ($a->media_dt == $b->media_dt) {
+ return 0;
+ }
+ return ($a->media_dt > $b->media_dt) ? -1 : 1;
+ case 'name-desc':
+ return strcasecmp($b->basename, $a->basename);
+ case 'name-asc':
+ default:
+ return strcasecmp($a->basename, $b->basename);
+ }
+ }
+
+ /**
+ Gets current working directory content (using filesystem)
+
+ */
+ public function getFSDir()
+ {
+ parent::getDir();
+ }
+
+ /**
+ Gets current working directory content.
+
+ @param type string Media type filter
+ */
+ public function getDir($type = null)
+ {
+ if ($type) {
+ $this->type = $type;
+ }
+
+ $media_dir = $this->relpwd ?: '.';
+
+ $strReq =
+ 'SELECT media_file, media_id, media_path, media_title, media_meta, media_dt, ' .
+ 'media_creadt, media_upddt, media_private, user_id ' .
+ 'FROM ' . $this->table . ' ' .
+ "WHERE media_path = '" . $this->path . "' " .
+ "AND media_dir = '" . $this->con->escape($media_dir) . "' ";
+
+ if (!$this->core->auth->check('media_admin', $this->core->blog->id)) {
+ $strReq .= 'AND (media_private <> 1 ';
+
+ if ($this->core->auth->userID()) {
+ $strReq .= "OR user_id = '" . $this->con->escape($this->core->auth->userID()) . "'";
+ }
+ $strReq .= ') ';
+ }
+
+ $rs = $this->con->select($strReq);
+
+ parent::getDir();
+
+ $f_res = [];
+ $p_dir = $this->dir;
+
+ # If type is set, remove items from p_dir
+ if ($this->type) {
+ foreach ($p_dir['files'] as $k => $f) {
+ if ($f->type_prefix != $this->type) {
+ unset($p_dir['files'][$k]);
+ }
+ }
+ }
+
+ $f_reg = [];
+
+ while ($rs->fetch()) {
+ # File in subdirectory, forget about it!
+ if (dirname($rs->media_file) != '.' && dirname($rs->media_file) != $this->relpwd) {
+ continue;
+ }
+
+ if ($this->inFiles($rs->media_file)) {
+ $f = $this->fileRecord($rs);
+ if ($f !== null) {
+ if (isset($f_reg[$rs->media_file])) {
+ # That media is duplicated in the database,
+ # time to do a bit of house cleaning.
+ $this->con->execute(
+ 'DELETE FROM ' . $this->table . ' ' .
+ "WHERE media_id = " . $this->fileRecord($rs)->media_id
+ );
+ } else {
+ $f_res[] = $this->fileRecord($rs);
+ $f_reg[$rs->media_file] = 1;
+ }
+ }
+ } elseif (!empty($p_dir['files']) && $this->relpwd == '') {
+ # Physical file does not exist remove it from DB
+ # Because we don't want to erase everything on
+ # dotclear upgrade, do it only if there are files
+ # in directory and directory is root
+ $this->con->execute(
+ 'DELETE FROM ' . $this->table . ' ' .
+ "WHERE media_path = '" . $this->con->escape($this->path) . "' " .
+ "AND media_file = '" . $this->con->escape($rs->media_file) . "' "
+ );
+ $this->callFileHandler(files::getMimeType($rs->media_file), 'remove', $this->pwd . '/' . $rs->media_file);
+ }
+ }
+
+ $this->dir['files'] = $f_res;
+ foreach ($this->dir['dirs'] as $k => $v) {
+ $v->media_icon = sprintf($this->icon_img, ($v->parent ? 'folder-up' : 'folder'));
+ }
+
+ # Check files that don't exist in database and create them
+ if ($this->core->auth->check('media,media_admin', $this->core->blog->id)) {
+ foreach ($p_dir['files'] as $f) {
+ if (!isset($f_reg[$f->relname])) {
+ if (($id = $this->createFile($f->basename, null, false, null, false)) !== false) {
+ $this->dir['files'][] = $this->getFile($id);
+ }
+ }
+ }
+ }
+ try {
+ usort($this->dir['files'], [$this, 'sortFileHandler']);
+ } catch (Exception $e) {}
+ }
+
+ /**
+ Gets file by its id. Returns a filteItem object.
+
+ @param id integer File ID
+ @return fileItem
+ */
+ public function getFile($id)
+ {
+ $strReq =
+ 'SELECT media_id, media_path, media_title, ' .
+ 'media_file, media_meta, media_dt, media_creadt, ' .
+ 'media_upddt, media_private, user_id ' .
+ 'FROM ' . $this->table . ' ' .
+ "WHERE media_path = '" . $this->path . "' " .
+ 'AND media_id = ' . (integer) $id . ' ';
+
+ if (!$this->core->auth->check('media_admin', $this->core->blog->id)) {
+ $strReq .= 'AND (media_private <> 1 ';
+
+ if ($this->core->auth->userID()) {
+ $strReq .= "OR user_id = '" . $this->con->escape($this->core->auth->userID()) . "'";
+ }
+ $strReq .= ') ';
+ }
+
+ $rs = $this->con->select($strReq);
+ return $this->fileRecord($rs);
+ }
+
+ /**
+ Search into media db (only).
+
+ @param query string Search query
+ @return boolean true or false if nothing found
+ */
+ public function searchMedia($query)
+ {
+ if ($query == '') {
+ return false;
+ }
+
+ $strReq =
+ 'SELECT media_file, media_id, media_path, media_title, media_meta, media_dt, ' .
+ 'media_creadt, media_upddt, media_private, user_id ' .
+ 'FROM ' . $this->table . ' ' .
+ "WHERE media_path = '" . $this->path . "' " .
+ "AND (media_title LIKE '%" . $this->con->escape($query) . "%' " .
+ " OR media_file LIKE '%" . $this->con->escape($query) . "%' " .
+ " OR media_meta LIKE '%" . $this->con->escape($query) . "% ')";
+
+ if (!$this->core->auth->check('media_admin', $this->core->blog->id)) {
+ $strReq .= 'AND (media_private <> 1 ';
+
+ if ($this->core->auth->userID()) {
+ $strReq .= "OR user_id = '" . $this->con->escape($this->core->auth->userID()) . "'";
+ }
+ $strReq .= ') ';
+ }
+
+ $rs = $this->con->select($strReq);
+
+ $this->dir = ['dirs' => [], 'files' => []];
+ $f_res = [];
+ while ($rs->fetch()) {
+ $fr = $this->fileRecord($rs);
+ if ($fr) {
+ $f_res[] = $fr;
+ }
+ }
+ $this->dir['files'] = $f_res;
+
+ try {
+ usort($this->dir['files'], [$this, 'sortFileHandler']);
+ } catch (Exception $e) {}
+
+ return (count($f_res) > 0 ? true : false);
+ }
+
+ /**
+ Returns media items attached to a blog post. Result is an array containing
+ fileItems objects.
+
+ @param post_id integer Post ID
+ @param media_id integer Optionnal media ID
+ @param link_type string Optionnal link type
+ @return array Array of fileItems
+ */
+ public function getPostMedia($post_id, $media_id = null, $link_type = null)
+ {
+ $params = [
+ 'post_id' => $post_id,
+ 'media_path' => $this->path
+ ];
+ if ($media_id) {
+ $params['media_id'] = (integer) $media_id;
+ }
+ if ($link_type) {
+ $params['link_type'] = $link_type;
+ }
+ $rs = $this->postmedia->getPostMedia($params);
+
+ $res = [];
+
+ while ($rs->fetch()) {
+ $f = $this->fileRecord($rs);
+ if ($f !== null) {
+ $res[] = $f;
+ }
+ }
+
+ return $res;
+ }
+
+ /**
+ @deprecated since version 2.4
+ @see dcPostMedia::addPostMedia
+ */
+ public function addPostMedia($post_id, $media_id, $link_type = 'attachment')
+ {
+ $this->postmedia->addPostMedia($post_id, $media_id, $link_type);
+ }
+
+ /**
+ @deprecated since version 2.4
+ @see dcPostMedia::removePostMedia
+ */
+ public function removePostMedia($post_id, $media_id, $link_type = 'attachment')
+ {
+ $this->postmedia->removePostMedia($post_id, $media_id, $link_type);
+ }
+
+ /**
+ Rebuilds database items collection. Optional $pwd parameter is
+ the path where to start rebuild.
+
+ @param pwd string Directory to rebuild
+ */
+ public function rebuild($pwd = '')
+ {
+ if (!$this->core->auth->isSuperAdmin()) {
+ throw new Exception(__('You are not a super administrator.'));
+ }
+
+ $this->chdir($pwd);
+ parent::getDir();
+
+ $dir = $this->dir;
+
+ foreach ($dir['dirs'] as $d) {
+ if (!$d->parent) {
+ $this->rebuild($d->relname, false);
+ }
+ }
+
+ foreach ($dir['files'] as $f) {
+ $this->chdir(dirname($f->relname));
+ $this->createFile($f->basename);
+ }
+
+ $this->rebuildDB($pwd);
+ }
+
+ protected function rebuildDB($pwd)
+ {
+ $media_dir = $pwd ?: '.';
+
+ $strReq =
+ 'SELECT media_file, media_id ' .
+ 'FROM ' . $this->table . ' ' .
+ "WHERE media_path = '" . $this->path . "' " .
+ "AND media_dir = '" . $this->con->escape($media_dir) . "' ";
+
+ $rs = $this->con->select($strReq);
+
+ $delReq = 'DELETE FROM ' . $this->table . ' ' .
+ 'WHERE media_id IN (%s) ';
+ $del_ids = [];
+
+ while ($rs->fetch()) {
+ if (!is_file($this->root . '/' . $rs->media_file)) {
+ $del_ids[] = (integer) $rs->media_id;
+ }
+ }
+
+ if (!empty($del_ids)) {
+ $this->con->execute(sprintf($delReq, implode(',', $del_ids)));
+ }
+ }
+
+ public function makeDir($d)
+ {
+ $d = files::tidyFileName($d);
+ parent::makeDir($d);
+ }
+
+ /**
+ Creates or updates a file in database. Returns new media ID or false if
+ file does not exist.
+
+ @param name string File name (relative to working directory)
+ @param title string File title
+ @param private boolean File is private
+ @param dt string File date
+ @return integer New media ID
+ */
+ public function createFile($name, $title = null, $private = false, $dt = null, $force = true)
+ {
+ if (!$this->core->auth->check('media,media_admin', $this->core->blog->id)) {
+ throw new Exception(__('Permission denied.'));
+ }
+
+ $file = $this->pwd . '/' . $name;
+ if (!file_exists($file)) {
+ return false;
+ }
+
+ $media_file = $this->relpwd ? path::clean($this->relpwd . '/' . $name) : path::clean($name);
+ $media_type = files::getMimeType($name);
+
+ $cur = $this->con->openCursor($this->table);
+
+ $strReq = 'SELECT media_id ' .
+ 'FROM ' . $this->table . ' ' .
+ "WHERE media_path = '" . $this->con->escape($this->path) . "' " .
+ "AND media_file = '" . $this->con->escape($media_file) . "' ";
+
+ $rs = $this->con->select($strReq);
+
+ if ($rs->isEmpty()) {
+ $this->con->writeLock($this->table);
+ try
+ {
+ $rs = $this->con->select('SELECT MAX(media_id) FROM ' . $this->table);
+ $media_id = (integer) $rs->f(0) + 1;
+
+ $cur->media_id = $media_id;
+ $cur->user_id = (string) $this->core->auth->userID();
+ $cur->media_path = (string) $this->path;
+ $cur->media_file = (string) $media_file;
+ $cur->media_dir = (string) dirname($media_file);
+ $cur->media_creadt = date('Y-m-d H:i:s');
+ $cur->media_upddt = date('Y-m-d H:i:s');
+
+ $cur->media_title = !$title ? (string) $name : (string) $title;
+ $cur->media_private = (integer) (boolean) $private;
+
+ if ($dt) {
+ $cur->media_dt = (string) $dt;
+ } else {
+ $cur->media_dt = strftime('%Y-%m-%d %H:%M:%S', filemtime($file));
+ }
+
+ try {
+ $cur->insert();
+ } catch (Exception $e) {
+ @unlink($name);
+ throw $e;
+ }
+ $this->con->unlock();
+ } catch (Exception $e) {
+ $this->con->unlock();
+ throw $e;
+ }
+ } else {
+ $media_id = (integer) $rs->media_id;
+
+ $cur->media_upddt = date('Y-m-d H:i:s');
+
+ $cur->update('WHERE media_id = ' . $media_id);
+ }
+
+ $this->callFileHandler($media_type, 'create', $cur, $name, $media_id, $force);
+
+ return $media_id;
+ }
+
+ /**
+ Updates a file in database.
+
+ @param file fileItem Current fileItem object
+ @param newFile fileItem New fileItem object
+ */
+ public function updateFile($file, $newFile)
+ {
+ if (!$this->core->auth->check('media,media_admin', $this->core->blog->id)) {
+ throw new Exception(__('Permission denied.'));
+ }
+
+ $id = (integer) $file->media_id;
+
+ if (!$id) {
+ throw new Exception('No file ID');
+ }
+
+ if (!$this->core->auth->check('media_admin', $this->core->blog->id)
+ && $this->core->auth->userID() != $file->media_user) {
+ throw new Exception(__('You are not the file owner.'));
+ }
+
+ $cur = $this->con->openCursor($this->table);
+
+ # We need to tidy newFile basename. If dir isn't empty, concat to basename
+ $newFile->relname = files::tidyFileName($newFile->basename);
+ if ($newFile->dir) {
+ $newFile->relname = $newFile->dir . '/' . $newFile->relname;
+ }
+
+ if ($file->relname != $newFile->relname) {
+ $newFile->file = $this->root . '/' . $newFile->relname;
+
+ if ($this->isFileExclude($newFile->relname)) {
+ throw new Exception(__('This file is not allowed.'));
+ }
+
+ if (file_exists($newFile->file)) {
+ throw new Exception(__('New file already exists.'));
+ }
+
+ $this->moveFile($file->relname, $newFile->relname);
+
+ $cur->media_file = (string) $newFile->relname;
+ $cur->media_dir = (string) dirname($newFile->relname);
+ }
+
+ $cur->media_title = (string) $newFile->media_title;
+ $cur->media_dt = (string) $newFile->media_dtstr;
+ $cur->media_upddt = date('Y-m-d H:i:s');
+ $cur->media_private = (integer) $newFile->media_priv;
+
+ if ($newFile->media_meta instanceof SimpleXMLElement) {
+ $cur->media_meta = $newFile->media_meta->asXML();
+ }
+
+ $cur->update('WHERE media_id = ' . $id);
+
+ $this->callFileHandler($file->type, 'update', $file, $newFile);
+ }
+
+ /**
+ Uploads a file.
+
+ @param tmp string Full path of temporary uploaded file
+ @param name string File name (relative to working directory)
+ @param title string File title
+ @param private boolean File is private
+ */
+ public function uploadFile($tmp, $name, $title = null, $private = false, $overwrite = false)
+ {
+ if (!$this->core->auth->check('media,media_admin', $this->core->blog->id)) {
+ throw new Exception(__('Permission denied.'));
+ }
+
+ $name = files::tidyFileName($name);
+
+ parent::uploadFile($tmp, $name, $overwrite);
+
+ return $this->createFile($name, $title, $private);
+ }
+
+ /**
+ Creates a file from binary content.
+
+ @param name string File name (relative to working directory)
+ @param bits string Binary file content
+ */
+ public function uploadBits($name, $bits)
+ {
+ if (!$this->core->auth->check('media,media_admin', $this->core->blog->id)) {
+ throw new Exception(__('Permission denied.'));
+ }
+
+ $name = files::tidyFileName($name);
+
+ parent::uploadBits($name, $bits);
+
+ return $this->createFile($name, null, null);
+ }
+
+ /**
+ Removes a file.
+
+ @param f fileItem fileItem object
+ */
+ public function removeFile($f)
+ {
+ if (!$this->core->auth->check('media,media_admin', $this->core->blog->id)) {
+ throw new Exception(__('Permission denied.'));
+ }
+
+ $media_file = $this->relpwd ? path::clean($this->relpwd . '/' . $f) : path::clean($f);
+
+ $strReq = 'DELETE FROM ' . $this->table . ' ' .
+ "WHERE media_path = '" . $this->con->escape($this->path) . "' " .
+ "AND media_file = '" . $this->con->escape($media_file) . "' ";
+
+ if (!$this->core->auth->check('media_admin', $this->core->blog->id)) {
+ $strReq .= "AND user_id = '" . $this->con->escape($this->core->auth->userID()) . "'";
+ }
+
+ $this->con->execute($strReq);
+
+ if ($this->con->changes() == 0) {
+ throw new Exception(__('File does not exist in the database.'));
+ }
+
+ parent::removeFile($f);
+
+ $this->callFileHandler(files::getMimeType($media_file), 'remove', $f);
+ }
+
+ /**
+ * Root directories
+ *
+ * Returns an array of directory under {@link $root} directory.
+ *
+ * @uses fileItem
+ * @return array
+ */
+ public function getDBDirs()
+ {
+ $media_dir = $this->relpwd ?: '.';
+
+ $strReq =
+ 'SELECT distinct media_dir ' .
+ 'FROM ' . $this->table . ' ' .
+ "WHERE media_path = '" . $this->path . "'";
+ $rs = $this->con->select($strReq);
+ while ($rs->fetch()) {
+ if (is_dir($this->root . '/' . $rs->media_dir)) {
+ $dir[] = ($rs->media_dir == '.' ? '' : $rs->media_dir);
+ }
+
+ }
+
+ return $dir;
+ }
+
+ /**
+ Extract zip file in current location
+
+ @param f fileRecord fileRecord object
+ */
+ public function inflateZipFile($f, $create_dir = true)
+ {
+ $zip = new fileUnzip($f->file);
+ $zip->setExcludePattern($this->exclude_pattern);
+ $list = $zip->getList(false, '#(^|/)(__MACOSX|\.svn|\.hg.*|\.git.*|\.DS_Store|\.directory|Thumbs\.db)(/|$)#');
+
+ if ($create_dir) {
+ $zip_root_dir = $zip->getRootDir();
+ if ($zip_root_dir != false) {
+ $destination = $zip_root_dir;
+ $target = $f->dir;
+ } else {
+ $destination = preg_replace('/\.([^.]+)$/', '', $f->basename);
+ $target = $f->dir . '/' . $destination;
+ }
+
+ if (is_dir($f->dir . '/' . $destination)) {
+ throw new Exception(sprintf(__('Extract destination directory %s already exists.'), dirname($f->relname) . '/' . $destination));
+ }
+ } else {
+ $target = $f->dir;
+ $destination = '';
+ }
+
+ $zip->unzipAll($target);
+ $zip->close();
+
+ // Clean-up all extracted filenames
+ $clean = function ($name) {
+ $n = text::deaccent($name);
+ $n = preg_replace('/^[.]/u', '', $n);
+ return preg_replace('/[^A-Za-z0-9._\-\/]/u', '_', $n);
+ };
+ foreach ($list as $zk => $zv) {
+ // Check if extracted file exists
+ $zf = $target . '/' . $zk;
+ if (!$zv['is_dir'] && file_exists($zf)) {
+ $zt = $clean($zf);
+ if ($zt != $zf) {
+ rename($zf, $zt);
+ }
+ }
+ }
+
+ return dirname($f->relname) . '/' . $destination;
+ }
+
+ /**
+ Returns zip file content
+
+ @param f fileRecord fileRecord object
+ @return array
+ */
+ public function getZipContent($f)
+ {
+ $zip = new fileUnzip($f->file);
+ $list = $zip->getList(false, '#(^|/)(__MACOSX|\.svn|\.hg.*|\.git.*|\.DS_Store|\.directory|Thumbs\.db)(/|$)#');
+ $zip->close();
+ return $list;
+ }
+
+ /**
+ Calls file handlers registered for recreate event
+
+ @param f fileItem fileItem object
+ */
+ public function mediaFireRecreateEvent($f)
+ {
+ $media_type = files::getMimeType($f->basename);
+ $this->callFileHandler($media_type, 'recreate', null, $f->basename); // Args list to be completed as necessary (Franck)
+ }
+
+ /* Image handlers
+ ------------------------------------------------------- */
+ public function imageThumbCreate($cur, $f, $force = true)
+ {
+ $file = $this->pwd . '/' . $f;
+
+ if (!file_exists($file)) {
+ return false;
+ }
+
+ $p = path::info($file);
+ $alpha = strtolower($p['extension']) === 'png';
+ $webp = strtolower($p['extension']) === 'webp';
+ $thumb = sprintf(($alpha ? $this->thumb_tp_alpha :
+ ($webp ? $this->thumb_tp_webp :
+ $this->thumb_tp)),
+ $p['dirname'], $p['base'], '%s');
+
+ try
+ {
+ $img = new imageTools();
+ $img->loadImage($file);
+
+ $w = $img->getW();
+ $h = $img->getH();
+
+ if ($force) {
+ $this->imageThumbRemove($f);
+ }
+
+ foreach ($this->thumb_sizes as $suffix => $s) {
+ $thumb_file = sprintf($thumb, $suffix);
+ if (!file_exists($thumb_file) && $s[0] > 0 &&
+ ($suffix == 'sq' || $w > $s[0] || $h > $s[0])) {
+ $rate = ($s[0] < 100 ? 95 : ($s[0] < 600 ? 90 : 85));
+ $img->resize($s[0], $s[0], $s[1]);
+ $img->output(($alpha || $webp ? strtolower($p['extension']) : 'jpeg'), $thumb_file, $rate);
+ $img->loadImage($file);
+ }
+ }
+ $img->close();
+ } catch (Exception $e) {
+ if ($cur === null) {
+ # Called only if cursor is null (public call)
+ throw $e;
+ }
+ }
+ }
+
+ protected function imageThumbUpdate($file, $newFile)
+ {
+ if ($file->relname != $newFile->relname) {
+ $p = path::info($file->relname);
+ $alpha = strtolower($p['extension']) === 'png';
+ $webp = strtolower($p['extension']) === 'webp';
+ $thumb_old = sprintf(($alpha ? $this->thumb_tp_alpha :
+ ($webp ? $this->thumb_tp_webp :
+ $this->thumb_tp)),
+ $p['dirname'], $p['base'], '%s');
+
+ $p = path::info($newFile->relname);
+ $alpha = strtolower($p['extension']) === 'png';
+ $webp = strtolower($p['extension']) === 'webp';
+ $thumb_new = sprintf(($alpha ? $this->thumb_tp_alpha :
+ ($webp ? $this->thumb_tp_webp :
+ $this->thumb_tp)),
+ $p['dirname'], $p['base'], '%s');
+
+ foreach ($this->thumb_sizes as $suffix => $s) {
+ try {
+ parent::moveFile(sprintf($thumb_old, $suffix), sprintf($thumb_new, $suffix));
+ } catch (Exception $e) {}
+ }
+ }
+ }
+
+ public function imageThumbRemove($f)
+ {
+ $p = path::info($f);
+ $alpha = strtolower($p['extension']) === 'png';
+ $webp = strtolower($p['extension']) === 'webp';
+ $thumb = sprintf(($alpha ? $this->thumb_tp_alpha :
+ ($webp ? $this->thumb_tp_webp :
+ $this->thumb_tp)),
+ '', $p['base'], '%s');
+
+ foreach ($this->thumb_sizes as $suffix => $s) {
+ try {
+ parent::removeFile(sprintf($thumb, $suffix));
+ } catch (Exception $e) {}
+ }
+ }
+
+ protected function imageMetaCreate($cur, $f, $id)
+ {
+ $file = $this->pwd . '/' . $f;
+
+ if (!file_exists($file)) {
+ return false;
+ }
+
+ $xml = new xmlTag('meta');
+ $meta = imageMeta::readMeta($file);
+ $xml->insertNode($meta);
+
+ $c = $this->core->con->openCursor($this->table);
+ $c->media_meta = $xml->toXML();
+
+ if ($cur->media_title !== null && $cur->media_title == basename($cur->media_file)) {
+ if ($meta['Title']) {
+ $c->media_title = $meta['Title'];
+ }
+ }
+
+ if ($meta['DateTimeOriginal'] && $cur->media_dt === '') {
+ # We set picture time to user timezone
+ $media_ts = strtotime($meta['DateTimeOriginal']);
+ if ($media_ts !== false) {
+ $o = dt::getTimeOffset($this->core->auth->getInfo('user_tz'), $media_ts);
+ $c->media_dt = dt::str('%Y-%m-%d %H:%M:%S', $media_ts + $o);
+ }
+ }
+
+ $c->update('WHERE media_id = ' . $id);
+ }
+
+ /**
+ Returns HTML code for audio player (HTML5, Flash player fallback is obsolete since 2.15)
+
+ @param type string audio mime type
+ @param url string audio URL to play
+ @param player string Player URL (flash player fallback, obsolete)
+ @param args array Player parameters (flash player fallback, obsolete)
+ @param fallback boolean Include Flash player fallback (obsolete)
+ @param preload boolean Add preload="auto" attribute if true, else preload="none"
+ @return string
+ */
+ public static function audioPlayer($type, $url, $player = null, $args = null, $fallback = false, $preload = true)
+ {
+ return
+ '' .
+ '' .
+ ' ';
+ }
+
+ /**
+ Returns HTML code for video player (HTML5, Flash player fallback is obsolete since 2.15)
+
+ @param type string video mime type
+ @param url string video URL to play
+ @param player string Player URL (flash player fallback, obsolete)
+ @param args array Player parameters (flash player fallback, obsolete)
+ @param fallback boolean Include Flash player fallback (if not .flv, obsolete)
+ @param preload boolean Add preload="auto" attribute if true, else preload="none"
+ @return string
+ */
+ public static function videoPlayer($type, $url, $player = null, $args = null, $fallback = false, $preload = true)
+ {
+ $video = '';
+
+ if ($type != 'video/x-flv') {
+ // Cope with width and height, if given
+ $width = 400;
+ $height = 300;
+ if (is_array($args)) {
+ if (!empty($args['width']) && $args['width']) {
+ $width = (int) $args['width'];
+ }
+ if (!empty($args['height']) && $args['height']) {
+ $height = (int) $args['height'];
+ }
+ }
+
+ $video =
+ '' .
+ '' .
+ ' ';
+ }
+
+ return $video;
+ }
+
+ /**
+ Returns HTML code for MP3 player (Flash player fallback is obsolete since 2.15)
+
+ @param url string MP3 URL to play
+ @param player string Player URL
+ @param args array Player parameters
+ @param fallback boolean Include Flash player fallback (obsolete)
+ @param preload boolean Add preload="auto" attribute if true, else preload="none"
+ @return string
+ */
+ public static function mp3player($url, $player = null, $args = null, $fallback = false, $preload = true)
+ {
+ return
+ '' .
+ '' .
+ ' ';
+ }
+
+ /**
+ Returns HTML code for FLV player (obsolete since 2.15)
+
+ @param url string FLV URL to play
+ @param player string Player URL
+ @param args array Player parameters
+ @return string
+ */
+ public static function flvplayer($url, $player = null, $args = null)
+ {
+ return '';
+ }
+}
diff --git a/dotclear._no/inc/core/class.dc.meta.php b/dotclear._no/inc/core/class.dc.meta.php
new file mode 100644
index 0000000..bb57251
--- /dev/null
+++ b/dotclear._no/inc/core/class.dc.meta.php
@@ -0,0 +1,613 @@
+dcCore dcCore instance
+ private $con; ///< connection Database connection object
+ private $table; ///< string Media table name
+
+ /**
+ Object constructor.
+
+ @param core dcCore dcCore instance
+ */
+ public function __construct($core)
+ {
+ $this->core = &$core;
+ $this->con = &$this->core->con;
+ $this->table = $this->core->prefix . 'meta';
+ }
+
+ /**
+ Splits up comma-separated values into an array of
+ unique, URL-proof metadata values.
+
+ @param str string Comma-separated metadata.
+
+ @return Array The array of sanitized metadata
+ */
+ public function splitMetaValues($str)
+ {
+ $res = [];
+ foreach (explode(',', $str) as $i => $tag) {
+ $tag = trim($tag);
+ $tag = self::sanitizeMetaID($tag);
+
+ if ($tag != false) {
+ $res[$i] = $tag;
+ }
+ }
+
+ return array_unique($res);
+ }
+
+ /**
+ Make a metadata ID URL-proof.
+
+ @param str string the metadata ID.
+
+ @return string The sanitized metadata
+ */
+ public static function sanitizeMetaID($str)
+ {
+ return text::tidyURL($str, false, true);
+ }
+
+ /**
+ Converts serialized metadata (for instance in dc_post post_meta)
+ into a meta array.
+
+ @param str string the serialized metadata.
+
+ @return Array the resulting array of post meta
+ */
+ public function getMetaArray($str)
+ {
+ $meta = @unserialize($str);
+
+ if (!is_array($meta)) {
+ return [];
+ }
+
+ return $meta;
+ }
+
+ /**
+ Converts serialized metadata (for instance in dc_post post_meta)
+ into a comma-separated meta list for a given type.
+
+ @param str string the serialized metadata.
+ @param type string meta type to retrieve metaIDs from.
+
+ @return string the comma-separated list of meta
+ */
+ public function getMetaStr($str, $type)
+ {
+ $meta = $this->getMetaArray($str);
+
+ if (!isset($meta[$type])) {
+ return '';
+ }
+
+ return implode(', ', $meta[$type]);
+ }
+
+ /**
+ Converts serialized metadata (for instance in dc_post post_meta)
+ into a "fetchable" metadata record.
+
+ @param str string the serialized metadata.
+ @param type string meta type to retrieve metaIDs from.
+
+ @return record the meta recordset
+ */
+ public function getMetaRecordset($str, $type)
+ {
+ $meta = $this->getMetaArray($str);
+ $data = [];
+
+ if (isset($meta[$type])) {
+ foreach ($meta[$type] as $v) {
+ $data[] = [
+ 'meta_id' => $v,
+ 'meta_type' => $type,
+ 'meta_id_lower' => mb_strtolower($v),
+ 'count' => 0,
+ 'percent' => 0,
+ 'roundpercent' => 0
+ ];
+ }
+ }
+
+ return staticRecord::newFromArray($data);
+ }
+
+ /**
+ @deprecated since version 2.2 : $core->meta is always defined
+ @see getMetaRecordset
+ static version of getMetaRecordset
+ */
+ public static function getMetaRecord($core, $str, $type)
+ {
+ $meta = new self($core);
+ return $meta->getMetaRecordset($str, $type);
+ }
+
+ /**
+ Checks whether the current user is allowed to change post meta
+ An exception is thrown if user is not allowed.
+
+ @param post_id string the post_id to check.
+ */
+ private function checkPermissionsOnPost($post_id)
+ {
+ $post_id = (integer) $post_id;
+
+ if (!$this->core->auth->check('usage,contentadmin', $this->core->blog->id)) {
+ throw new Exception(__('You are not allowed to change this entry status'));
+ }
+
+ #�If user can only publish, we need to check the post's owner
+ if (!$this->core->auth->check('contentadmin', $this->core->blog->id)) {
+ $strReq = 'SELECT post_id ' .
+ 'FROM ' . $this->core->prefix . 'post ' .
+ 'WHERE post_id = ' . $post_id . ' ' .
+ "AND user_id = '" . $this->con->escape($this->core->auth->userID()) . "' ";
+
+ $rs = $this->con->select($strReq);
+
+ if ($rs->isEmpty()) {
+ throw new Exception(__('You are not allowed to change this entry status'));
+ }
+ }
+ }
+
+ /**
+ Updates serialized post_meta information with dc_meta table information.
+
+ @param post_id string the post_id to update.
+ */
+ private function updatePostMeta($post_id)
+ {
+ $post_id = (integer) $post_id;
+
+ $strReq = 'SELECT meta_id, meta_type ' .
+ 'FROM ' . $this->table . ' ' .
+ 'WHERE post_id = ' . $post_id . ' ';
+
+ $rs = $this->con->select($strReq);
+
+ $meta = [];
+ while ($rs->fetch()) {
+ $meta[$rs->meta_type][] = $rs->meta_id;
+ }
+
+ $post_meta = serialize($meta);
+
+ $cur = $this->con->openCursor($this->core->prefix . 'post');
+ $cur->post_meta = $post_meta;
+
+ $cur->update('WHERE post_id = ' . $post_id);
+ $this->core->blog->triggerBlog();
+ }
+
+ /**
+ Retrieves posts corresponding to given meta criteria.
+ $params is an array taking the following optional parameters:
+ - meta_id : get posts having meta id
+ - meta_type : get posts having meta type
+
+ @param params array Parameters
+ @param count_only boolean Only counts results
+
+ @return record the resulting posts record
+ */
+ public function getPostsByMeta($params = [], $count_only = false)
+ {
+ if (!isset($params['meta_id'])) {
+ return;
+ }
+
+ $params['from'] = ', ' . $this->table . ' META ';
+ $params['sql'] = 'AND META.post_id = P.post_id ';
+
+ $params['sql'] .= "AND META.meta_id = '" . $this->con->escape($params['meta_id']) . "' ";
+
+ if (!empty($params['meta_type'])) {
+ $params['sql'] .= "AND META.meta_type = '" . $this->con->escape($params['meta_type']) . "' ";
+ unset($params['meta_type']);
+ }
+
+ unset($params['meta_id']);
+
+ return $this->core->blog->getPosts($params, $count_only);
+ }
+
+ /**
+ Retrieves comments to posts corresponding to given meta criteria.
+ $params is an array taking the following optional parameters:
+ - meta_id : get comments to posts having meta id
+ - meta_type : get comments to posts having meta type
+
+ @param params array Parameters
+ @param count_only boolean Only counts results
+
+ @return record the resulting comments record
+ */
+ public function getCommentsByMeta($params = [], $count_only = false)
+ {
+ if (!isset($params['meta_id'])) {
+ return;
+ }
+
+ $params['from'] = ', ' . $this->table . ' META ';
+ $params['sql'] = 'AND META.post_id = P.post_id ';
+ $params['sql'] .= "AND META.meta_id = '" . $this->con->escape($params['meta_id']) . "' ";
+
+ if (!empty($params['meta_type'])) {
+ $params['sql'] .= "AND META.meta_type = '" . $this->con->escape($params['meta_type']) . "' ";
+ unset($params['meta_type']);
+ }
+
+ return $this->core->blog->getComments($params, $count_only);
+ }
+
+ /**
+ @deprecated since 2.2. Use getMetadata and computeMetaStats instead.
+ Generic-purpose metadata retrieval : gets metadatas according to given
+ criteria. Metadata get enriched with stastistics columns (only relevant
+ if limit parameter is not set). Metadata are sorted by post count
+ descending
+
+ @param type string if not null, get metas having the given type
+ @param limit string if not null, number of max fetched metas
+ @param meta_id string if not null, get metas having the given id
+ @param post_id string if not null, get metas for the given post id
+
+ @return record the meta recordset
+ */
+ public function getMeta($type = null, $limit = null, $meta_id = null, $post_id = null)
+ {
+ $params = [];
+
+ if ($type != null) {
+ $params['meta_type'] = $type;
+ }
+
+ if ($limit != null) {
+ $params['limit'] = $limit;
+ }
+
+ if ($meta_id != null) {
+ $params['meta_id'] = $meta_id;
+ }
+
+ if ($meta_id != null) {
+ $params['post_id'] = $post_id;
+ }
+
+ $rs = $this->getMetadata($params, false);
+ return $this->computeMetaStats($rs);
+ }
+
+ /**
+ Generic-purpose metadata retrieval : gets metadatas according to given
+ criteria. $params is an array taking the following
+ optionnal parameters:
+
+ - type: get metas having the given type
+ - meta_id: if not null, get metas having the given id
+ - post_id: get metas for the given post id
+ - limit: number of max fetched metas
+ - order: results order (default : posts count DESC)
+
+ @param params array Parameters
+ @param count_only boolean Only counts results
+
+ @return record the resulting comments record
+ */
+ public function getMetadata($params = [], $count_only = false)
+ {
+ if ($count_only) {
+ $strReq = 'SELECT count(distinct M.meta_id) ';
+ } else {
+ $strReq = 'SELECT M.meta_id, M.meta_type, COUNT(M.post_id) as count, MAX(P.post_dt) as latest, MIN(P.post_dt) as oldest ';
+ }
+
+ $strReq .=
+ 'FROM ' . $this->table . ' M LEFT JOIN ' . $this->core->prefix . 'post P ' .
+ 'ON M.post_id = P.post_id ' .
+ "WHERE P.blog_id = '" . $this->con->escape($this->core->blog->id) . "' ";
+
+ if (isset($params['meta_type'])) {
+ $strReq .= " AND meta_type = '" . $this->con->escape($params['meta_type']) . "' ";
+ }
+
+ if (isset($params['meta_id'])) {
+ $strReq .= " AND meta_id = '" . $this->con->escape($params['meta_id']) . "' ";
+ }
+
+ if (isset($params['post_id'])) {
+ $strReq .= ' AND P.post_id ' . $this->con->in($params['post_id']) . ' ';
+ }
+
+ if (!$this->core->auth->check('contentadmin', $this->core->blog->id)) {
+ $strReq .= 'AND ((post_status = 1 ';
+
+ if ($this->core->blog->without_password) {
+ $strReq .= 'AND post_password IS NULL ';
+ }
+ $strReq .= ') ';
+
+ if ($this->core->auth->userID()) {
+ $strReq .= "OR P.user_id = '" . $this->con->escape($this->core->auth->userID()) . "')";
+ } else {
+ $strReq .= ') ';
+ }
+ }
+
+ if (!$count_only) {
+ if (!isset($params['order'])) {
+ $params['order'] = 'count DESC';
+ }
+
+ $strReq .=
+ 'GROUP BY meta_id,meta_type,P.blog_id ' .
+ 'ORDER BY ' . $params['order'];
+
+ if (isset($params['limit'])) {
+ $strReq .= $this->con->limit($params['limit']);
+ }
+ }
+
+ $rs = $this->con->select($strReq);
+ return $rs;
+ }
+
+ /**
+ Computes statistics from a metadata recordset.
+ Each record gets enriched with lowercase name, percent and roundpercent columns
+
+ @param rs record recordset to enrich
+
+ @return record the enriched recordset
+ */
+ public function computeMetaStats($rs)
+ {
+ $rs_static = $rs->toStatic();
+
+ $max = [];
+ while ($rs_static->fetch()) {
+ $type = $rs_static->meta_type;
+ if (!isset($max[$type])) {
+ $max[$type] = $rs_static->count;
+ } else {
+ if ($rs_static->count > $max[$type]) {
+ $max[$type] = $rs_static->count;
+ }
+ }
+ }
+
+ while ($rs_static->fetch()) {
+ $rs_static->set('meta_id_lower', dcUtils::removeDiacritics(mb_strtolower($rs_static->meta_id)));
+
+ $count = $rs_static->count;
+ $percent = ((integer) $rs_static->count) * 100 / $max[$rs_static->meta_type];
+
+ $rs_static->set('percent', (integer) round($percent));
+ $rs_static->set('roundpercent', round($percent / 10) * 10);
+ }
+
+ return $rs_static;
+ }
+
+ /**
+ Adds a metadata to a post.
+
+ @param post_id integer the post id
+ @param type string meta type
+ @param value integer meta value
+ */
+ public function setPostMeta($post_id, $type, $value)
+ {
+ $this->checkPermissionsOnPost($post_id);
+
+ $value = trim($value);
+ if ($value === false) {return;}
+
+ $cur = $this->con->openCursor($this->table);
+
+ $cur->post_id = (integer) $post_id;
+ $cur->meta_id = (string) $value;
+ $cur->meta_type = (string) $type;
+
+ $cur->insert();
+ $this->updatePostMeta((integer) $post_id);
+ }
+
+ /**
+ Removes metadata from a post.
+
+ @param post_id integer the post id
+ @param type string meta type (if null, delete all types)
+ @param value integer meta value (if null, delete all values)
+ */
+ public function delPostMeta($post_id, $type = null, $meta_id = null)
+ {
+ $post_id = (integer) $post_id;
+
+ $this->checkPermissionsOnPost($post_id);
+
+ $strReq = 'DELETE FROM ' . $this->table . ' ' .
+ 'WHERE post_id = ' . $post_id;
+
+ if ($type !== null) {
+ $strReq .= " AND meta_type = '" . $this->con->escape($type) . "' ";
+ }
+
+ if ($meta_id !== null) {
+ $strReq .= " AND meta_id = '" . $this->con->escape($meta_id) . "' ";
+ }
+
+ $this->con->execute($strReq);
+ $this->updatePostMeta((integer) $post_id);
+ }
+
+ /**
+ Mass updates metadata for a given post_type.
+
+ @param meta_id integer old value
+ @param new_meta integer new value
+ @param type string meta type (if null, select all types)
+ @param post_type integer impacted post_type (if null, select all types)
+ @return boolean true if at least 1 post has been impacted
+ */
+ public function updateMeta($meta_id, $new_meta_id, $type = null, $post_type = null)
+ {
+ $new_meta_id = self::sanitizeMetaID($new_meta_id);
+
+ if ($new_meta_id == $meta_id) {
+ return true;
+ }
+
+ $getReq = 'SELECT M.post_id ' .
+ 'FROM ' . $this->table . ' M, ' . $this->core->prefix . 'post P ' .
+ 'WHERE P.post_id = M.post_id ' .
+ "AND P.blog_id = '" . $this->con->escape($this->core->blog->id) . "' " .
+ "AND meta_id = '%s' ";
+
+ if (!$this->core->auth->check('contentadmin', $this->core->blog->id)) {
+ $getReq .= "AND P.user_id = '" . $this->con->escape($this->core->auth->userID()) . "' ";
+ }
+ if ($post_type !== null) {
+ $getReq .= "AND P.post_type = '" . $this->con->escape($post_type) . "' ";
+ }
+
+ $delReq = 'DELETE FROM ' . $this->table . ' ' .
+ 'WHERE post_id IN (%s) ' .
+ "AND meta_id = '%s' ";
+
+ $updReq = 'UPDATE ' . $this->table . ' ' .
+ "SET meta_id = '%s' " .
+ 'WHERE post_id IN (%s) ' .
+ "AND meta_id = '%s' ";
+
+ if ($type !== null) {
+ $plus = " AND meta_type = '%s' ";
+ $getReq .= $plus;
+ $delReq .= $plus;
+ $updReq .= $plus;
+ }
+
+ $to_update = $to_remove = [];
+
+ $rs = $this->con->select(sprintf($getReq, $this->con->escape($meta_id),
+ $this->con->escape($type)));
+
+ while ($rs->fetch()) {
+ $to_update[] = $rs->post_id;
+ }
+
+ if (empty($to_update)) {
+ return false;
+ }
+
+ $rs = $this->con->select(sprintf($getReq, $new_meta_id, $type));
+ while ($rs->fetch()) {
+ if (in_array($rs->post_id, $to_update)) {
+ $to_remove[] = $rs->post_id;
+ unset($to_update[array_search($rs->post_id, $to_update)]);
+ }
+ }
+
+ # Delete duplicate meta
+ if (!empty($to_remove)) {
+ $this->con->execute(sprintf($delReq, implode(',', $to_remove),
+ $this->con->escape($meta_id),
+ $this->con->escape($type)));
+
+ foreach ($to_remove as $post_id) {
+ $this->updatePostMeta($post_id);
+ }
+ }
+
+ # Update meta
+ if (!empty($to_update)) {
+ $this->con->execute(sprintf($updReq, $this->con->escape($new_meta_id),
+ implode(',', $to_update),
+ $this->con->escape($meta_id),
+ $this->con->escape($type)));
+
+ foreach ($to_update as $post_id) {
+ $this->updatePostMeta($post_id);
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ Mass delete metadata for a given post_type.
+
+ @param meta_id integer meta value
+ @param type string meta type (if null, select all types)
+ @param post_type integer impacted post_type (if null, select all types)
+ @return Array the list of impacted post_ids
+ */
+ public function delMeta($meta_id, $type = null, $post_type = null)
+ {
+ $strReq = 'SELECT M.post_id ' .
+ 'FROM ' . $this->table . ' M, ' . $this->core->prefix . 'post P ' .
+ 'WHERE P.post_id = M.post_id ' .
+ "AND P.blog_id = '" . $this->con->escape($this->core->blog->id) . "' " .
+ "AND meta_id = '" . $this->con->escape($meta_id) . "' ";
+
+ if ($type !== null) {
+ $strReq .= " AND meta_type = '" . $this->con->escape($type) . "' ";
+ }
+
+ if ($post_type !== null) {
+ $strReq .= " AND P.post_type = '" . $this->con->escape($post_type) . "' ";
+ }
+
+ $rs = $this->con->select($strReq);
+
+ if ($rs->isEmpty()) {
+ return [];
+ }
+
+ $ids = [];
+ while ($rs->fetch()) {
+ $ids[] = $rs->post_id;
+ }
+
+ $strReq = 'DELETE FROM ' . $this->table . ' ' .
+ 'WHERE post_id IN (' . implode(',', $ids) . ') ' .
+ "AND meta_id = '" . $this->con->escape($meta_id) . "' ";
+
+ if ($type !== null) {
+ $strReq .= " AND meta_type = '" . $this->con->escape($type) . "' ";
+ }
+
+ $rs = $this->con->execute($strReq);
+
+ foreach ($ids as $post_id) {
+ $this->updatePostMeta($post_id);
+ }
+
+ return $ids;
+ }
+}
diff --git a/dotclear._no/inc/core/class.dc.modules.php b/dotclear._no/inc/core/class.dc.modules.php
new file mode 100644
index 0000000..6d64d92
--- /dev/null
+++ b/dotclear._no/inc/core/class.dc.modules.php
@@ -0,0 +1,745 @@
+dcCore dcCore instance
+
+ /**
+ Object constructor.
+
+ @param core dcCore dcCore instance
+ */
+ public function __construct($core)
+ {
+ $this->core = &$core;
+ }
+
+ /**
+ * Checks all modules dependencies
+ * Fills in the following information in module :
+ * * cannot_enable : list reasons why module cannot be enabled. Not set if module can be enabled
+ * * cannot_disable : list reasons why module cannot be disabled. Not set if module can be disabled
+ * * implies : reverse dependencies
+ * @return array list of enabled modules with unmet dependencies, and that must be disabled.
+ */
+ public function checkDependencies()
+ {
+ $dc_version = preg_replace('/\-dev.*$/', '', DC_VERSION);
+ $this->to_disable = [];
+ foreach ($this->all_modules as $k => &$m) {
+ if (isset($m['requires'])) {
+ $missing = [];
+ foreach ($m['requires'] as &$dep) {
+ if (!is_array($dep)) {
+ $dep = [$dep];
+ }
+ // grab missing dependencies
+ if (!isset($this->all_modules[$dep[0]]) && ($dep[0] != 'core')) {
+ // module not present
+ $missing[$dep[0]] = sprintf(__("Requires %s module which is not installed"), $dep[0]);
+ } elseif ((count($dep) > 1) &&
+ version_compare(($dep[0] == 'core' ? $dc_version : $this->all_modules[$dep[0]]['version']), $dep[1]) == -1) {
+ // module present, but version missing
+ if ($dep[0] == 'core') {
+ $missing[$dep[0]] = sprintf(__("Requires Dotclear version %s, but version %s is installed"),
+ $dep[1], $dc_version);
+ } else {
+ $missing[$dep[0]] = sprintf(__("Requires %s module version %s, but version %s is installed"),
+ $dep[0], $dep[1], $this->all_modules[$dep[0]]['version']);
+ }
+ } elseif (($dep[0] != 'core') && !$this->all_modules[$dep[0]]['enabled']) {
+ // module disabled
+ $missing[$dep[0]] = sprintf(__("Requires %s module which is disabled"), $dep[0]);
+ }
+ $this->all_modules[$dep[0]]['implies'][] = $k;
+ }
+ if (count($missing)) {
+ $m['cannot_enable'] = $missing;
+ if ($m['enabled']) {
+ $this->to_disable[] = ['name' => $k, 'reason' => $missing];
+ }
+ }
+ }
+ }
+ // Check modules that cannot be disabled
+ foreach ($this->modules as $k => &$m) {
+ if (isset($m['implies']) && $m['enabled']) {
+ foreach ($m['implies'] as $im) {
+ if (isset($this->all_modules[$im]) && $this->all_modules[$im]['enabled']) {
+ $m['cannot_disable'][] = $im;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks all modules dependencies, and disable unmet dependencies
+ * @param string $redir_url URL to redirect if modules are to disable
+ * @return boolean, true if a redirection has been performed
+ */
+ public function disableDepModules($redir_url)
+ {
+ if (isset($_GET['dep'])) {
+ // Avoid infinite redirects
+ return false;
+ }
+ $reason = [];
+ foreach ($this->to_disable as $module) {
+ try {
+ $this->deactivateModule($module['name']);
+ $reason[] = sprintf("%s : %s ", $module['name'], join(',', $module['reason']));
+ } catch (Exception $e) {
+ }
+ }
+ if (count($reason)) {
+ $message = sprintf("%s
",
+ __('The following extensions have been disabled :'),
+ join('', $reason)
+ );
+ dcPage::addWarningNotice($message, ['divtag' => true, 'with_ts' => false]);
+ $url = $redir_url . (strpos($redir_url, "?") ? '&' : '?') . 'dep=1';
+ http::redirect($url);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ Loads modules. $path could be a separated list of paths
+ (path separator depends on your OS).
+
+ $ns indicates if an additionnal file needs to be loaded on plugin
+ load, value could be:
+ - admin (loads module's _admin.php)
+ - public (loads module's _public.php)
+ - xmlrpc (loads module's _xmlrpc.php)
+
+ $lang indicates if we need to load a lang file on plugin
+ loading.
+ */
+ public function loadModules($path, $ns = null, $lang = null)
+ {
+ $this->path = explode(PATH_SEPARATOR, $path);
+ $this->ns = $ns;
+
+ $disabled = isset($_SESSION['sess_safe_mode']) && $_SESSION['sess_safe_mode'];
+ $disabled = $disabled && !get_parent_class($this) ? true : false;
+
+ $ignored = [];
+
+ foreach ($this->path as $root) {
+ if (!is_dir($root) || !is_readable($root)) {
+ continue;
+ }
+
+ if (substr($root, -1) != '/') {
+ $root .= '/';
+ }
+
+ if (($d = @dir($root)) === false) {
+ continue;
+ }
+
+ while (($entry = $d->read()) !== false) {
+ $full_entry = $root . $entry;
+
+ if ($entry != '.' && $entry != '..' && is_dir($full_entry)
+ && file_exists($full_entry . '/_define.php')) {
+ if (!file_exists($full_entry . '/_disabled') && !$disabled) {
+ $this->id = $entry;
+ $this->mroot = $full_entry;
+ ob_start();
+ require $full_entry . '/_define.php';
+ ob_end_clean();
+ $this->all_modules[$entry] = &$this->modules[$entry];
+ $this->id = null;
+ $this->mroot = null;
+ } else {
+ if (file_exists($full_entry . '/_define.php')) {
+ $this->id = $entry;
+ $this->mroot = $full_entry;
+ $this->disabled_mode = true;
+ ob_start();
+ require $full_entry . '/_define.php';
+ ob_end_clean();
+ $this->disabled_mode = false;
+ $this->disabled[$entry] = $this->disabled_meta;
+ $this->all_modules[$entry] = &$this->disabled[$entry];
+ $this->id = null;
+ $this->mroot = null;
+ }
+ }
+ }
+ }
+ $d->close();
+ }
+ $this->checkDependencies();
+ # Sort plugins
+ uasort($this->modules, [$this, 'sortModules']);
+
+ foreach ($this->modules as $id => $m) {
+ # Load translation and _prepend
+ if (isset($m['root']) && file_exists($m['root'] . '/_prepend.php')) {
+ $r = $this->loadModuleFile($m['root'] . '/_prepend.php');
+
+ # If _prepend.php file returns null (ie. it has a void return statement)
+ if (is_null($r)) {
+ $ignored[] = $id;
+ continue;
+ }
+ unset($r);
+ }
+
+ $this->loadModuleL10N($id, $lang, 'main');
+ if ($ns == 'admin') {
+ $this->loadModuleL10Nresources($id, $lang);
+ $this->core->adminurl->register('admin.plugin.' . $id, 'plugin.php', ['p' => $id]);
+ }
+ }
+
+ // Give opportunity to do something before loading context (admin,public,xmlrpc) files
+ $this->core->callBehavior('coreBeforeLoadingNsFiles', $this->core, $this, $lang);
+
+ foreach ($this->modules as $id => $m) {
+ # If _prepend.php file returns null (ie. it has a void return statement)
+ if (in_array($id, $ignored)) {
+ continue;
+ }
+ # Load ns_file
+ $this->loadNsFile($id, $ns);
+ }
+ }
+
+ public function requireDefine($dir, $id)
+ {
+ if (file_exists($dir . '/_define.php')) {
+ $this->id = $id;
+ ob_start();
+ require $dir . '/_define.php';
+ ob_end_clean();
+ $this->id = null;
+ }
+ }
+
+ /**
+ This method registers a module in modules list. You should use this to
+ register a new module.
+
+ $permissions is a comma separated list of permissions for your
+ module. If $permissions is null, only super admin has access to
+ this module.
+
+ $priority is an integer. Modules are sorted by priority and name.
+ Lowest priority comes first.
+
+ @param name string Module name
+ @param desc string Module description
+ @param author string Module author name
+ @param version string Module version
+ @param properties array extra properties
+ (currently available keys : permissions, priority, type)
+ */
+ public function registerModule($name, $desc, $author, $version, $properties = [])
+ {
+ if ($this->disabled_mode) {
+ $this->disabled_meta = array_merge(
+ $properties,
+ [
+ 'root' => $this->mroot,
+ 'name' => $name,
+ 'desc' => $desc,
+ 'author' => $author,
+ 'version' => $version,
+ 'enabled' => false,
+ 'root_writable' => is_writable($this->mroot)
+ ]
+ );
+ return;
+ }
+ # Fallback to legacy registerModule parameters
+ if (!is_array($properties)) {
+ $args = func_get_args();
+ $properties = [];
+ if (isset($args[4])) {
+ $properties['permissions'] = $args[4];
+ }
+ if (isset($args[5])) {
+ $properties['priority'] = (integer) $args[5];
+ }
+ }
+
+ # Default module properties
+ $properties = array_merge(
+ [
+ 'permissions' => null,
+ 'priority' => 1000,
+ 'standalone_config' => false,
+ 'type' => null,
+ 'enabled' => true,
+ 'requires' => [],
+ 'settings' => []
+ ], $properties
+ );
+
+ # Check module type
+ if (self::$type !== null && $properties['type'] !== null && $properties['type'] != self::$type) {
+ $this->errors[] = sprintf(
+ __('Module "%s" has type "%s" that mismatch required module type "%s".'),
+ '' . html::escapeHTML($name) . ' ',
+ '' . html::escapeHTML($properties['type']) . ' ',
+ '' . html::escapeHTML(self::$type) . ' '
+ );
+ return;
+ }
+
+ # Check module perms on admin side
+ $permissions = $properties['permissions'];
+ if ($this->ns == 'admin') {
+ if ($permissions == '' && !$this->core->auth->isSuperAdmin()) {
+ return;
+ } elseif (!$this->core->auth->check($permissions, $this->core->blog->id)) {
+ return;
+ }
+ }
+
+ # Check module install on multiple path
+ if ($this->id) {
+ $module_exists = array_key_exists($name, $this->modules_names);
+ $module_overwrite = $module_exists ? version_compare($this->modules_names[$name], $version, '<') : false;
+ if (!$module_exists || ($module_exists && $module_overwrite)) {
+ $this->modules_names[$name] = $version;
+ $this->modules[$this->id] = array_merge(
+ $properties,
+ [
+ 'root' => $this->mroot,
+ 'name' => $name,
+ 'desc' => $desc,
+ 'author' => $author,
+ 'version' => $version,
+ 'root_writable' => is_writable($this->mroot)
+ ]
+ );
+ } else {
+ $path1 = path::real($this->moduleInfo($name, 'root'));
+ $path2 = path::real($this->mroot);
+ $this->errors[] = sprintf(
+ __('Module "%s" is installed twice in "%s" and "%s".'),
+ '' . $name . ' ',
+ '' . $path1 . ' ',
+ '' . $path2 . ' '
+ );
+ }
+ }
+ }
+
+ public function resetModulesList()
+ {
+ $this->modules = [];
+ $this->modules_names = [];
+ $this->errors = [];
+ }
+
+ public static function installPackage($zip_file, dcModules &$modules)
+ {
+ $zip = new fileUnzip($zip_file);
+ $zip->getList(false, '#(^|/)(__MACOSX|\.svn|\.hg.*|\.git.*|\.DS_Store|\.directory|Thumbs\.db)(/|$)#');
+
+ $zip_root_dir = $zip->getRootDir();
+ $define = '';
+ if ($zip_root_dir != false) {
+ $target = dirname($zip_file);
+ $destination = $target . '/' . $zip_root_dir;
+ $define = $zip_root_dir . '/_define.php';
+ $has_define = $zip->hasFile($define);
+ } else {
+ $target = dirname($zip_file) . '/' . preg_replace('/\.([^.]+)$/', '', basename($zip_file));
+ $destination = $target;
+ $define = '_define.php';
+ $has_define = $zip->hasFile($define);
+ }
+
+ if ($zip->isEmpty()) {
+ $zip->close();
+ unlink($zip_file);
+ throw new Exception(__('Empty module zip file.'));
+ }
+
+ if (!$has_define) {
+ $zip->close();
+ unlink($zip_file);
+ throw new Exception(__('The zip file does not appear to be a valid Dotclear module.'));
+ }
+
+ $ret_code = 1;
+
+ if (!is_dir($destination)) {
+ try {
+ files::makeDir($destination, true);
+
+ $sandbox = clone $modules;
+ $zip->unzip($define, $target . '/_define.php');
+
+ $sandbox->resetModulesList();
+ $sandbox->requireDefine($target, basename($destination));
+ unlink($target . '/_define.php');
+
+ $new_errors = $sandbox->getErrors();
+ if (!empty($new_errors)) {
+ $new_errors = is_array($new_errors) ? implode(" \n", $new_errors) : $new_errors;
+ throw new Exception($new_errors);
+ }
+
+ files::deltree($destination);
+ } catch (Exception $e) {
+ $zip->close();
+ unlink($zip_file);
+ files::deltree($destination);
+ throw new Exception($e->getMessage());
+ }
+ } else {
+ # test for update
+ $sandbox = clone $modules;
+ $zip->unzip($define, $target . '/_define.php');
+
+ $sandbox->resetModulesList();
+ $sandbox->requireDefine($target, basename($destination));
+ unlink($target . '/_define.php');
+ $new_modules = $sandbox->getModules();
+
+ if (!empty($new_modules)) {
+ $tmp = array_keys($new_modules);
+ $id = $tmp[0];
+ $cur_module = $modules->getModules($id);
+ if (!empty($cur_module) && (defined('DC_DEV') && DC_DEV === true || dcUtils::versionsCompare($new_modules[$id]['version'], $cur_module['version'], '>', true))) {
+ # delete old module
+ if (!files::deltree($destination)) {
+ throw new Exception(__('An error occurred during module deletion.'));
+ }
+ $ret_code = 2;
+ } else {
+ $zip->close();
+ unlink($zip_file);
+ throw new Exception(sprintf(__('Unable to upgrade "%s". (older or same version)'), basename($destination)));
+ }
+ } else {
+ $zip->close();
+ unlink($zip_file);
+ throw new Exception(sprintf(__('Unable to read new _define.php file')));
+ }
+ }
+ $zip->unzipAll($target);
+ $zip->close();
+ unlink($zip_file);
+ return $ret_code;
+ }
+
+ /**
+ This method installs all modules having a _install file.
+
+ @see dcModules::installModule
+ */
+ public function installModules()
+ {
+ $res = ['success' => [], 'failure' => []];
+ foreach ($this->modules as $id => &$m) {
+ $i = $this->installModule($id, $msg);
+ if ($i === true) {
+ $res['success'][$id] = true;
+ } elseif ($i === false) {
+ $res['failure'][$id] = $msg;
+ }
+ }
+
+ return $res;
+ }
+
+ /**
+ This method installs module with ID $id and having a _install
+ file. This file should throw exception on failure or true if it installs
+ successfully.
+
+ $msg is an out parameter that handle installer message.
+
+ @param id string Module ID
+ @param msg string Module installer message
+ @return boolean
+ */
+ public function installModule($id, &$msg)
+ {
+ if (!isset($this->modules[$id])) {
+ return;
+ }
+ try {
+ $i = $this->loadModuleFile($this->modules[$id]['root'] . '/_install.php');
+ if ($i === true) {
+ return true;
+ }
+ } catch (Exception $e) {
+ $msg = $e->getMessage();
+ return false;
+ }
+
+ return;
+ }
+
+ public function deleteModule($id, $disabled = false)
+ {
+ if ($disabled) {
+ $p = &$this->disabled;
+ } else {
+ $p = &$this->modules;
+ }
+
+ if (!isset($p[$id])) {
+ throw new Exception(__('No such module.'));
+ }
+
+ if (!files::deltree($p[$id]['root'])) {
+ throw new Exception(__('Cannot remove module files'));
+ }
+ }
+
+ public function deactivateModule($id)
+ {
+ if (!isset($this->modules[$id])) {
+ throw new Exception(__('No such module.'));
+ }
+
+ if (!$this->modules[$id]['root_writable']) {
+ throw new Exception(__('Cannot deactivate plugin.'));
+ }
+
+ if (@file_put_contents($this->modules[$id]['root'] . '/_disabled', '')) {
+ throw new Exception(__('Cannot deactivate plugin.'));
+ }
+ }
+
+ public function activateModule($id)
+ {
+ if (!isset($this->disabled[$id])) {
+ throw new Exception(__('No such module.'));
+ }
+
+ if (!$this->disabled[$id]['root_writable']) {
+ throw new Exception(__('Cannot activate plugin.'));
+ }
+
+ if (@unlink($this->disabled[$id]['root'] . '/_disabled') === false) {
+ throw new Exception(__('Cannot activate plugin.'));
+ }
+ }
+
+ /**
+ This method will search for file $file in language
+ $lang for module $id .
+
+ $file should not have any extension.
+
+ @param id string Module ID
+ @param lang string Language code
+ @param file string File name (without extension)
+ */
+ public function loadModuleL10N($id, $lang, $file)
+ {
+ if (!$lang || !isset($this->modules[$id])) {
+ return;
+ }
+
+ $lfile = $this->modules[$id]['root'] . '/locales/%s/%s';
+ if (l10n::set(sprintf($lfile, $lang, $file)) === false && $lang != 'en') {
+ l10n::set(sprintf($lfile, 'en', $file));
+ }
+ }
+
+ public function loadModuleL10Nresources($id, $lang)
+ {
+ if (!$lang || !isset($this->modules[$id])) {
+ return;
+ }
+
+ $f = l10n::getFilePath($this->modules[$id]['root'] . '/locales', 'resources.php', $lang);
+ if ($f) {
+ $this->loadModuleFile($f);
+ }
+ }
+
+ /**
+ Returns all modules associative array or only one module if $id
+ is present.
+
+ @param id string Optionnal module ID
+ @return array
+ */
+ public function getModules($id = null)
+ {
+ if ($id && isset($this->modules[$id])) {
+ return $this->modules[$id];
+ }
+ return $this->modules;
+ }
+
+ /**
+ Returns true if the module with ID $id exists.
+
+ @param id string Module ID
+ @return boolean
+ */
+ public function moduleExists($id)
+ {
+ return isset($this->modules[$id]);
+ }
+
+ /**
+ Returns all disabled modules in an array
+
+ @return array
+ */
+ public function getDisabledModules()
+ {
+ return $this->disabled;
+ }
+
+ /**
+ Returns root path for module with ID $id .
+
+ @param id string Module ID
+ @return string
+ */
+ public function moduleRoot($id)
+ {
+ return $this->moduleInfo($id, 'root');
+ }
+
+ /**
+ Returns a module information that could be:
+ - root
+ - name
+ - desc
+ - author
+ - version
+ - permissions
+ - priority
+
+ @param id string Module ID
+ @param info string Information to retrieve
+ @return string
+ */
+ public function moduleInfo($id, $info)
+ {
+ return isset($this->modules[$id][$info]) ? $this->modules[$id][$info] : null;
+ }
+
+ /**
+ Loads namespace $ns specific files for all modules.
+
+ @param ns string Namespace name
+ */
+ public function loadNsFiles($ns = null)
+ {
+ foreach ($this->modules as $k => $v) {
+ $this->loadNsFile($k, $ns);
+ }
+ }
+
+ /**
+ Loads namespace $ns specific file for module with ID
+ $id
+
+ @param id string Module ID
+ @param ns string Namespace name
+ */
+ public function loadNsFile($id, $ns = null)
+ {
+ if (!isset($this->modules[$id])) {
+ return;
+ }
+ switch ($ns) {
+ case 'admin':
+ $this->loadModuleFile($this->modules[$id]['root'] . '/_admin.php');
+ break;
+ case 'public':
+ $this->loadModuleFile($this->modules[$id]['root'] . '/_public.php');
+ break;
+ case 'xmlrpc':
+ $this->loadModuleFile($this->modules[$id]['root'] . '/_xmlrpc.php');
+ break;
+ }
+ }
+
+ public function getErrors()
+ {
+ return $this->errors;
+ }
+
+ protected function loadModuleFile($________, $catch = true)
+ {
+ if (!file_exists($________)) {
+ return;
+ }
+
+ self::$_k = array_keys($GLOBALS);
+
+ foreach (self::$_k as self::$_n) {
+ if (!in_array(self::$_n, self::$superglobals)) {
+ global ${self::$_n};
+ }
+ }
+
+ if ($catch) {
+ // Catch ouput to prevents hacked or corrupted modules
+ ob_start();
+ $ret = require $________;
+ ob_end_clean();
+ return $ret;
+ }
+
+ return require $________;
+ }
+
+ private function sortModules($a, $b)
+ {
+ if (!isset($a['priority']) || !isset($b['priority'])) {
+ return 1;
+ }
+ if ($a['priority'] == $b['priority']) {
+ return strcasecmp($a['name'], $b['name']);
+ }
+
+ return ($a['priority'] < $b['priority']) ? -1 : 1;
+ }
+}
diff --git a/dotclear._no/inc/core/class.dc.namespace.php b/dotclear._no/inc/core/class.dc.namespace.php
new file mode 100644
index 0000000..c3ca882
--- /dev/null
+++ b/dotclear._no/inc/core/class.dc.namespace.php
@@ -0,0 +1,445 @@
+connection Database connection object
+ protected $table; ///< string Settings table name
+ protected $blog_id; ///< string Blog ID
+
+ protected $global_settings = []; ///< array Global settings array
+ protected $local_settings = []; ///< array Local settings array
+ protected $settings = []; ///< array Associative settings array
+ protected $ns; ///< string Current namespace
+
+ /**
+ Object constructor. Retrieves blog settings and puts them in $settings
+ array. Local (blog) settings have a highest priority than global settings.
+
+ @param name string ID for this namespace
+ */
+ public function __construct(&$core, $blog_id, $name, $rs = null)
+ {
+ if (preg_match('/^[a-zA-Z][a-zA-Z0-9]+$/', $name)) {
+ $this->ns = $name;
+ } else {
+ throw new Exception(sprintf(__('Invalid setting dcNamespace: %s'), $name));
+ }
+
+ $this->con = &$core->con;
+ $this->table = $core->prefix . 'setting';
+ $this->blog_id = &$blog_id;
+
+ $this->getSettings($rs);
+ }
+
+ private function getSettings($rs = null)
+ {
+ if ($rs == null) {
+ $strReq = 'SELECT blog_id, setting_id, setting_value, ' .
+ 'setting_type, setting_label, setting_ns ' .
+ 'FROM ' . $this->table . ' ' .
+ "WHERE (blog_id = '" . $this->con->escape($this->blog_id) . "' " .
+ 'OR blog_id IS NULL) ' .
+ "AND setting_ns = '" . $this->con->escape($this->ns) . "' " .
+ 'ORDER BY setting_id DESC ';
+
+ try {
+ $rs = $this->con->select($strReq);
+ } catch (Exception $e) {
+ trigger_error(__('Unable to retrieve settings:') . ' ' . $this->con->error(), E_USER_ERROR);
+ }
+ }
+ while ($rs->fetch()) {
+ if ($rs->f('setting_ns') != $this->ns) {
+ break;
+ }
+ $id = trim($rs->f('setting_id'));
+ $value = $rs->f('setting_value');
+ $type = $rs->f('setting_type');
+
+ if ($type == 'array') {
+ $value = @json_decode($value, true);
+ } else {
+ if ($type == 'float' || $type == 'double') {
+ $type = 'float';
+ } elseif ($type != 'boolean' && $type != 'integer') {
+ $type = 'string';
+ }
+ }
+
+ settype($value, $type);
+
+ $array = $rs->blog_id ? 'local' : 'global';
+
+ $this->{$array . '_settings'}[$id] = [
+ 'ns' => $this->ns,
+ 'value' => $value,
+ 'type' => $type,
+ 'label' => (string) $rs->f('setting_label'),
+ 'global' => $rs->blog_id == ''
+ ];
+ }
+
+ $this->settings = $this->global_settings;
+
+ foreach ($this->local_settings as $id => $v) {
+ $this->settings[$id] = $v;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns true if a setting exist, else false
+ *
+ * @param string $id The identifier
+ * @param boolean $global The global
+ *
+ * @return boolean
+ */
+ public function settingExists($id, $global = false)
+ {
+ $array = $global ? 'global' : 'local';
+ return isset($this->{$array . '_settings'}[$id]);
+ }
+
+ /**
+ Returns setting value if exists.
+
+ @param n string Setting name
+ @return mixed
+ */
+ public function get($n)
+ {
+ if (isset($this->settings[$n]['value'])) {
+ return $this->settings[$n]['value'];
+ }
+
+ return;
+ }
+
+ /**
+ Returns global setting value if exists.
+
+ @param n string setting name
+ @return mixed
+ */
+ public function getGlobal($n)
+ {
+ if (isset($this->global_settings[$n]['value'])) {
+ return $this->global_settings[$n]['value'];
+ }
+
+ return;
+ }
+
+ /**
+ Returns local setting value if exists.
+
+ @param n string setting name
+ @return mixed
+ */
+ public function getLocal($n)
+ {
+ if (isset($this->local_settings[$n]['value'])) {
+ return $this->local_settings[$n]['value'];
+ }
+
+ return;
+ }
+
+ /**
+ Magic __get method.
+ @copydoc ::get
+ */
+ public function __get($n)
+ {
+ return $this->get($n);
+ }
+
+ /**
+ Sets a setting in $settings property. This sets the setting for script
+ execution time only and if setting exists.
+
+ @param n string Setting name
+ @param v mixed Setting value
+ */
+ public function set($n, $v)
+ {
+ if (isset($this->settings[$n])) {
+ $this->settings[$n]['value'] = $v;
+ }
+ }
+
+ /**
+ Magic __set method.
+ @copydoc ::set
+ */
+ public function __set($n, $v)
+ {
+ $this->set($n, $v);
+ }
+
+ /**
+ Creates or updates a setting.
+
+ $type could be 'string', 'integer', 'float', 'boolean' or null. If $type is
+ null and setting exists, it will keep current setting type.
+
+ $value_change allow you to not change setting. Useful if you need to change
+ a setting label or type and don't want to change its value.
+
+ @param id string Setting ID
+ @param value mixed Setting value
+ @param type string Setting type
+ @param label string Setting label
+ @param value_change boolean Change setting value or not
+ @param global boolean Setting is global
+ */
+ public function put($id, $value, $type = null, $label = null, $value_change = true, $global = false)
+ {
+ if (!preg_match('/^[a-zA-Z][a-zA-Z0-9_]+$/', $id)) {
+ throw new Exception(sprintf(__('%s is not a valid setting id'), $id));
+ }
+
+ # We don't want to change setting value
+ if (!$value_change) {
+ if (!$global && $this->settingExists($id, false)) {
+ $value = $this->local_settings[$id]['value'];
+ } elseif ($this->settingExists($id, true)) {
+ $value = $this->global_settings[$id]['value'];
+ }
+ }
+
+ # Setting type
+ if ($type == 'double') {
+ $type = 'float';
+ } elseif ($type === null) {
+ if (!$global && $this->settingExists($id, false)) {
+ $type = $this->local_settings[$id]['type'];
+ } elseif ($this->settingExists($id, true)) {
+ $type = $this->global_settings[$id]['type'];
+ } else {
+ if (is_array($value)) {
+ $type = 'array';
+ } else {
+ $type = 'string';
+ }
+ }
+ } elseif ($type != 'boolean' && $type != 'integer' && $type != 'float' && $type != 'array') {
+ $type = 'string';
+ }
+
+ # We don't change label
+ if ($label == null) {
+ if (!$global && $this->settingExists($id, false)) {
+ $label = $this->local_settings[$id]['label'];
+ } elseif ($this->settingExists($id, true)) {
+ $label = $this->global_settings[$id]['label'];
+ }
+ }
+
+ if ($type != 'array') {
+ settype($value, $type);
+ } else {
+ $value = json_encode($value);
+ }
+
+ $cur = $this->con->openCursor($this->table);
+ $cur->setting_value = ($type == 'boolean') ? (string) (integer) $value : (string) $value;
+ $cur->setting_type = $type;
+ $cur->setting_label = $label;
+
+ #If we are local, compare to global value
+ if (!$global && $this->settingExists($id, true)) {
+ $g = $this->global_settings[$id];
+ $same_setting = $g['ns'] == $this->ns && $g['value'] == $value
+ && $g['type'] == $type && $g['label'] == $label;
+
+ # Drop setting if same value as global
+ if ($same_setting && $this->settingExists($id, false)) {
+ $this->drop($id);
+ } elseif ($same_setting) {
+ return;
+ }
+ }
+
+ if ($this->settingExists($id, $global) && $this->ns == $this->settings[$id]['ns']) {
+ if ($global) {
+ $where = 'WHERE blog_id IS NULL ';
+ } else {
+ $where = "WHERE blog_id = '" . $this->con->escape($this->blog_id) . "' ";
+ }
+
+ $cur->update($where . "AND setting_id = '" . $this->con->escape($id) . "' AND setting_ns = '" . $this->con->escape($this->ns) . "' ");
+ } else {
+ $cur->setting_id = $id;
+ $cur->blog_id = $global ? null : $this->blog_id;
+ $cur->setting_ns = $this->ns;
+
+ $cur->insert();
+ }
+ }
+
+ /**
+ Rename an existing setting in a Namespace
+
+ @param $oldId string Current setting name
+ @param $newId string New setting name
+ @return boolean
+ */
+ public function rename($oldId, $newId)
+ {
+ if (!$this->ns) {
+ throw new Exception(__('No namespace specified'));
+ }
+
+ if (!array_key_exists($oldId, $this->settings) || array_key_exists($newId, $this->settings)) {
+ return false;
+ }
+
+ // Rename the setting in the settings array
+ $this->settings[$newId] = $this->settings[$oldId];
+ unset($this->settings[$oldId]);
+
+ // Rename the setting in the database
+ $strReq = 'UPDATE ' . $this->table .
+ " SET setting_id = '" . $this->con->escape($newId) . "' " .
+ " WHERE setting_ns = '" . $this->con->escape($this->ns) . "' " .
+ " AND setting_id = '" . $this->con->escape($oldId) . "' ";
+ $this->con->execute($strReq);
+ return true;
+ }
+
+ /**
+ Removes an existing setting in a Namespace
+
+ @param id string Setting ID
+ */
+ public function drop($id)
+ {
+ if (!$this->ns) {
+ throw new Exception(__('No namespace specified'));
+ }
+
+ $strReq = 'DELETE FROM ' . $this->table . ' ';
+
+ if ($this->blog_id === null) {
+ $strReq .= 'WHERE blog_id IS NULL ';
+ } else {
+ $strReq .= "WHERE blog_id = '" . $this->con->escape($this->blog_id) . "' ";
+ }
+
+ $strReq .= "AND setting_id = '" . $this->con->escape($id) . "' ";
+ $strReq .= "AND setting_ns = '" . $this->con->escape($this->ns) . "' ";
+
+ $this->con->execute($strReq);
+ }
+
+ /**
+ * Removes every existing specific setting in a namespace
+ *
+ * @param string $id Setting ID
+ * @param boolean $global Remove global setting too
+ */
+ public function dropEvery($id, $global = false)
+ {
+ if (!$this->ns) {
+ throw new Exception(__('No namespace specified'));
+ }
+
+ $strReq = 'DELETE FROM ' . $this->table . ' ';
+ if (!$global) {
+ $strReq .= 'WHERE blog_id IS NOT NULL ';
+ }
+ $strReq .= "AND setting_id = '" . $this->con->escape($id) . "' ";
+ $strReq .= "AND setting_ns = '" . $this->con->escape($this->ns) . "' ";
+
+ $this->con->execute($strReq);
+ }
+
+ /**
+ Removes all existing settings in a Namespace
+
+ @param force_global boolean Force global pref drop
+ */
+ public function dropAll($force_global = false)
+ {
+ if (!$this->ns) {
+ throw new Exception(__('No namespace specified'));
+ }
+
+ $strReq = 'DELETE FROM ' . $this->table . ' ';
+
+ if (($force_global) || ($this->blog_id === null)) {
+ $strReq .= 'WHERE blog_id IS NULL ';
+ $global = true;
+ } else {
+ $strReq .= "WHERE blog_id = '" . $this->con->escape($this->blog_id) . "' ";
+ $global = false;
+ }
+
+ $strReq .= "AND setting_ns = '" . $this->con->escape($this->ns) . "' ";
+
+ $this->con->execute($strReq);
+
+ $array = $global ? 'global' : 'local';
+ unset($this->{$array . '_settings'});
+ $this->{$array . '_settings'} = [];
+
+ $array = $global ? 'local' : 'global';
+ $this->settings = $this->{$array . '_settings'};
+ }
+
+ /**
+ Returns $ns property content.
+
+ @return string
+ */
+ public function dumpNamespace()
+ {
+ return $this->ns;
+ }
+
+ /**
+ Returns $settings property content.
+
+ @return array
+ */
+ public function dumpSettings()
+ {
+ return $this->settings;
+ }
+
+ /**
+ Returns $local_settings property content.
+
+ @return array
+ */
+ public function dumpLocalSettings()
+ {
+ return $this->local_settings;
+ }
+
+ /**
+ Returns $global_settings property content.
+
+ @return array
+ */
+ public function dumpGlobalSettings()
+ {
+ return $this->global_settings;
+ }
+
+}
diff --git a/dotclear._no/inc/core/class.dc.plugins.php b/dotclear._no/inc/core/class.dc.plugins.php
new file mode 100644
index 0000000..3beb1f1
--- /dev/null
+++ b/dotclear._no/inc/core/class.dc.plugins.php
@@ -0,0 +1,57 @@
+$priority is an integer. Modules are sorted by priority and name.
+ Lowest priority comes first. This property is currently ignored when dealing
+ with themes.
+
+ @param name string Module name
+ @param desc string Module description
+ @param author string Module author name
+ @param version string Module version
+ @param properties array extra properties (currently available keys : permissions, priority, standalone_config, type)
+ */
+ public function registerModule($name, $desc, $author, $version, $properties = [])
+ {
+ # Fallback to legacy registerModule parameters
+ if (!is_array($properties)) {
+ $args = func_get_args();
+ $properties = [];
+ if (isset($args[4])) {
+ $properties['permissions'] = $args[4];
+ }
+ if (isset($args[5])) {
+ $properties['priority'] = (integer) $args[5];
+ }
+ }
+
+ parent::registerModule($name, $desc, $author, $version, $properties);
+ }
+}
diff --git a/dotclear._no/inc/core/class.dc.postmedia.php b/dotclear._no/inc/core/class.dc.postmedia.php
new file mode 100644
index 0000000..c263eaa
--- /dev/null
+++ b/dotclear._no/inc/core/class.dc.postmedia.php
@@ -0,0 +1,171 @@
+dcCore dcCore instance
+ protected $con; ///< connection Database connection
+ protected $table; ///< string Post-Media table name
+
+ /**
+ Object constructor.
+
+ @param core dcCore dcCore instance
+ @param type string Media type filter
+ */
+ public function __construct($core, $type = '')
+ {
+ $this->core = &$core;
+ $this->con = &$core->con;
+ $this->table = $this->core->prefix . 'post_media';
+ }
+
+ /**
+ Returns media items attached to a blog post. Result is an array containing
+ fileItems objects.
+
+ @param post_id integer Post ID
+ @param media_id integer Optionnal media ID
+ @return array Array of fileItems
+ */
+ public function getPostMedia($params = [])
+ {
+ $strReq =
+ 'SELECT M.media_file, M.media_id, M.media_path, M.media_title, M.media_meta, M.media_dt, ' .
+ 'M.media_creadt, M.media_upddt, M.media_private, M.user_id, PM.post_id ';
+
+ if (!empty($params['columns']) && is_array($params['columns'])) {
+ $strReq .= implode(', ', $params['columns']) . ', ';
+ }
+
+ $strReq .=
+ 'FROM ' . $this->core->prefix . 'media M ' .
+ 'INNER JOIN ' . $this->table . ' PM ON (M.media_id = PM.media_id) ';
+
+ if (!empty($params['from'])) {
+ $strReq .= $params['from'] . ' ';
+ }
+
+ $where = [];
+ if (isset($params['post_id'])) {
+ $where[] = "PM.post_id " . $this->con->in($params['post_id']);
+ }
+ if (isset($params['media_id'])) {
+ $where[] = "M.media_id " . $this->con->in($params['media_id']);
+ }
+ if (isset($params['media_path'])) {
+ $where[] = "M.media_path " . $this->con->in($params['media_path']);
+ }
+ if (isset($params['link_type'])) {
+ $where[] = "PM.link_type " . $this->con->in($params['link_type']);
+ } else {
+ $where[] = "PM.link_type='attachment'";
+ }
+
+ $strReq .= 'WHERE ' . join('AND ', $where) . ' ';
+
+ if (isset($params['sql'])) {
+ $strReq .= $params['sql'];
+ }
+
+ $rs = $this->con->select($strReq);
+
+ return $rs;
+ }
+
+ /**
+ Attaches a media to a post.
+
+ @param post_id integer Post ID
+ @param media_id integer Optionnal media ID
+ @param link_type string Optionnal link type (default: attachment)
+ */
+ public function addPostMedia($post_id, $media_id, $link_type = 'attachment')
+ {
+ $post_id = (integer) $post_id;
+ $media_id = (integer) $media_id;
+
+ $f = $this->getPostMedia(['post_id' => $post_id, 'media_id' => $media_id, 'link_type' => $link_type]);
+
+ if (!$f->isEmpty()) {
+ return;
+ }
+
+ $cur = $this->con->openCursor($this->table);
+ $cur->post_id = $post_id;
+ $cur->media_id = $media_id;
+ $cur->link_type = $link_type;
+
+ $cur->insert();
+ $this->core->blog->triggerBlog();
+ }
+
+ /**
+ Detaches a media from a post.
+
+ @param post_id integer Post ID
+ @param media_id integer Optionnal media ID
+ @param link_type string Optionnal link type
+ */
+ public function removePostMedia($post_id, $media_id, $link_type = null)
+ {
+ $post_id = (integer) $post_id;
+ $media_id = (integer) $media_id;
+
+ $strReq = 'DELETE FROM ' . $this->table . ' ' .
+ 'WHERE post_id = ' . $post_id . ' ' .
+ 'AND media_id = ' . $media_id . ' ';
+ if ($link_type != null) {
+ $strReq .= "AND link_type = '" . $this->con->escape($link_type) . "'";
+ }
+ $this->con->execute($strReq);
+ $this->core->blog->triggerBlog();
+ }
+
+ /**
+ Returns media items attached to a blog post. Result is an array containing
+ fileItems objects.
+
+ @param post_id integer Post ID
+ @param media_id integer Optionnal media ID
+ @return array Array of fileItems
+ */
+ public function getLegacyPostMedia($post_id, $media_id = null)
+ {
+ $post_id = (integer) $post_id;
+
+ $strReq =
+ 'SELECT media_file, M.media_id, media_path, media_title, media_meta, media_dt, ' .
+ 'media_creadt, media_upddt, media_private, user_id ' .
+ 'FROM ' . $this->table . ' M ' .
+ 'INNER JOIN ' . $this->table_ref . ' PM ON (M.media_id = PM.media_id) ' .
+ "WHERE media_path = '" . $this->path . "' " .
+ 'AND post_id = ' . $post_id . ' ';
+
+ if ($media_id) {
+ $strReq .= 'AND M.media_id = ' . (integer) $media_id . ' ';
+ }
+
+ $rs = $this->con->select($strReq);
+
+ $res = [];
+
+ while ($rs->fetch()) {
+ $f = $this->fileRecord($rs);
+ if ($f !== null) {
+ $res[] = $f;
+ }
+ }
+
+ return $res;
+ }
+
+}
diff --git a/dotclear._no/inc/core/class.dc.prefs.php b/dotclear._no/inc/core/class.dc.prefs.php
new file mode 100644
index 0000000..69a4685
--- /dev/null
+++ b/dotclear._no/inc/core/class.dc.prefs.php
@@ -0,0 +1,175 @@
+connection Database connection object
+ protected $table; ///< string Prefs table name
+ protected $user_id; ///< string User ID
+
+ protected $workspaces = []; ///< array Associative workspaces array
+
+ protected $ws; ///< string Current workspace
+
+ /**
+ Object constructor. Retrieves user prefs and puts them in $workspaces
+ array. Local (user) prefs have a highest priority than global prefs.
+
+ @param core dcCore dcCore object
+ @param user_id string User ID
+ */
+ public function __construct($core, $user_id)
+ {
+ $this->con = &$core->con;
+ $this->table = $core->prefix . 'pref';
+ $this->user_id = &$user_id;
+ try { $this->loadPrefs();} catch (Exception $e) {
+ if (version_compare($core->getVersion('core'), '2.3', '>')) {
+ trigger_error(__('Unable to retrieve workspaces:') . ' ' . $this->con->error(), E_USER_ERROR);
+ }
+ }
+ }
+
+ /**
+ Retrieves all workspaces (and their prefs) from database, with one query.
+ */
+ private function loadPrefs()
+ {
+ $strReq = 'SELECT user_id, pref_id, pref_value, ' .
+ 'pref_type, pref_label, pref_ws ' .
+ 'FROM ' . $this->table . ' ' .
+ "WHERE user_id = '" . $this->con->escape($this->user_id) . "' " .
+ 'OR user_id IS NULL ' .
+ 'ORDER BY pref_ws ASC, pref_id ASC';
+ try {
+ $rs = $this->con->select($strReq);
+ } catch (Exception $e) {
+ throw $e;
+ }
+
+ /* Prevent empty tables (install phase, for instance) */
+ if ($rs->isEmpty()) {
+ return;
+ }
+
+ do {
+ $ws = trim($rs->f('pref_ws'));
+ if (!$rs->isStart()) {
+ // we have to go up 1 step, since workspaces construction performs a fetch()
+ // at very first time
+ $rs->movePrev();
+ }
+ $this->workspaces[$ws] = new dcWorkspace($GLOBALS['core'], $this->user_id, $ws, $rs);
+ } while (!$rs->isStart());
+ }
+
+ /**
+ Create a new workspace. If the workspace already exists, return it without modification.
+
+ @param ws string Workspace name
+ @return dcWorkspace The workspace created
+ */
+ public function addWorkspace($ws)
+ {
+ if (!array_key_exists($ws, $this->workspaces)) {
+ $this->workspaces[$ws] = new dcWorkspace($GLOBALS['core'], $this->user_id, $ws);
+ }
+ return $this->workspaces[$ws];
+ }
+
+ /**
+ Rename a workspace.
+
+ @param oldWs string Old workspace name
+ @param newws string New workspace name
+ @return boolean
+ */
+ public function renWorkspace($oldNs, $newNs)
+ {
+ if (!array_key_exists($oldWs, $this->workspaces) || array_key_exists($newWs, $this->workspaces)) {
+ return false;
+ }
+
+ // Rename the workspace in the workspace array
+ $this->workspaces[$newWs] = $this->workspaces[$oldWs];
+ unset($this->workspaces[$oldWs]);
+
+ // Rename the workspace in the database
+ $strReq = 'UPDATE ' . $this->table .
+ " SET pref_ws = '" . $this->con->escape($newWs) . "' " .
+ " WHERE pref_ws = '" . $this->con->escape($oldWs) . "' ";
+ $this->con->execute($strReq);
+ return true;
+ }
+
+ /**
+ Delete a whole workspace with all preferences pertaining to it.
+
+ @param ws string Workspace name
+ @return boolean
+ */
+ public function delWorkspace($ws)
+ {
+ if (!array_key_exists($ws, $this->workspaces)) {
+ return false;
+ }
+
+ // Remove the workspace from the workspace array
+ unset($this->workspaces[$ws]);
+
+ // Delete all preferences from the workspace in the database
+ $strReq = 'DELETE FROM ' . $this->table .
+ " WHERE pref_ws = '" . $this->con->escape($ws) . "' ";
+ $this->con->execute($strReq);
+ return true;
+ }
+
+ /**
+ Returns full workspace with all prefs pertaining to it.
+
+ @param ws string Workspace name
+ @return dcWorkspace
+ */
+ public function get($ws)
+ {
+ if (array_key_exists($ws, $this->workspaces)) {
+ return $this->workspaces[$ws];
+ }
+
+ return;
+ }
+
+ /**
+ Magic __get method.
+ @copydoc ::get
+ */
+ public function __get($n)
+ {
+ return $this->get($n);
+ }
+
+ /**
+ Returns $workspaces property content.
+
+ @return array
+ */
+ public function dumpWorkspaces()
+ {
+ return $this->workspaces;
+ }
+
+}
diff --git a/dotclear._no/inc/core/class.dc.rest.php b/dotclear._no/inc/core/class.dc.rest.php
new file mode 100644
index 0000000..b6aefca
--- /dev/null
+++ b/dotclear._no/inc/core/class.dc.rest.php
@@ -0,0 +1,47 @@
+dcCore dcCore instance
+ */
+ public function __construct($core)
+ {
+ parent::__construct();
+
+ $this->core = &$core;
+ }
+
+ /**
+ Rest method call.
+
+ @param name string Method name
+ @param get array GET parameters copy
+ @param post array POST parameters copy
+ @return mixed Rest method result
+ */
+ protected function callFunction($name, $get, $post)
+ {
+ if (isset($this->functions[$name])) {
+ return call_user_func($this->functions[$name], $this->core, $get, $post);
+ }
+ }
+}
diff --git a/dotclear._no/inc/core/class.dc.rs.extensions.php b/dotclear._no/inc/core/class.dc.rs.extensions.php
new file mode 100644
index 0000000..dc8a2ee
--- /dev/null
+++ b/dotclear._no/inc/core/class.dc.rs.extensions.php
@@ -0,0 +1,966 @@
+boolean
+ */
+ public static function isEditable($rs)
+ {
+ # If user is admin or contentadmin, true
+ if ($rs->core->auth->check('contentadmin', $rs->core->blog->id)) {
+ return true;
+ }
+
+ # No user id in result ? false
+ if (!$rs->exists('user_id')) {
+ return false;
+ }
+
+ # If user is usage and owner of the entrie
+ if ($rs->core->auth->check('usage', $rs->core->blog->id)
+ && $rs->user_id == $rs->core->auth->userID()) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ Returns whether post is deletable
+
+ @param rs Invisible parameter
+ @return boolean
+ */
+ public static function isDeletable($rs)
+ {
+ # If user is admin, or contentadmin, true
+ if ($rs->core->auth->check('contentadmin', $rs->core->blog->id)) {
+ return true;
+ }
+
+ # No user id in result ? false
+ if (!$rs->exists('user_id')) {
+ return false;
+ }
+
+ # If user has delete rights and is owner of the entrie
+ if ($rs->core->auth->check('delete', $rs->core->blog->id)
+ && $rs->user_id == $rs->core->auth->userID()) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ Returns whether post is the first one of its day.
+
+ @param rs Invisible parameter
+ @return boolean
+ */
+ public static function firstPostOfDay($rs)
+ {
+ if ($rs->isStart()) {
+ return true;
+ }
+
+ $cdate = date('Ymd', strtotime($rs->post_dt));
+ $rs->movePrev();
+ $ndate = date('Ymd', strtotime($rs->post_dt));
+ $rs->moveNext();
+ return $ndate != $cdate;
+ }
+
+ /**
+ Returns whether post is the last one of its day.
+
+ @param rs Invisible parameter
+ @return boolean
+ */
+ public static function lastPostOfDay($rs)
+ {
+ if ($rs->isEnd()) {
+ return true;
+ }
+
+ $cdate = date('Ymd', strtotime($rs->post_dt));
+ $rs->moveNext();
+ $ndate = date('Ymd', strtotime($rs->post_dt));
+ $rs->movePrev();
+ return $ndate != $cdate;
+ }
+
+ /**
+ Returns whether comments are enabled on post.
+
+ @param rs Invisible parameter
+ @return boolean
+ */
+ public static function commentsActive($rs)
+ {
+ return
+ $rs->core->blog->settings->system->allow_comments
+ && $rs->post_open_comment
+ && ($rs->core->blog->settings->system->comments_ttl == 0 ||
+ time() - ($rs->core->blog->settings->system->comments_ttl * 86400) < $rs->getTS());
+ }
+
+ /**
+ Returns whether trackbacks are enabled on post.
+
+ @param rs Invisible parameter
+ @return boolean
+ */
+ public static function trackbacksActive($rs)
+ {
+ return
+ $rs->core->blog->settings->system->allow_trackbacks
+ && $rs->post_open_tb
+ && ($rs->core->blog->settings->system->trackbacks_ttl == 0 ||
+ time() - ($rs->core->blog->settings->system->trackbacks_ttl * 86400) < $rs->getTS());
+ }
+
+ /**
+ Returns whether post has at least one comment.
+
+ @param rs Invisible parameter
+ @return boolean
+ */
+ public static function hasComments($rs)
+ {
+ return $rs->nb_comment > 0;
+ }
+
+ /**
+ Returns whether post has at least one trackbacks.
+
+ @return boolean
+ */
+ public static function hasTrackbacks($rs)
+ {
+ return $rs->nb_trackback > 0;
+ }
+
+ /**
+ Returns whether post has been updated since publication.
+
+ @return boolean
+ */
+ public static function isRepublished($rs)
+ {
+ return ($rs->getTS('upddt') + dt::getTimeOffset($rs->post_tz)) > $rs->getTS();
+ }
+
+ /**
+ Returns full post URL.
+
+ @param rs Invisible parameter
+ @return string
+ */
+ public static function getURL($rs)
+ {
+ return $rs->core->blog->url . $rs->core->getPostPublicURL(
+ $rs->post_type, html::sanitizeURL($rs->post_url)
+ );
+ }
+
+ /**
+ Returns full post category URL.
+
+ @param rs Invisible parameter
+ @return string
+ */
+ public static function getCategoryURL($rs)
+ {
+ return $rs->core->blog->url . $rs->core->url->getURLFor('category', html::sanitizeURL($rs->cat_url));
+ }
+
+ /**
+ Returns whether post has an excerpt.
+
+ @param rs Invisible parameter
+ @return boolean
+ */
+ public static function isExtended($rs)
+ {
+ return $rs->post_excerpt_xhtml != '';
+ }
+
+ /**
+ Returns post timestamp.
+
+ @param rs Invisible parameter
+ @param type string (dt|upddt|creadt) defaults to post_dt
+ @return integer
+ */
+ public static function getTS($rs, $type = '')
+ {
+ if ($type == 'upddt') {
+ return strtotime($rs->post_upddt);
+ } elseif ($type == 'creadt') {
+ return strtotime($rs->post_creadt);
+ } else {
+ return strtotime($rs->post_dt);
+ }
+ }
+
+ /**
+ Returns post date formating according to the ISO 8601 standard.
+
+ @param rs Invisible parameter
+ @param type string (dt|upddt|creadt) defaults to post_dt
+ @return string
+ */
+ public static function getISO8601Date($rs, $type = '')
+ {
+ if ($type == 'upddt' || $type == 'creadt') {
+ return dt::iso8601($rs->getTS($type) + dt::getTimeOffset($rs->post_tz), $rs->post_tz);
+ } else {
+ return dt::iso8601($rs->getTS(), $rs->post_tz);
+ }
+ }
+
+ /**
+ Returns post date formating according to RFC 822.
+
+ @param rs Invisible parameter
+ @param type string (dt|upddt|creadt) defaults to post_dt
+ @return string
+ */
+ public static function getRFC822Date($rs, $type = '')
+ {
+ if ($type == 'upddt' || $type == 'creadt') {
+ return dt::rfc822($rs->getTS($type) + dt::getTimeOffset($rs->post_tz), $rs->post_tz);
+ } else {
+ return dt::rfc822($rs->getTS($type), $rs->post_tz);
+ }
+ }
+
+ /**
+ Returns post date with $format as formatting pattern. If format
+ is empty, uses date_format blog setting.
+
+ @param rs Invisible parameter
+ @param format string Date format pattern
+ @param type string (dt|upddt|creadt) defaults to post_dt
+ @return string
+ */
+ public static function getDate($rs, $format, $type = '')
+ {
+ if (!$format) {
+ $format = $rs->core->blog->settings->system->date_format;
+ }
+
+ if ($type == 'upddt') {
+ return dt::dt2str($format, $rs->post_upddt, $rs->post_tz);
+ } elseif ($type == 'creadt') {
+ return dt::dt2str($format, $rs->post_creadt, $rs->post_tz);
+ } else {
+ return dt::dt2str($format, $rs->post_dt);
+ }
+ }
+
+ /**
+ Returns post time with $format as formatting pattern. If format
+ is empty, uses time_format blog setting.
+
+ @param rs Invisible parameter
+ @param format string Time format pattern
+ @param type string (dt|upddt|creadt) defaults to post_dt
+ @return string
+ */
+ public static function getTime($rs, $format, $type = '')
+ {
+ if (!$format) {
+ $format = $rs->core->blog->settings->system->time_format;
+ }
+
+ if ($type == 'upddt') {
+ return dt::dt2str($format, $rs->post_upddt, $rs->post_tz);
+ } elseif ($type == 'creadt') {
+ return dt::dt2str($format, $rs->post_creadt, $rs->post_tz);
+ } else {
+ return dt::dt2str($format, $rs->post_dt);
+ }
+ }
+
+ /**
+ Returns author common name using user_id, user_name, user_firstname and
+ user_displayname fields.
+
+ @param rs Invisible parameter
+ @return string
+ */
+ public static function getAuthorCN($rs)
+ {
+ return dcUtils::getUserCN($rs->user_id, $rs->user_name,
+ $rs->user_firstname, $rs->user_displayname);
+ }
+
+ /**
+ Returns author common name with a link if he specified one in its
+ preferences.
+
+ @param rs Invisible parameter
+ @return string
+ */
+ public static function getAuthorLink($rs)
+ {
+ $res = '%1$s';
+ $url = $rs->user_url;
+ if ($url) {
+ $res = '%1$s ';
+ }
+
+ return sprintf($res, html::escapeHTML($rs->getAuthorCN()), html::escapeHTML($url));
+ }
+
+ /**
+ Returns author e-mail address. If $encoded is true, "@" sign is
+ replaced by "%40" and "." by "%2e".
+
+ @param rs Invisible parameter
+ @param encoded boolean Encode address.
+ @return string
+ */
+ public static function getAuthorEmail($rs, $encoded = true)
+ {
+ if ($encoded) {
+ return strtr($rs->user_email, ['@' => '%40', '.' => '%2e']);
+ }
+ return $rs->user_email;
+ }
+
+ /**
+ Returns post feed unique ID.
+
+ @param rs Invisible parameter
+ @return string
+ */
+ public static function getFeedID($rs)
+ {
+ return 'urn:md5:' . md5($rs->core->blog->uid . $rs->post_id);
+ }
+
+ /**
+ Returns trackback RDF information block in HTML comment.
+
+ @param rs Invisible parameter
+ @return string
+ */
+ public static function getTrackbackData($rs, $format = 'html')
+ {
+ return
+ ($format == 'xml' ? "\n" : '') .
+ "\n";
+ }
+
+ /**
+ Returns post trackback full URL.
+
+ @param rs Invisible parameter
+ @return string
+ */
+ public static function getTrackbackLink($rs)
+ {
+ return $rs->core->blog->url . $rs->core->url->getURLFor('trackback', $rs->post_id);
+ }
+
+ /**
+ Returns post content. If $absolute_urls is true, appends full
+ blog URL to each relative post URLs.
+
+ @param rs Invisible parameter
+ @param absolute_urls boolean With absolute URLs
+ @return string
+ */
+ public static function getContent($rs, $absolute_urls = false)
+ {
+ if ($absolute_urls) {
+ return html::absoluteURLs($rs->post_content_xhtml, $rs->getURL());
+ } else {
+ return $rs->post_content_xhtml;
+ }
+ }
+
+ /**
+ Returns post excerpt. If $absolute_urls is true, appends full
+ blog URL to each relative post URLs.
+
+ @param rs Invisible parameter
+ @param absolute_urls boolean With absolute URLs
+ @return string
+ */
+ public static function getExcerpt($rs, $absolute_urls = false)
+ {
+ if ($absolute_urls) {
+ return html::absoluteURLs($rs->post_excerpt_xhtml, $rs->getURL());
+ } else {
+ return $rs->post_excerpt_xhtml;
+ }
+ }
+
+ /**
+ Returns post media count using a subquery.
+
+ @param rs Invisible parameter
+ @return integer
+ */
+ public static function countMedia($rs, $link_type = null)
+ {
+ if (isset($rs->_nb_media[$rs->index()])) {
+ return $rs->_nb_media[$rs->index()];
+ } else {
+ $strReq =
+ 'SELECT count(media_id) ' .
+ 'FROM ' . $rs->core->prefix . 'post_media ' .
+ 'WHERE post_id = ' . (integer) $rs->post_id . ' ';
+ if ($link_type != null) {
+ $strReq .= "AND link_type = '" . $rs->core->con->escape($link_type) . "'";
+ }
+
+ $res = (integer) $rs->core->con->select($strReq)->f(0);
+ $rs->_nb_media[$rs->index()] = $res;
+ return $res;
+ }
+ }
+
+ /**
+ * Returns true if current category if in given cat_url subtree
+ *
+ * @param record $rs Invisible parameter
+ * @param string $cat_url The cat url
+ *
+ * @return boolean true if current cat is in given cat subtree
+ */
+ public static function underCat($rs, $cat_url)
+ {
+ return $rs->core->blog->IsInCatSubtree($rs->cat_url, $cat_url);
+ }
+}
+
+/**
+@ingroup DC_CORE
+@brief Dotclear comment record helpers.
+
+This class adds new methods to database comment results.
+You can call them on every record comming from dcBlog::getComments and similar
+methods.
+
+@warning You should not give the first argument (usualy $rs) of every described
+function.
+ */
+class rsExtComment
+{
+ /**
+ Returns comment date with $format as formatting pattern. If
+ format is empty, uses date_format blog setting.
+
+ @param rs Invisible parameter
+ @param format string Date format pattern
+ @param type string (dt|upddt) defaults to comment_dt
+ @return string
+ */
+ public static function getDate($rs, $format, $type = '')
+ {
+ if (!$format) {
+ $format = $rs->core->blog->settings->system->date_format;
+ }
+
+ if ($type == 'upddt') {
+ return dt::dt2str($format, $rs->comment_upddt, $rs->comment_tz);
+ } else {
+ return dt::dt2str($format, $rs->comment_dt);
+ }
+ }
+
+ /**
+ Returns comment time with $format as formatting pattern. If
+ format is empty, uses time_format blog setting.
+
+ @param rs Invisible parameter
+ @param format string Date format pattern
+ @param type string (dt|upddt) defaults to comment_dt
+ @return string
+ */
+ public static function getTime($rs, $format, $type = '')
+ {
+ if (!$format) {
+ $format = $rs->core->blog->settings->system->time_format;
+ }
+
+ if ($type == 'upddt') {
+ return dt::dt2str($format, $rs->comment_updt, $rs->comment_tz);
+ } else {
+ return dt::dt2str($format, $rs->comment_dt);
+ }
+ }
+
+ /**
+ Returns comment timestamp.
+
+ @param rs Invisible parameter
+ @param type string (dt|upddt) defaults to comment_dt
+ @return integer
+ */
+ public static function getTS($rs, $type = '')
+ {
+ if ($type == 'upddt') {
+ return strtotime($rs->comment_upddt);
+ } else {
+ return strtotime($rs->comment_dt);
+ }
+ }
+
+ /**
+ Returns comment date formating according to the ISO 8601 standard.
+
+ @param rs Invisible parameter
+ @param type string (dt|upddt) defaults to comment_dt
+ @return string
+ */
+ public static function getISO8601Date($rs, $type = '')
+ {
+ if ($type == 'upddt') {
+ return dt::iso8601($rs->getTS($type) + dt::getTimeOffset($rs->comment_tz), $rs->comment_tz);
+ } else {
+ return dt::iso8601($rs->getTS(), $rs->comment_tz);
+ }
+ }
+
+ /**
+ Returns comment date formating according to RFC 822.
+
+ @param rs Invisible parameter
+ @param type string (dt|upddt) defaults to comment_dt
+ @return string
+ */
+ public static function getRFC822Date($rs, $type = '')
+ {
+ if ($type == 'upddt') {
+ return dt::rfc822($rs->getTS($type) + dt::getTimeOffset($rs->comment_tz), $rs->comment_tz);
+ } else {
+ return dt::rfc822($rs->getTS(), $rs->comment_tz);
+ }
+ }
+
+ /**
+ Returns comment content. If $absolute_urls is true, appends full
+ blog URL to each relative post URLs.
+
+ @param rs Invisible parameter
+ @param absolute_urls boolean With absolute URLs
+ @return string
+ */
+ public static function getContent($rs, $absolute_urls = false)
+ {
+ $res = $rs->comment_content;
+
+
+ if ($rs->core->blog->settings->system->comments_nofollow) {
+ $res = preg_replace_callback('##ms', ['self', 'noFollowURL'], $res);
+ } else {
+ $res = preg_replace_callback('# #ms', ['self', 'UgcURL'], $res);
+ }
+
+ if ($absolute_urls) {
+ $res = html::absoluteURLs($res, $rs->getPostURL());
+ }
+
+ return $res;
+ }
+
+ private static function noFollowURL($m)
+ {
+ if (preg_match('/rel="ugc nofollow"/', $m[1])) {
+ return $m[0];
+ }
+
+ return ' ';
+ }
+
+ private static function UgcURL($m)
+ {
+ if (preg_match('/rel="ugc"/', $m[1])) {
+ return $m[0];
+ }
+
+ return ' ';
+ }
+
+ /**
+ Returns comment author link to his website if he specified one.
+
+ @param rs Invisible parameter
+ @return string
+ */
+ public static function getAuthorURL($rs)
+ {
+ if (trim($rs->comment_site)) {
+ return trim($rs->comment_site);
+ }
+ }
+
+ /**
+ Returns comment post full URL.
+
+ @param rs Invisible parameter
+ @return string
+ */
+ public static function getPostURL($rs)
+ {
+ return $rs->core->blog->url . $rs->core->getPostPublicURL(
+ $rs->post_type, html::sanitizeURL($rs->post_url)
+ );
+ }
+
+ /**
+ Returns comment author name in a link to his website if he specified one.
+
+ @param rs Invisible parameter
+ @return string
+ */
+ public static function getAuthorLink($rs)
+ {
+ $res = '%1$s';
+ $url = $rs->getAuthorURL();
+ if ($url) {
+ $res = ' %1$s ';
+ }
+
+ $rel = 'ugc';
+ if ($rs->core->blog->settings->system->comments_nofollow) {
+ $rel .= ' nofollow';
+ }
+
+ return sprintf($res, html::escapeHTML($rs->comment_author), html::escapeHTML($url), $rel);
+ }
+
+ /**
+ Returns comment author e-mail address. If $encoded is true,
+ "@" sign is replaced by "%40" and "." by "%2e".
+
+ @param rs Invisible parameter
+ @param encoded boolean Encode address.
+ @return string
+ */
+ public static function getEmail($rs, $encoded = true)
+ {
+ if ($encoded) {
+ return strtr($rs->comment_email, ['@' => '%40', '.' => '%2e']);
+ }
+ return $rs->comment_email;
+ }
+
+ /**
+ Returns trackback site title if comment is a trackback.
+
+ @param rs Invisible parameter
+ @return string
+ */
+ public static function getTrackbackTitle($rs)
+ {
+ if ($rs->comment_trackback == 1 &&
+ preg_match('|(.*?)
|msU', $rs->comment_content,
+ $match)) {
+ return html::decodeEntities($match[1]);
+ }
+ }
+
+ /**
+ Returns trackback content if comment is a trackback.
+
+ @param rs Invisible parameter
+ @return string
+ */
+ public static function getTrackbackContent($rs)
+ {
+ if ($rs->comment_trackback == 1) {
+ return preg_replace('|.*?
|msU', '',
+ $rs->comment_content);
+ }
+ }
+
+ /**
+ Returns comment feed unique ID.
+
+ @param rs Invisible parameter
+ @return string
+ */
+ public static function getFeedID($rs)
+ {
+ return 'urn:md5:' . md5($rs->core->blog->uid . $rs->comment_id);
+ }
+
+ /**
+ Returns whether comment is from the post author.
+
+ @param rs Invisible parameter
+ @return boolean
+ */
+ public static function isMe($rs)
+ {
+ return
+ $rs->comment_email && $rs->comment_site &&
+ $rs->comment_email == $rs->user_email &&
+ $rs->comment_site == $rs->user_url;
+ }
+}
+
+/**
+@ingroup DC_CORE
+@brief Dotclear dates record helpers.
+
+This class adds new methods to database dates results.
+You can call them on every record comming from dcBlog::getDates.
+
+@warning You should not give the first argument (usualy $rs) of every described
+function.
+ */
+class rsExtDates
+{
+ /**
+ @param rs Invisible parameter
+ @return integer Date timestamp
+ */
+ public static function ts($rs)
+ {
+ return strtotime($rs->dt);
+ }
+
+ /**
+ @param rs Invisible parameter
+ @return string Date year
+ */
+ public static function year($rs)
+ {
+ return date('Y', strtotime($rs->dt));
+ }
+
+ /**
+ @param rs Invisible parameter
+ @return string Date month
+ */
+ public static function month($rs)
+ {
+ return date('m', strtotime($rs->dt));
+ }
+
+ /**
+ @param rs Invisible parameter
+ @return integer Date day
+ */
+ public static function day($rs)
+ {
+ return date('d', strtotime($rs->dt));
+ }
+
+ /**
+ Returns date month archive full URL.
+
+ @param rs Invisible parameter
+ @param core dcCore dcCore instance
+ @return integer
+ */
+ public static function url($rs, $core)
+ {
+ $url = date('Y/m', strtotime($rs->dt));
+
+ return $core->blog->url . $core->url->getURLFor('archive', $url);
+ }
+
+ /**
+ Returns whether date is the first of year.
+
+ @param rs Invisible parameter
+ @return boolean
+ */
+ public static function yearHeader($rs)
+ {
+ if ($rs->isStart()) {
+ return true;
+ }
+
+ $y = $rs->year();
+ $rs->movePrev();
+ $py = $rs->year();
+ $rs->moveNext();
+
+ return $y != $py;
+ }
+
+ /**
+ Returns whether date is the last of year.
+
+ @param rs Invisible parameter
+ @return boolean
+ */
+ public static function yearFooter($rs)
+ {
+ if ($rs->isEnd()) {
+ return true;
+ }
+
+ $y = $rs->year();
+ if ($rs->moveNext()) {
+ $ny = $rs->year();
+ $rs->movePrev();
+ return $y != $ny;
+ }
+ return false;
+
+ }
+}
+
+/**
+@ingroup DC_CORE
+@brief Dotclear dates record helpers.
+
+This class adds new methods to database dates results.
+You can call them on every record comming from dcAuth::checkUser and
+dcCore::getUsers.
+
+@warning You should not give the first argument (usualy $rs) of every described
+function.
+ */
+class rsExtUser
+{
+ private static $sortfield;
+ private static $sortsign;
+ /**
+ Returns a user option.
+
+ @param rs Invisible parameter
+ @param name string Option name
+ @return string
+ */
+ public static function option($rs, $name)
+ {
+ $options = self::options($rs);
+
+ if (isset($options[$name])) {
+ return $options[$name];
+ }
+ return;
+ }
+
+ /**
+ Returns all user options.
+
+ @param rs Invisible parameter
+ @return array
+ */
+ public static function options($rs)
+ {
+ $options = @unserialize($rs->user_options);
+ if (is_array($options)) {
+ return $options;
+ }
+ return [];
+ }
+
+ /**
+ Converts this record to a {@link extStaticRecord} instance.
+
+ @param rs Invisible parameter
+ */
+ public static function toExtStatic($rs)
+ {
+ if ($rs instanceof extStaticRecord) {
+ return $rs;
+ }
+ return new extStaticRecord($rs);
+ }
+}
+
+class rsExtBlog
+{
+ private static $sortfield;
+ private static $sortsign;
+ /**
+ Converts this record to a {@link extStaticRecord} instance.
+
+ @param rs Invisible parameter
+ */
+ public static function toExtStatic($rs)
+ {
+ if ($rs instanceof extStaticRecord) {
+ return $rs;
+ }
+ return new extStaticRecord($rs);
+ }
+}
+
+class extStaticRecord extends staticRecord
+{
+ private $sortfield;
+ private $sortsign;
+
+ public function __construct($rs)
+ {
+ parent::__construct($rs->__data, $rs->__info);
+ }
+
+ /**
+ Lexically sort.
+
+ @param field string sort field
+ @param order string sort order
+ */
+ public function lexicalSort($field, $order = 'asc')
+ {
+ $this->sortfield = $field;
+ $this->sortsign = strtolower($order) == 'asc' ? 1 : -1;
+
+ usort($this->__data, [$this, 'lexicalSortCallback']);
+
+ $this->sortfield = null;
+ $this->sortsign = null;
+ }
+ private function lexicalSortCallback($a, $b)
+ {
+ $a = $a[$this->sortfield];
+ $b = $b[$this->sortfield];
+
+ # Integer values
+ if ($a == (string) (integer) $a && $b == (string) (integer) $b) {
+ $a = (integer) $a;
+ $b = (integer) $b;
+ return ($a - $b) * $this->sortsign;
+ }
+
+ return strcoll(strtolower(dcUtils::removeDiacritics($a)), strtolower(dcUtils::removeDiacritics($b))) * $this->sortsign;
+ }
+}
diff --git a/dotclear._no/inc/core/class.dc.settings.php b/dotclear._no/inc/core/class.dc.settings.php
new file mode 100644
index 0000000..4bcb5ad
--- /dev/null
+++ b/dotclear._no/inc/core/class.dc.settings.php
@@ -0,0 +1,428 @@
+connection Database connection object
+ protected $con; ///< connection Database connection object
+ protected $table; ///< string Settings table name
+ protected $blog_id; ///< string Blog ID
+
+ protected $namespaces = []; ///< array Associative namespaces array
+
+ protected $ns; ///< string Current namespace
+
+ /**
+ Object constructor. Retrieves blog settings and puts them in $namespaces
+ array. Local (blog) settings have a highest priority than global settings.
+
+ @param core dcCore dcCore object
+ @param blog_id string Blog ID
+ */
+ public function __construct($core, $blog_id)
+ {
+ $this->core = &$core;
+ $this->con = &$core->con;
+ $this->table = $core->prefix . 'setting';
+ $this->blog_id = &$blog_id;
+ $this->loadSettings();
+ }
+
+ /**
+ Retrieves all namespaces (and their settings) from database, with one query.
+ */
+ private function loadSettings()
+ {
+ $strReq = 'SELECT blog_id, setting_id, setting_value, ' .
+ 'setting_type, setting_label, setting_ns ' .
+ 'FROM ' . $this->table . ' ' .
+ "WHERE blog_id = '" . $this->con->escape($this->blog_id) . "' " .
+ 'OR blog_id IS NULL ' .
+ 'ORDER BY setting_ns ASC, setting_id DESC';
+ try {
+ $rs = $this->con->select($strReq);
+ } catch (Exception $e) {
+ trigger_error(__('Unable to retrieve namespaces:') . ' ' . $this->con->error(), E_USER_ERROR);
+ }
+
+ /* Prevent empty tables (install phase, for instance) */
+ if ($rs->isEmpty()) {
+ return;
+ }
+
+ do {
+ $ns = trim($rs->f('setting_ns'));
+ if (!$rs->isStart()) {
+ // we have to go up 1 step, since namespaces construction performs a fetch()
+ // at very first time
+ $rs->movePrev();
+ }
+ $this->namespaces[$ns] = new dcNamespace($this->core, $this->blog_id, $ns, $rs);
+ } while (!$rs->isStart());
+ }
+
+ /**
+ Create a new namespace. If the namespace already exists, return it without modification.
+
+ @param ns string Namespace name
+ @return dcNamespace The namespace created
+ */
+ public function addNamespace($ns)
+ {
+ if (!array_key_exists($ns, $this->namespaces)) {
+ $this->namespaces[$ns] = new dcNamespace($this->core, $this->blog_id, $ns);
+ }
+ return $this->namespaces[$ns];
+ }
+
+ /**
+ Rename a namespace.
+
+ @param oldNs string Old namespace name
+ @param newNs string New namespace name
+ @return boolean
+ */
+ public function renNamespace($oldNs, $newNs)
+ {
+ if (!array_key_exists($oldNs, $this->namespaces) || array_key_exists($newNs, $this->namespaces)) {
+ return false;
+ }
+
+ // Rename the namespace in the namespace array
+ $this->namespaces[$newNs] = $this->namespaces[$oldNs];
+ unset($this->namespaces[$oldNs]);
+
+ // Rename the namespace in the database
+ $strReq = 'UPDATE ' . $this->table .
+ " SET setting_ns = '" . $this->con->escape($newNs) . "' " .
+ " WHERE setting_ns = '" . $this->con->escape($oldNs) . "' ";
+ $this->con->execute($strReq);
+ return true;
+ }
+
+ /**
+ Delete a whole namespace with all settings pertaining to it.
+
+ @param ns string Namespace name
+ @return boolean
+ */
+ public function delNamespace($ns)
+ {
+ if (!array_key_exists($ns, $this->namespaces)) {
+ return false;
+ }
+
+ // Remove the namespace from the namespace array
+ unset($this->namespaces[$ns]);
+
+ // Delete all settings from the namespace in the database
+ $strReq = 'DELETE FROM ' . $this->table .
+ " WHERE setting_ns = '" . $this->con->escape($ns) . "' ";
+ $this->con->execute($strReq);
+ return true;
+ }
+
+ /**
+ Returns full namespace with all settings pertaining to it.
+
+ @param ns string Namespace name
+ @return dcNamespace
+ */
+ public function get($ns)
+ {
+ return $this->namespaces[$ns];
+ }
+
+ /**
+ Magic __get method.
+ @copydoc ::get
+ */
+ public function __get($n)
+ {
+ if (!array_key_exists($n, $this->namespaces)) {
+ // For backward compatibility only: the developer tried to access
+ // a setting directly, without passing via a namespace.
+ $this->raiseDeprecated('old_style_get');
+ return $this->getSetting($n);
+ }
+ return $this->get($n);
+ }
+
+ /**
+ Magic __set method.
+ @copydoc ::set
+ */
+ public function __set($n, $v)
+ {
+ $this->set($n, $v);
+ }
+
+ /**
+ Returns $namespaces property content.
+
+ @return array
+ */
+ public function dumpNamespaces()
+ {
+ return $this->namespaces;
+ }
+
+ /**
+ Raises a E_USER_NOTICE errror for deprecated functions.
+ This allows the developer to know he's been using deprecated functions.
+
+ @param name string Name of the deprecated function that was called.
+ */
+ private function raiseDeprecated($name)
+ {
+ if (DC_DEBUG) {
+ $trace = debug_backtrace();
+ array_shift($trace);
+ $grand = array_shift($trace);
+ $msg = 'Deprecated function called. (';
+ $msg .= 'dcSettings::' . $name . ' was called from ' . $grand['file'] . ' [' . $grand['line'] . '])';
+ trigger_error($msg, E_USER_NOTICE);
+ }
+ }
+
+ /**
+ @deprecated Please set your settings via $core->blog->settings->{namespace}->{setting}
+
+ Sets a setting in $settings property. This sets the setting for script
+ execution time only and if setting exists.
+
+ @param n string Setting name
+ @param v mixed Setting value
+ */
+ public function set($n, $v)
+ {
+ // For backward compatibility only: the developer tried to access
+ // a setting directly, without passing via a namespace.
+ $this->raiseDeprecated('old_style_set');
+
+ if (!$this->ns) {
+ throw new Exception(__('No namespace specified'));
+ }
+
+ if (isset($this->namespaces[$this->ns]->$n)) {
+ $this->namespaces[$this->ns]->$n['value'] = $v;
+ } else {
+ $this->namespaces[$this->ns]->$n = [
+ 'ns' => $this->ns,
+ 'value' => $v,
+ 'type' => gettype($n),
+ 'label' => '',
+ 'global' => false
+ ];
+ }
+ }
+
+ /**
+ @deprecated Please access your settings via $core->blog->settings->{namespace}->...
+
+ Sets a working namespace. You should do this before accessing any setting.
+
+ @param ns string Namespace name
+ */
+ public function setNamespace($ns)
+ {
+ $this->raiseDeprecated('setNamespace');
+ if (preg_match('/^[a-zA-Z][a-zA-Z0-9]+$/', $ns)) {
+ $this->ns = $ns;
+ } else {
+ throw new Exception(sprintf(__('Invalid setting namespace: %s'), $ns));
+ }
+ }
+
+ /**
+ @deprecated Please set your settings via $core->blog->settings->{namespace}->put()
+
+ Creates or updates a setting.
+
+ $type could be 'string', 'integer', 'float', 'boolean' or null. If $type is
+ null and setting exists, it will keep current setting type.
+
+ $value_change allow you to not change setting. Useful if you need to change
+ a setting label or type and don't want to change its value.
+
+ Don't forget to set namespace before calling this method.
+
+ @param id string Setting ID
+ @param value mixed Setting value
+ @param type string Setting type
+ @param label string Setting label
+ @param value_change boolean Change setting value or not
+ @param global boolean Setting is global
+ */
+ public function put($id, $value, $type = null, $label = null, $value_change = true, $global = false)
+ {
+ $this->raiseDeprecated('put');
+ if (!$this->ns) {
+ throw new Exception(__('No namespace specified'));
+ }
+ if (!isset($this->namespaces[$this->ns])) {
+ // Create namespace if needed
+ $this->namespaces[$this->ns] = new dcNamespace($this->core, $this->blog_id, $this->ns);
+ }
+ $this->namespaces[$this->ns]->put($id, $value, $type, $label, $value_change, $global);
+ }
+
+ /**
+ @deprecated Please get your settings via $core->blog->settings->{namespace}->{setting}
+
+ Returns setting value if exists.
+
+ @param n string Setting name
+ @return mixed
+ */
+ public function getSetting($n)
+ {
+ if ($this->namespaces['system']->get($n) != null) {
+ // Give preference to system settings
+ return $this->namespaces['system']->get($n);
+ } else {
+ // Parse all the namespaces
+ foreach (array_keys($this->namespaces) as $id => $ns) {
+ if ($this->namespaces[$ns]->get($n) != null) {
+ // Return the first setting with matching name
+ return $this->namespaces[$ns]->get($n);
+ }
+ }
+ }
+
+ return;
+ }
+
+ /**
+ @deprecated Please get your settings via $core->blog->settings->{namespace}->dumpSettings
+
+ Returns all settings content.
+
+ @return array
+ */
+ public function dumpSettings()
+ {
+ // For backward compatibility only: the developer tried to access
+ // the settings directly, without passing via a namespace.
+ $this->raiseDeprecated('dumpSettings');
+
+ $settings = [];
+ // Parse all the namespaces
+ foreach (array_keys($this->namespaces) as $id => $ns) {
+ $settings = array_merge($settings, $this->namespaces[$ns]->dumpSettings());
+ }
+
+ return $settings;
+ }
+
+ /**
+ @deprecated Please get your settings via $core->blog->settings->{namespace}->dumpGlobalSettings
+
+ Returns all global settings content.
+
+ @return array
+ */
+ public function dumpGlobalSettings()
+ {
+ // For backward compatibility only: the developer tried to access
+ // the settings directly, without passing via a namespace.
+ $this->raiseDeprecated('dumpGlobalSettings');
+
+ $settings = [];
+ // Parse all the namespaces
+ foreach (array_keys($this->namespaces) as $id => $ns) {
+ $settings = array_merge($settings, $this->namespaces[$ns]->dumpGlobalSettings());
+ }
+
+ return $settings;
+ }
+
+ /**
+ Returns a list of settings matching given criteria, for any blog.
+ $params is an array taking the following
+ optionnal parameters:
+
+ - ns : retrieve setting from given namespace
+ - id : retrieve only settings corresponding to the given id
+
+ @param params array Parameters
+ @return record A record
+ */
+ public function getGlobalSettings($params = [])
+ {
+ $strReq = "SELECT * from " . $this->table . " ";
+ $where = [];
+ if (!empty($params['ns'])) {
+ $where[] = "setting_ns = '" . $this->con->escape($params['ns']) . "'";
+ }
+ if (!empty($params['id'])) {
+ $where[] = "setting_id = '" . $this->con->escape($params['id']) . "'";
+ }
+ if (isset($params['blog_id'])) {
+ if (!empty($params['blog_id'])) {
+ $where[] = "blog_id = '" . $this->con->escape($params['blog_id']) . "'";
+ } else {
+ $where[] = "blog_id IS NULL";
+ }
+ }
+ if (count($where) != 0) {
+ $strReq .= " WHERE " . join(" AND ", $where);
+ }
+ $strReq .= " ORDER by blog_id";
+ return $this->con->select($strReq);
+ }
+
+ /**
+ Updates a setting from a given record
+
+ @param rs record the setting to update
+ */
+ public function updateSetting($rs)
+ {
+ $cur = $this->con->openCursor($this->table);
+ $cur->setting_id = $rs->setting_id;
+ $cur->setting_value = $rs->setting_value;
+ $cur->setting_type = $rs->setting_type;
+ $cur->setting_label = $rs->setting_label;
+ $cur->blog_id = $rs->blog_id;
+ $cur->setting_ns = $rs->setting_ns;
+ if ($cur->blog_id == null) {
+ $where = 'WHERE blog_id IS NULL ';
+ } else {
+ $where = "WHERE blog_id = '" . $this->con->escape($cur->blog_id) . "' ";
+ }
+ $cur->update($where . "AND setting_id = '" . $this->con->escape($cur->setting_id) . "' AND setting_ns = '" . $this->con->escape($cur->setting_ns) . "' ");
+ }
+
+ /**
+ Drops a setting from a given record
+
+ @param rs record the setting to drop
+ @return int number of deleted records (0 if setting does not exist)
+ */
+ public function dropSetting($rs)
+ {
+ $strReq = "DELETE FROM " . $this->table . ' ';
+ if ($rs->blog_id == null) {
+ $strReq .= 'WHERE blog_id IS NULL ';
+ } else {
+ $strReq .= "WHERE blog_id = '" . $this->con->escape($rs->blog_id) . "' ";
+ }
+ $strReq .= "AND setting_id = '" . $this->con->escape($rs->setting_id) . "' AND setting_ns = '" . $this->con->escape($rs->setting_ns) . "' ";
+ return $this->con->execute($strReq);
+ }
+}
diff --git a/dotclear._no/inc/core/class.dc.sql.statement.php b/dotclear._no/inc/core/class.dc.sql.statement.php
new file mode 100644
index 0000000..499e90d
--- /dev/null
+++ b/dotclear._no/inc/core/class.dc.sql.statement.php
@@ -0,0 +1,926 @@
+core = &$core;
+ $this->con = &$core->con;
+ $this->ctx = $ctx;
+
+ $this->columns =
+ $this->from =
+ $this->where =
+ $this->cond =
+ $this->sql =
+ [];
+ }
+
+ /**
+ * Magic getter method
+ *
+ * @param string $property The property
+ *
+ * @return mixed property value if property exists
+ */
+ public function __get($property)
+ {
+ if (property_exists($this, $property)) {
+ return $this->$property;
+ }
+ trigger_error('Unknown property ' . $property, E_USER_ERROR);
+ return;
+ }
+
+ /**
+ * Magic setter method
+ *
+ * @param string $property The property
+ * @param mixed $value The value
+ *
+ * @return self
+ */
+ public function __set($property, $value)
+ {
+ if (property_exists($this, $property)) {
+ $this->$property = $value;
+ } else {
+ trigger_error('Unknown property ' . $property, E_USER_ERROR);
+ }
+ return $this;
+ }
+
+ /**
+ * Adds context
+ *
+ * @param mixed $c the context(s)
+ *
+ * @return dcSelectStatement self instance, enabling to chain calls
+ */
+ public function ctx($c)
+ {
+ $this->ctx = $c;
+ return $this;
+ }
+
+ /**
+ * Adds column(s)
+ *
+ * @param mixed $c the column(s)
+ * @param boolean $reset reset previous column(s) first
+ *
+ * @return dcSelectStatement self instance, enabling to chain calls
+ */
+ public function columns($c, $reset = false)
+ {
+ if ($reset) {
+ $this->columns = [];
+ }
+ if (is_array($c)) {
+ $this->columns = array_merge($this->columns, $c);
+ } else {
+ array_push($this->columns, $c);
+ }
+ return $this;
+ }
+
+ /**
+ * columns() alias
+ *
+ * @param mixed $c the column(s)
+ * @param boolean $reset reset previous column(s) first
+ *
+ * @return dcSelectStatement self instance, enabling to chain calls
+ */
+ public function column($c, $reset = false)
+ {
+ return $this->columns($c, $reset);
+ }
+
+ /**
+ * Adds FROM clause(s)
+ *
+ * @param mixed $c the from clause(s)
+ * @param boolean $reset reset previous from(s) first
+ *
+ * @return dcSelectStatement self instance, enabling to chain calls
+ */
+ public function from($c, $reset = false)
+ {
+ if ($reset) {
+ $this->from = [];
+ }
+ // Remove comma on beginning of clause(s) (legacy code)
+ if (is_array($c)) {
+ $filter = function ($v) {
+ return trim(ltrim($v, ','));
+ };
+ $c = array_map($filter, $c); // Cope with legacy code
+ $this->from = array_merge($this->from, $c);
+ } else {
+ $c = trim(ltrim($c, ',')); // Cope with legacy code
+ array_push($this->from, $c);
+ }
+ return $this;
+ }
+
+ /**
+ * Adds WHERE clause(s) condition (each will be AND combined in statement)
+ *
+ * @param mixed $c the clause(s)
+ * @param boolean $reset reset previous where(s) first
+ *
+ * @return dcSelectStatement self instance, enabling to chain calls
+ */
+ public function where($c, $reset = false)
+ {
+ if ($reset) {
+ $this->where = [];
+ }
+ if (is_array($c)) {
+ $this->where = array_merge($this->where, $c);
+ } else {
+ array_push($this->where, $c);
+ }
+ return $this;
+ }
+
+ /**
+ * Adds additional WHERE clause condition(s) (including an operator at beginning)
+ *
+ * @param mixed $c the clause(s)
+ * @param boolean $reset reset previous condition(s) first
+ *
+ * @return dcSelectStatement self instance, enabling to chain calls
+ */
+ public function cond($c, $reset = false)
+ {
+ if ($reset) {
+ $this->cond = [];
+ }
+ if (is_array($c)) {
+ $this->cond = array_merge($this->cond, $c);
+ } else {
+ array_push($this->cond, $c);
+ }
+ return $this;
+ }
+
+ /**
+ * Adds generic clause(s)
+ *
+ * @param mixed $c the clause(s)
+ * @param boolean $reset reset previous generic clause(s) first
+ *
+ * @return dcSelectStatement self instance, enabling to chain calls
+ */
+ public function sql($c, $reset = false)
+ {
+ if ($reset) {
+ $this->sql = [];
+ }
+ if (is_array($c)) {
+ $this->sql = array_merge($this->sql, $c);
+ } else {
+ array_push($this->sql, $c);
+ }
+ return $this;
+ }
+
+ // Helpers
+
+ /**
+ * Escape a value
+ *
+ * @param string $value The value
+ *
+ * @return string
+ */
+ public function escape($value)
+ {
+ return $this->con->escape($value);
+ }
+
+ /**
+ * Quote and escape a value if necessary (type string)
+ *
+ * @param mixed $value The value
+ * @param boolean $escape The escape
+ *
+ * @return string
+ */
+ public function quote($value, $escape = true)
+ {
+ return
+ (is_string($value) ? "'" : '') .
+ ($escape ? $this->con->escape($value) : $value) .
+ (is_string($value) ? "'" : '');
+ }
+
+ /**
+ * Return an SQL IN (…) fragment
+ *
+ * @param mixed $list The list
+ *
+ * @return string
+ */
+ public function in($list)
+ {
+ return $this->con->in($list);
+ }
+
+ /**
+ * Return an SQL formatted date
+ *
+ * @param string $field Field name
+ * @param string $pattern Date format
+ *
+ * @return string
+ */
+ public function dateFormat($field, $pattern)
+ {
+ return $this->con->dateFormat($field, $pattern);
+ }
+
+ /**
+ * Return an SQL formatted REGEXP clause
+ *
+ * @param string $value The value
+ *
+ * @return string
+ */
+ public function regexp($value)
+ {
+ if ($this->con->syntax() == 'mysql') {
+ $clause = "REGEXP '^" . $this->escape(preg_quote($value)) . "[0-9]+$'";
+ } elseif ($this->con->syntax() == 'postgresql') {
+ $clause = "~ '^" . $this->escape(preg_quote($value)) . "[0-9]+$'";
+ } else {
+ $clause = "LIKE '" .
+ $this->escape(preg_replace(['%', '_', '!'], ['!%', '!_', '!!'], $value)) .
+ "%' ESCAPE '!'";
+ }
+ return $clause;
+ }
+
+ /**
+ * Compare two SQL queries
+ *
+ * May be used for debugging purpose as:
+ *
+ * if (!$sql->isSame($sql->statement(), $oldRequest)) {
+ * trigger_error('SQL statement error: ' . $sql->statement() . ' / ' . $oldRequest, E_USER_ERROR);
+ * }
+ *
+ * @param string $local The local
+ * @param string $external The external
+ *
+ * @return boolean True if same, False otherwise.
+ */
+ public function isSame($local, $external)
+ {
+ $filter = function ($s) {
+ $s = strtoupper($s);
+ $patterns = [
+ '\s+' => ' ', // Multiple spaces/tabs -> one space
+ ' \)' => ')', // ) -> )
+ ' ,' => ',', // , -> ,
+ '\( ' => '(' // ( -> (
+ ];
+ foreach ($patterns as $pattern => $replace) {
+ $s = preg_replace('!' . $pattern . '!', $replace, $s);
+ }
+ return trim($s);
+ };
+ return ($filter($local) === $filter($external));
+ }
+}
+
+/**
+ * Select Statement : small utility to build select queries
+ */
+class dcSelectStatement extends dcSqlStatement
+{
+ protected $join;
+ protected $having;
+ protected $order;
+ protected $group;
+ protected $limit;
+ protected $offset;
+ protected $distinct;
+
+ /**
+ * Class constructor
+ *
+ * @param dcCore $core dcCore instance
+ * @param mixed $ctx optional context
+ */
+ public function __construct(&$core, $ctx = null)
+ {
+ $this->join =
+ $this->having =
+ $this->order =
+ $this->group =
+ [];
+
+ $this->limit = null;
+ $this->offset = null;
+ $this->distinct = false;
+
+ parent::__construct($core, $ctx);
+ }
+
+ /**
+ * Adds JOIN clause(s) (applied on first from item only)
+ *
+ * @param mixed $c the join clause(s)
+ * @param boolean $reset reset previous join(s) first
+ *
+ * @return dcSelectStatement self instance, enabling to chain calls
+ */
+ public function join($c, $reset = false)
+ {
+ if ($reset) {
+ $this->join = [];
+ }
+ if (is_array($c)) {
+ $this->join = array_merge($this->join, $c);
+ } else {
+ array_push($this->join, $c);
+ }
+ return $this;
+ }
+
+ /**
+ * Adds HAVING clause(s)
+ *
+ * @param mixed $c the clause(s)
+ * @param boolean $reset reset previous having(s) first
+ *
+ * @return dcSelectStatement self instance, enabling to chain calls
+ */
+ public function having($c, $reset = false)
+ {
+ if ($reset) {
+ $this->having = [];
+ }
+ if (is_array($c)) {
+ $this->having = array_merge($this->having, $c);
+ } else {
+ array_push($this->having, $c);
+ }
+ return $this;
+ }
+
+ /**
+ * Adds ORDER BY clause(s)
+ *
+ * @param mixed $c the clause(s)
+ * @param boolean $reset reset previous order(s) first
+ *
+ * @return dcSelectStatement self instance, enabling to chain calls
+ */
+ public function order($c, $reset = false)
+ {
+ if ($reset) {
+ $this->order = [];
+ }
+ if (is_array($c)) {
+ $this->order = array_merge($this->order, $c);
+ } else {
+ array_push($this->order, $c);
+ }
+ return $this;
+ }
+
+ /**
+ * Adds GROUP BY clause(s)
+ *
+ * @param mixed $c the clause(s)
+ * @param boolean $reset reset previous group(s) first
+ *
+ * @return dcSelectStatement self instance, enabling to chain calls
+ */
+ public function group($c, $reset = false)
+ {
+ if ($reset) {
+ $this->group = [];
+ }
+ if (is_array($c)) {
+ $this->group = array_merge($this->group, $c);
+ } else {
+ array_push($this->group, $c);
+ }
+ return $this;
+ }
+
+ /**
+ * Defines the LIMIT for select
+ *
+ * @param mixed $limit
+ * @return dcSelectStatement self instance, enabling to chain calls
+ */
+ public function limit($limit)
+ {
+ $offset = null;
+ if (is_array($limit)) {
+ // Keep only values
+ $limit = array_values($limit);
+ // If 2 values, [0] -> offset, [1] -> limit
+ // If 1 value, [0] -> limit
+ if (isset($limit[1])) {
+ $offset = $limit[0];
+ $limit = $limit[1];
+ } else {
+ $limit = limit[0];
+ }
+ }
+ $this->limit = $limit;
+ if ($offset !== null) {
+ $this->offset = $offset;
+ }
+ return $this;
+ }
+
+ /**
+ * Defines the OFFSET for select
+ *
+ * @param integer $offset
+ * @return dcSelectStatement self instance, enabling to chain calls
+ */
+ public function offset($offset)
+ {
+ $this->offset = $offset;
+ return $this;
+ }
+
+ /**
+ * Defines the DISTINCT flag for select
+ *
+ * @param boolean $distinct
+ * @return dcSelectStatement self instance, enabling to chain calls
+ */
+ public function distinct($distinct = true)
+ {
+ $this->distinct = $distinct;
+ return $this;
+ }
+
+ /**
+ * Returns the select statement
+ *
+ * @return string the statement
+ */
+ public function statement()
+ {
+ # --BEHAVIOR-- coreBeforeSelectStatement
+ $this->core->callBehavior('coreBeforeSelectStatement', $this);
+
+ // Check if source given
+ if (!count($this->from)) {
+ trigger_error(__('SQL SELECT requires a FROM source'), E_USER_ERROR);
+ return '';
+ }
+
+ // Query
+ $query = 'SELECT ' . ($this->distinct ? 'DISTINCT ' : '');
+
+ // Specific column(s) or all (*)
+ if (count($this->columns)) {
+ $query .= join(', ', $this->columns) . ' ';
+ } else {
+ $query .= '* ';
+ }
+
+ // Table(s) and Join(s)
+ $query .= 'FROM ' . $this->from[0] . ' ';
+ $query .= join(' ', $this->join) . ' ';
+ if (count($this->from) > 1) {
+ array_shift($this->from);
+ $query .= ', ' . join(', ', $this->from) . ' '; // All other from(s)
+ }
+
+ // Where clause(s)
+ if (count($this->where)) {
+ $query .= 'WHERE ' . join(' AND ', $this->where) . ' ';
+ }
+
+ // Direct where clause(s)
+ if (count($this->cond)) {
+ if (!count($this->where)) {
+ $query .= 'WHERE 1 '; // Hack to cope with the operator included in top of each condition
+ }
+ $query .= join(' ', $this->cond) . ' ';
+ }
+
+ // Generic clause(s)
+ if (count($this->sql)) {
+ $query .= join(' ', $this->sql) . ' ';
+ }
+
+ // Group by clause (columns or aliases)
+ if (count($this->group)) {
+ $query .= 'GROUP BY ' . join(', ', $this->group) . ' ';
+ }
+
+ // Having clause(s)
+ if (count($this->having)) {
+ $query .= 'HAVING ' . join(' AND ', $this->having) . ' ';
+ }
+
+ // Order by clause (columns or aliases and optionnaly order ASC/DESC)
+ if (count($this->order)) {
+ $query .= 'ORDER BY ' . join(', ', $this->order) . ' ';
+ }
+
+ // Limit clause
+ if ($this->limit !== null) {
+ $query .= 'LIMIT ' . $this->limit . ' ';
+ }
+
+ // Offset clause
+ if ($this->offset !== null) {
+ $query .= 'OFFSET ' . $this->offset . ' ';
+ }
+
+ $query = trim($query);
+
+ # --BEHAVIOR-- coreAfertSelectStatement
+ $this->core->callBehavior('coreAfterSelectStatement', $this, $query);
+
+ return $query;
+ }
+}
+
+/**
+ * Delete Statement : small utility to build delete queries
+ */
+class dcDeleteStatement extends dcSqlStatement
+{
+ /**
+ * Returns the delete statement
+ *
+ * @return string the statement
+ */
+ public function statement()
+ {
+ # --BEHAVIOR-- coreBeforeDeleteStatement
+ $this->core->callBehavior('coreBeforeDeleteStatement', $this);
+
+ // Check if source given
+ if (!count($this->from)) {
+ trigger_error(__('SQL DELETE requires a FROM source'), E_USER_ERROR);
+ return '';
+ }
+
+ // Query
+ $query = 'DELETE ';
+
+ // Table
+ $query .= 'FROM ' . $this->from[0] . ' ';
+
+ // Where clause(s)
+ if (count($this->where)) {
+ $query .= 'WHERE ' . join(' AND ', $this->where) . ' ';
+ }
+
+ // Direct where clause(s)
+ if (count($this->cond)) {
+ if (!count($this->where)) {
+ $query .= 'WHERE 1 '; // Hack to cope with the operator included in top of each condition
+ }
+ $query .= join(' ', $this->cond) . ' ';
+ }
+
+ // Generic clause(s)
+ if (count($this->sql)) {
+ $query .= join(' ', $this->sql) . ' ';
+ }
+
+ $query = trim($query);
+
+ # --BEHAVIOR-- coreAfertDeleteStatement
+ $this->core->callBehavior('coreAfterDeleteStatement', $this, $query);
+
+ return $query;
+ }
+}
+
+/**
+ * Update Statement : small utility to build update queries
+ */
+class dcUpdateStatement extends dcSqlStatement
+{
+ protected $set;
+
+ /**
+ * Class constructor
+ *
+ * @param dcCore $core dcCore instance
+ * @param mixed $ctx optional context
+ */
+ public function __construct(&$core, $ctx = null)
+ {
+ $this->set = [];
+
+ parent::__construct($core, $ctx);
+ }
+
+ /**
+ * from() alias
+ *
+ * @param mixed $c the reference clause(s)
+ * @param boolean $reset reset previous reference first
+ *
+ * @return dcUpdateStatement self instance, enabling to chain calls
+ */
+ public function reference($c, $reset = false)
+ {
+ return $this->from($c, $reset);
+ }
+
+ /**
+ * from() alias
+ *
+ * @param mixed $c the reference clause(s)
+ * @param boolean $reset reset previous reference first
+ *
+ * @return dcUpdateStatement self instance, enabling to chain calls
+ */
+ public function ref($c, $reset = false)
+ {
+ return $this->reference($c, $reset);
+ }
+
+ /**
+ * Adds update value(s)
+ *
+ * @param mixed $c the udpate values(s)
+ * @param boolean $reset reset previous update value(s) first
+ *
+ * @return dcUpdateStatement self instance, enabling to chain calls
+ */
+ public function set($c, $reset = false)
+ {
+ if ($reset) {
+ $this->set = [];
+ }
+ if (is_array($c)) {
+ $this->set = array_merge($this->set, $c);
+ } else {
+ array_push($this->set, $c);
+ }
+ return $this;
+ }
+
+ /**
+ * set() alias
+ *
+ * @param mixed $c the update value(s)
+ * @param boolean $reset reset previous update value(s) first
+ *
+ * @return dcUpdateStatement self instance, enabling to chain calls
+ */
+ public function sets($c, $reset = false)
+ {
+ return $this->set($c, $reset);
+ }
+
+ /**
+ * Returns the WHERE part of update statement
+ *
+ * Useful to construct the where clause used with cursor->update() method
+ */
+ public function whereStatement()
+ {
+ # --BEHAVIOR-- coreBeforeUpdateWhereStatement
+ $this->core->callBehavior('coreBeforeUpdateWhereStatement', $this);
+
+ $query = '';
+
+ // Where clause(s)
+ if (count($this->where)) {
+ $query .= 'WHERE ' . join(' AND ', $this->where) . ' ';
+ }
+
+ // Direct where clause(s)
+ if (count($this->cond)) {
+ if (!count($this->where)) {
+ $query .= 'WHERE 1 '; // Hack to cope with the operator included in top of each condition
+ }
+ $query .= join(' ', $this->cond) . ' ';
+ }
+
+ // Generic clause(s)
+ if (count($this->sql)) {
+ $query .= join(' ', $this->sql) . ' ';
+ }
+
+ $query = trim($query);
+
+ # --BEHAVIOR-- coreAfertUpdateWhereStatement
+ $this->core->callBehavior('coreAfterUpdateWhereStatement', $this, $query);
+
+ return $query;
+ }
+
+ /**
+ * Returns the update statement
+ *
+ * @return string the statement
+ */
+ public function statement()
+ {
+ # --BEHAVIOR-- coreBeforeUpdateStatement
+ $this->core->callBehavior('coreBeforeUpdateStatement', $this);
+
+ // Check if source given
+ if (!count($this->from)) {
+ trigger_error(__('SQL UPDATE requires an INTO source'), E_USER_ERROR);
+ return '';
+ }
+
+ // Query
+ $query = 'UPDATE ';
+
+ // Reference
+ $query .= $this->from[0] . ' ';
+
+ // Value(s)
+ if (count($this->set)) {
+ $query .= 'SET ' . join(', ', $this->set) . ' ';
+ }
+
+ // Where clause(s)
+ if (count($this->where)) {
+ $query .= 'WHERE ' . join(' AND ', $this->where) . ' ';
+ }
+
+ // Direct where clause(s)
+ if (count($this->cond)) {
+ if (!count($this->where)) {
+ $query .= 'WHERE 1 '; // Hack to cope with the operator included in top of each condition
+ }
+ $query .= join(' ', $this->cond) . ' ';
+ }
+
+ // Generic clause(s)
+ if (count($this->sql)) {
+ $query .= join(' ', $this->sql) . ' ';
+ }
+
+ $query = trim($query);
+
+ # --BEHAVIOR-- coreAfertUpdateStatement
+ $this->core->callBehavior('coreAfterUpdateStatement', $this, $query);
+
+ return $query;
+ }
+}
+
+/**
+ * Insert Statement : small utility to build insert queries
+ */
+class dcInsertStatement extends dcSqlStatement
+{
+ protected $lines;
+
+ /**
+ * Class constructor
+ *
+ * @param dcCore $core dcCore instance
+ * @param mixed $ctx optional context
+ */
+ public function __construct(&$core, $ctx = null)
+ {
+ $this->lines = [];
+
+ parent::__construct($core, $ctx);
+ }
+
+ /**
+ * from() alias
+ *
+ * @param mixed $c the into clause(s)
+ * @param boolean $reset reset previous into first
+ *
+ * @return dcSelectStatement self instance, enabling to chain calls
+ */
+ public function into($c, $reset = false)
+ {
+ return $this->into($c, $reset);
+ }
+
+ /**
+ * Adds update value(s)
+ *
+ * @param mixed $c the insert values(s)
+ * @param boolean $reset reset previous insert value(s) first
+ *
+ * @return dcSelectStatement self instance, enabling to chain calls
+ */
+ public function lines($c, $reset = false)
+ {
+ if ($reset) {
+ $this->lines = [];
+ }
+ if (is_array($c)) {
+ $this->lines = array_merge($this->lines, $c);
+ } else {
+ array_push($this->lines, $c);
+ }
+ return $this;
+ }
+
+ /**
+ * line() alias
+ *
+ * @param mixed $c the insert value(s)
+ * @param boolean $reset reset previous insert value(s) first
+ *
+ * @return dcInsertStatement self instance, enabling to chain calls
+ */
+ public function line($c, $reset = false)
+ {
+ return $this->lines($c, $reset);
+ }
+
+ /**
+ * Returns the insert statement
+ *
+ * @return string the statement
+ */
+ public function statement()
+ {
+ # --BEHAVIOR-- coreBeforeInsertStatement
+ $this->core->callBehavior('coreBeforeInsertStatement', $this);
+
+ // Check if source given
+ if (!count($this->from)) {
+ trigger_error(__('SQL INSERT requires an INTO source'), E_USER_ERROR);
+ return '';
+ }
+
+ // Query
+ $query = 'INSERT ';
+
+ // Reference
+ $query .= 'INTO ' . $this->from[0] . ' ';
+
+ // Column(s)
+ if (count($this->columns)) {
+ $query .= '(' . join(', ', $this->columns) . ') ';
+ }
+
+ // Value(s)
+ $query .= 'VALUES ';
+ if (count($this->lines)) {
+ $raws = [];
+ foreach ($this->lines as $line) {
+ $raws[] = '(' . join(', ', $line) . ')';
+ }
+ $query .= join(', ', $raws);
+ } else {
+ // Use SQL default values (useful only if SQL strict mode is off or if every columns has a defined default value)
+ $query .= '()';
+ }
+
+ $query = trim($query);
+
+ # --BEHAVIOR-- coreAfertInsertStatement
+ $this->core->callBehavior('coreAfterInsertStatement', $this, $query);
+
+ return $query;
+ }
+}
diff --git a/dotclear._no/inc/core/class.dc.store.parser.php b/dotclear._no/inc/core/class.dc.store.parser.php
new file mode 100644
index 0000000..f59d79a
--- /dev/null
+++ b/dotclear._no/inc/core/class.dc.store.parser.php
@@ -0,0 +1,103 @@
+xml = simplexml_load_string($data);
+ $this->items = [];
+
+ if ($this->xml === false) {
+ throw new Exception(__('Wrong data feed'));
+ }
+
+ $this->_parse();
+
+ unset($data);
+ unset($this->xml);
+ }
+
+ /**
+ * Parse XML into array
+ */
+ protected function _parse()
+ {
+ if (empty($this->xml->module)) {
+ return;
+ }
+
+ foreach ($this->xml->module as $i) {
+ $attrs = $i->attributes();
+
+ $item = [];
+
+ # DC/DA shared markers
+ $item['id'] = (string) $attrs['id'];
+ $item['file'] = (string) $i->file;
+ $item['label'] = (string) $i->name; // deprecated
+ $item['name'] = (string) $i->name;
+ $item['version'] = (string) $i->version;
+ $item['author'] = (string) $i->author;
+ $item['desc'] = (string) $i->desc;
+
+ # DA specific markers
+ $item['dc_min'] = (string) $i->children(self::$bloc)->dcmin;
+ $item['details'] = (string) $i->children(self::$bloc)->details;
+ $item['section'] = (string) $i->children(self::$bloc)->section;
+ $item['support'] = (string) $i->children(self::$bloc)->support;
+ $item['sshot'] = (string) $i->children(self::$bloc)->sshot;
+
+ $tags = [];
+ foreach ($i->children(self::$bloc)->tags as $t) {
+ $tags[] = (string) $t->tag;
+ }
+ $item['tags'] = implode(', ', $tags);
+
+ # First filter right now. If DC_DEV is set all modules are parse
+ if (defined('DC_DEV') && DC_DEV === true || dcUtils::versionsCompare(DC_VERSION, $item['dc_min'], '>=', false)) {
+ $this->items[$item['id']] = $item;
+ }
+ }
+ }
+
+ /**
+ * Get modules.
+ *
+ * @return array Modules list
+ */
+ public function getModules()
+ {
+ return $this->items;
+ }
+}
diff --git a/dotclear._no/inc/core/class.dc.store.php b/dotclear._no/inc/core/class.dc.store.php
new file mode 100644
index 0000000..d9fd3b0
--- /dev/null
+++ b/dotclear._no/inc/core/class.dc.store.php
@@ -0,0 +1,292 @@
+ 10, 'name' => 8, 'author' => 6, 'tags' => 4, 'desc' => 2];
+
+ /** @var string User agent used to query repository */
+ protected $user_agent = 'DotClear.org RepoBrowser/0.1';
+ /** @var string XML feed URL */
+ protected $xml_url;
+ /** @var array Array of new/update modules from repository */
+ protected $data;
+
+ /**
+ * Constructor.
+ *
+ * @param object $modules dcModules instance
+ * @param string $xml_url XML feed URL
+ * @param boolean $force Force query repository
+ */
+ public function __construct(dcModules $modules, $xml_url, $force = false)
+ {
+ $this->core = $modules->core;
+ $this->modules = $modules;
+ $this->xml_url = $xml_url;
+ $this->user_agent = sprintf('Dotclear/%s)', DC_VERSION);
+
+ $this->check($force);
+ }
+
+ /**
+ * Check repository.
+ *
+ * @param boolean $force Force query repository
+ * @return boolean True if get feed or cache
+ */
+ public function check($force = false)
+ {
+ if (!$this->xml_url) {
+ return false;
+ }
+ if (($parser = dcStoreReader::quickParse($this->xml_url, DC_TPL_CACHE, $force)) === false) {
+ return false;
+ }
+
+ $raw_datas = $parser->getModules();
+
+ uasort($raw_datas, ['self', 'sort']);
+
+ $skipped = array_keys($this->modules->getDisabledModules());
+ foreach ($skipped as $p_id) {
+ if (isset($raw_datas[$p_id])) {
+ unset($raw_datas[$p_id]);
+ }
+ }
+
+ $updates = [];
+ $current = $this->modules->getModules();
+ foreach ($current as $p_id => $p_infos) {
+ if (isset($raw_datas[$p_id])) {
+ if (dcUtils::versionsCompare($raw_datas[$p_id]['version'], $p_infos['version'], '>')) {
+ $updates[$p_id] = $raw_datas[$p_id];
+ $updates[$p_id]['root'] = $p_infos['root'];
+ $updates[$p_id]['root_writable'] = $p_infos['root_writable'];
+ $updates[$p_id]['current_version'] = $p_infos['version'];
+ }
+ unset($raw_datas[$p_id]);
+ }
+ }
+
+ $this->data = [
+ 'new' => $raw_datas,
+ 'update' => $updates
+ ];
+
+ return true;
+ }
+
+ /**
+ * Get a list of modules.
+ *
+ * @param boolean $update True to get update modules, false for new ones
+ * @return array List of update/new modules
+ */
+ public function get($update = false)
+ {
+ return $this->data[$update ? 'update' : 'new'];
+ }
+
+ /**
+ * Search a module.
+ *
+ * Search string is cleaned, split and compare to split:
+ * - module id and clean id,
+ * - module name, clean name,
+ * - module desccription.
+ *
+ * Every time a part of query is find on module,
+ * result accuracy grow. Result is sorted by accuracy.
+ *
+ * @param string $pattern String to search
+ * @return array Match modules
+ */
+ public function search($pattern)
+ {
+ $result = [];
+
+ # Split query into small clean words
+ if (!($patterns = self::patternize($pattern))) {
+ return $result;
+ }
+
+ # For each modules
+ foreach ($this->data['new'] as $id => $module) {
+ $module['id'] = $id;
+
+ # Loop through required module fields
+ foreach (self::$weighting as $field => $weight) {
+
+ # Skip fields which not exsist on module
+ if (empty($module[$field])) {
+ continue;
+ }
+
+ # Split field value into small clean word
+ if (!($subjects = self::patternize($module[$field]))) {
+ continue;
+ }
+
+ # Check contents
+ if (!($nb = preg_match_all('/(' . implode('|', $patterns) . ')/', implode(' ', $subjects), $_))) {
+ continue;
+ }
+
+ # Add module to result
+ if (!isset($sorter[$id])) {
+ $sorter[$id] = 0;
+ $result[$id] = $module;
+ }
+
+ # Increment score by matches count * field weight
+ $sorter[$id] += $nb * $weight;
+ $result[$id]['score'] = $sorter[$id];
+ }
+ }
+ # Sort response by matches count
+ if (!empty($result)) {
+ array_multisort($sorter, SORT_DESC, $result);
+ }
+ return $result;
+ }
+
+ /**
+ * Quick download and install module.
+ *
+ * @param string $url Module package URL
+ * @param string $dest Path to install module
+ * @return integer 1 = installed, 2 = update
+ */
+ public function process($url, $dest)
+ {
+ $this->download($url, $dest);
+ return $this->install($dest);
+ }
+
+ /**
+ * Download a module.
+ *
+ * @param string $url Module package URL
+ * @param string $dest Path to put module package
+ */
+ public function download($url, $dest)
+ {
+ // Check and add default protocol if necessary
+ if (!preg_match('%^http[s]?:\/\/%', $url)) {
+ $url = 'http://' . $url;
+ }
+ // Download package
+ if ($client = netHttp::initClient($url, $path)) {
+ try {
+ $client->setUserAgent($this->user_agent);
+ $client->useGzip(false);
+ $client->setPersistReferers(false);
+ $client->setOutput($dest);
+ $client->get($path);
+ unset($client);
+ } catch (Exception $e) {
+ unset($client);
+ throw new Exception(__('An error occurred while downloading the file.'));
+ }
+ } else {
+ throw new Exception(__('An error occurred while downloading the file.'));
+ }
+ }
+
+ /**
+ * Install a previously downloaded module.
+ *
+ * @param string $path Module package URL
+ * @param string $path Path to module package
+ * @return integer 1 = installed, 2 = update
+ */
+ public function install($path)
+ {
+ return dcModules::installPackage($path, $this->modules);
+ }
+
+ /**
+ * User Agent String.
+ *
+ * @param string $str User agent string
+ */
+ public function agent($str)
+ {
+ $this->user_agent = $str;
+ }
+
+ /**
+ * Split and clean pattern.
+ *
+ * @param string $str String to sanitize
+ * @return array Array of cleaned pieces of string or false if none
+ */
+ public static function patternize($str)
+ {
+ $arr = [];
+
+ foreach (explode(' ', $str) as $_) {
+ $_ = strtolower(preg_replace('/[^A-Za-z0-9]/', '', $_));
+ if (strlen($_) > 2) {
+ $arr[] = $_;
+ }
+ }
+
+ return empty($arr) ? false : $arr;
+ }
+
+ /**
+ * Compare version.
+ *
+ * @param string $v1 Version
+ * @param string $v2 Version
+ * @param string $op Comparison operator
+ * @return boolean True is comparison is true, dude!
+ */
+ private static function compare($v1, $v2, $op)
+ {
+ return version_compare(
+ preg_replace('!-r(\d+)$!', '-p$1', $v1),
+ preg_replace('!-r(\d+)$!', '-p$1', $v2),
+ $op
+ );
+ }
+
+ /**
+ * Sort modules list.
+ *
+ * @param array $a A module
+ * @param array $b A module
+ * @return integer
+ */
+ private static function sort($a, $b)
+ {
+ $c = strtolower($a['id']);
+ $d = strtolower($b['id']);
+ if ($c == $d) {
+ return 0;
+ }
+ return ($c < $d) ? -1 : 1;
+ }
+}
diff --git a/dotclear._no/inc/core/class.dc.store.reader.php b/dotclear._no/inc/core/class.dc.store.reader.php
new file mode 100644
index 0000000..c660030
--- /dev/null
+++ b/dotclear._no/inc/core/class.dc.store.reader.php
@@ -0,0 +1,264 @@
+setUserAgent(sprintf('Dotclear/%s)', DC_VERSION));
+ }
+
+ /**
+ * Parse modules feed.
+ *
+ * @param string $url XML feed URL
+ * @return object dcStore instance
+ */
+ public function parse($url)
+ {
+ $this->validators = [];
+
+ if ($this->cache_dir) {
+ return $this->withCache($url);
+ } elseif (!$this->getModulesXML($url) || $this->getStatus() != '200') {
+ return false;
+ }
+
+ return new dcStoreParser($this->getContent());
+ }
+
+ /**
+ * Quick parse modules feed.
+ *
+ * @param string $url XML feed URL
+ * @param string $cache_dir Cache directoy or null for no cache
+ * @param boolean $force Force query repository
+ * @return object Self instance
+ */
+ public static function quickParse($url, $cache_dir = null, $force = false)
+ {
+ $parser = new self();
+ if ($cache_dir) {
+ $parser->setCacheDir($cache_dir);
+ }
+ if ($force) {
+ $parser->setForce($force);
+ }
+
+ return $parser->parse($url);
+ }
+
+ /**
+ * Set cache directory.
+ *
+ * @param string $dir Cache directory
+ * @return boolean True if cache dierctory is useable
+ */
+ public function setCacheDir($dir)
+ {
+ $this->cache_dir = null;
+
+ if (!empty($dir) && is_dir($dir) && is_writeable($dir)) {
+ $this->cache_dir = $dir;
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Set cache TTL.
+ *
+ * @param string $str Cache TTL
+ */
+ public function setCacheTTL($str)
+ {
+ $str = trim($str);
+
+ if (!empty($str)) {
+ $this->cache_ttl = substr($str, 0, 1) == '-' ? $str : '-' . $str;
+ }
+ }
+
+ /**
+ * Set force query repository.
+ *
+ * @param boolean $force True to force query
+ */
+ public function setForce($force)
+ {
+ $this->force = $force;
+ }
+
+ /**
+ * Get repository XML feed URL content.
+ *
+ * @param string $url XML feed URL
+ * @return string Feed content
+ */
+ protected function getModulesXML($url)
+ {
+ if (!self::readURL($url, $ssl, $host, $port, $path, $user, $pass)) {
+ return false;
+ }
+ $this->setHost($host, $port);
+ $this->useSSL($ssl);
+ $this->setAuthorization($user, $pass);
+
+ try {
+ return $this->get($path);
+ } catch (Exception $e) {
+ // @todo Log error when repository query fail
+ return false;
+ }
+ }
+
+ /**
+ * Get repository modules list using cache.
+ *
+ * @param string $url XML feed URL
+ * @return array Feed content or False on fail
+ */
+ protected function withCache($url)
+ {
+ $url_md5 = md5($url);
+ $cached_file = sprintf('%s/%s/%s/%s/%s.ser',
+ $this->cache_dir,
+ $this->cache_file_prefix,
+ substr($url_md5, 0, 2),
+ substr($url_md5, 2, 2),
+ $url_md5
+ );
+
+ $may_use_cached = false;
+
+ # Use cache file ?
+ if (@file_exists($cached_file) && !$this->force) {
+ $may_use_cached = true;
+ $ts = @filemtime($cached_file);
+ if ($ts > strtotime($this->cache_ttl)) {
+ # Direct cache
+ return unserialize(file_get_contents($cached_file));
+ }
+ $this->setValidator('IfModifiedSince', $ts);
+ }
+
+ # Query repository
+ if (!$this->getModulesXML($url)) {
+ if ($may_use_cached) {
+ # Touch cache TTL even if query failed ?
+ if ($this->cache_touch_on_fail) {
+ @files::touch($cached_file);
+ }
+ # Connection failed - fetched from cache
+ return unserialize(file_get_contents($cached_file));
+ }
+ return false;
+ }
+
+ # Parse response
+ switch ($this->getStatus()) {
+ # Not modified, use cache
+ case '304':
+ @files::touch($cached_file);
+ return unserialize(file_get_contents($cached_file));
+ # Ok, parse feed
+ case '200':
+ if ($modules = new dcStoreParser($this->getContent())) {
+ try {
+ files::makeDir(dirname($cached_file), true);
+ } catch (Exception $e) {
+ return $modules;
+ }
+
+ if (($fp = @fopen($cached_file, 'wb'))) {
+ fwrite($fp, serialize($modules));
+ fclose($fp);
+ files::inheritChmod($cached_file);
+ }
+ return $modules;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Prepare query.
+ *
+ * @return array Query headers
+ */
+ protected function buildRequest()
+ {
+ $headers = parent::buildRequest();
+
+ # Cache validators
+ if (!empty($this->validators)) {
+ if (isset($this->validators['IfModifiedSince'])) {
+ $headers[] = 'If-Modified-Since: ' . $this->validators['IfModifiedSince'];
+ }
+ if (isset($this->validators['IfNoneMatch'])) {
+ if (is_array($this->validators['IfNoneMatch'])) {
+ $etags = implode(',', $this->validators['IfNoneMatch']);
+ } else {
+ $etags = $this->validators['IfNoneMatch'];
+ }
+ $headers[] = '';
+ }
+ }
+
+ return $headers;
+ }
+
+ /**
+ * Tweak query cache validator.
+ *
+ * @param string $key Validator key
+ * @param string $value Validator value
+ */
+ private function setValidator($key, $value)
+ {
+ if ($key == 'IfModifiedSince') {
+ $value = gmdate('D, d M Y H:i:s', $value) . ' GMT';
+ }
+
+ $this->validators[$key] = $value;
+ }
+}
diff --git a/dotclear._no/inc/core/class.dc.themes.php b/dotclear._no/inc/core/class.dc.themes.php
new file mode 100644
index 0000000..6ba16e6
--- /dev/null
+++ b/dotclear._no/inc/core/class.dc.themes.php
@@ -0,0 +1,86 @@
+$parent is a optional value to indicate them inheritance.
+ If $parent is null / not set, we simply fall back to
+ the standard behavior, by using 'default'.
+
+ $priority is an integer. Modules are sorted by priority and name.
+ Lowest priority comes first. This property is currently ignored when dealing
+ with themes.
+
+ @param name string Module name
+ @param desc string Module description
+ @param author string Module author name
+ @param version string Module version
+ @param properties array extra properties
+ (currently available keys : parent, priority, standalone_config, type, tplset)
+ */
+ public function registerModule($name, $desc, $author, $version, $properties = [])
+ {
+ # Fallback to legacy registerModule parameters
+ if (!is_array($properties)) {
+ $args = func_get_args();
+ $properties = [];
+ if (isset($args[4])) {
+ $properties['parent'] = $args[4];
+ }
+ if (isset($args[5])) {
+ $properties['priority'] = (integer) $args[5];
+ }
+ }
+ # Themes specifics properties
+ $properties = array_merge(
+ ['parent' => null, 'tplset' => DC_DEFAULT_TPLSET],
+ $properties,
+ ['permissions' => 'admin'] // force themes perms
+ );
+
+ parent::registerModule($name, $desc, $author, $version, $properties);
+ }
+
+ /**
+ Loads namespace $ns specific file for module with ID
+ $id
+ Note : actually, only 'public' namespace is supported with themes.
+
+ @param id string Module ID
+ @param ns string Namespace name
+ */
+ public function loadNsFile($id, $ns = null)
+ {
+ switch ($ns) {
+ case 'public':
+ $parent = $this->modules[$id]['parent'];
+ if ($parent) {
+ // This is not a real cascade - since we don't call loadNsFile -,
+ // thus limiting inclusion process.
+ // TODO : See if we have to change this.
+ $this->loadModuleFile($this->modules[$parent]['root'] . '/_public.php');
+ }
+ $this->loadModuleFile($this->modules[$id]['root'] . '/_public.php');
+ break;
+ }
+ }
+}
diff --git a/dotclear._no/inc/core/class.dc.trackback.php b/dotclear._no/inc/core/class.dc.trackback.php
new file mode 100644
index 0000000..38c8ca3
--- /dev/null
+++ b/dotclear._no/inc/core/class.dc.trackback.php
@@ -0,0 +1,816 @@
+dcCore dcCore instance
+ public $table; ///< string done pings table name
+
+ /**
+ Object constructor
+
+ @param core dcCore dcCore instance
+ */
+ public function __construct($core)
+ {
+ $this->core = &$core;
+ $this->con = &$this->core->con;
+ $this->table = $this->core->prefix . 'ping';
+ }
+
+ /// @name Send
+ //@{
+ /**
+ Get all pings sent for a given post.
+
+ @param post_id integer Post ID
+ @return record
+ */
+ public function getPostPings($post_id)
+ {
+ $strReq = 'SELECT ping_url, ping_dt ' .
+ 'FROM ' . $this->table . ' ' .
+ 'WHERE post_id = ' . (integer) $post_id;
+
+ return $this->con->select($strReq);
+ }
+
+ /**
+ Sends a ping to given $url .
+
+ @param url string URL to ping
+ @param post_id integer Post ID
+ @param post_title string Post title
+ @param post_excerpt string Post excerpt
+ @param post_url string Post URL
+ */
+ public function ping($url, $post_id, $post_title, $post_excerpt, $post_url)
+ {
+ if ($this->core->blog === null) {
+ return false;
+ }
+
+ $post_id = (integer) $post_id;
+
+ # Check for previously done trackback
+ $strReq = 'SELECT post_id, ping_url FROM ' . $this->table . ' ' .
+ 'WHERE post_id = ' . $post_id . ' ' .
+ "AND ping_url = '" . $this->con->escape($url) . "' ";
+
+ $rs = $this->con->select($strReq);
+
+ if (!$rs->isEmpty()) {
+ throw new Exception(sprintf(__('%s has still been pinged'), $url));
+ }
+
+ $ping_parts = explode('|', $url);
+ # Maybe a webmention
+ if (count($ping_parts) == 3) {
+ $payload = http_build_query([
+ 'source' => $post_url,
+ 'target' => $ping_parts[1]
+ ]);
+
+ try {
+ $http = self::initHttp($ping_parts[0], $path);
+ $http->setMoreHeader('Content-Type: application/x-www-form-urlencoded');
+ $http->post($path, $payload, 'UTF-8');
+
+ # Read response status
+ $status = $http->getStatus();
+ $ping_error = '0';
+ } catch (Exception $e) {
+ throw new Exception(__('Unable to ping URL'));
+ }
+
+ if (!in_array($status, ['200', '201', '202'])) {
+ $ping_error = $http->getStatus();
+ $ping_msg = __('Bad server response code');
+ }
+ }
+ # No, let's walk by the trackback way
+ elseif (count($ping_parts) < 2) {
+ $data = [
+ 'title' => $post_title,
+ 'excerpt' => $post_excerpt,
+ 'url' => $post_url,
+ 'blog_name' => trim(html::escapeHTML(html::clean($this->core->blog->name)))
+ //,'__debug' => false
+ ];
+
+ # Ping
+ try {
+ $http = self::initHttp($url, $path);
+ $http->post($path, $data, 'UTF-8');
+ $res = $http->getContent();
+ } catch (Exception $e) {
+ throw new Exception(__('Unable to ping URL'));
+ }
+
+ $pattern =
+ '|.*(.*) (.*)' .
+ '((.*) (.*))?' .
+ ' |msU';
+
+ if (!preg_match($pattern, $res, $match)) {
+ throw new Exception(sprintf(__('%s is not a ping URL'), $url));
+ }
+
+ $ping_error = trim($match[1]);
+ $ping_msg = (!empty($match[4])) ? $match[4] : '';
+ }
+ # Damnit ! Let's play pingback
+ else {
+ try {
+ $xmlrpc = new xmlrpcClient($ping_parts[0]);
+ $res = $xmlrpc->query('pingback.ping', $post_url, $ping_parts[1]);
+ $ping_error = '0';
+ } catch (xmlrpcException $e) {
+ $ping_error = $e->getCode();
+ $ping_msg = $e->getMessage();
+ } catch (Exception $e) {
+ throw new Exception(__('Unable to ping URL'));
+ }
+ }
+
+ if ($ping_error != '0') {
+ throw new Exception(sprintf(__('%s, ping error:'), $url) . ' ' . $ping_msg);
+ } else {
+ # Notify ping result in database
+ $cur = $this->con->openCursor($this->table);
+ $cur->post_id = $post_id;
+ $cur->ping_url = $url;
+ $cur->ping_dt = date('Y-m-d H:i:s');
+
+ $cur->insert();
+ }
+ }
+ //@}
+
+ /// @name Receive
+ //@{
+ /**
+ Receives a trackback and insert it as a comment of given post.
+
+ @param post_id integer Post ID
+ */
+ public function receiveTrackback($post_id)
+ {
+ header('Content-Type: text/xml; charset=UTF-8');
+ if (empty($_POST)) {
+ http::head(405, 'Method Not Allowed');
+ echo
+ '' . "\n" .
+ "\n" .
+ " 1 \n" .
+ " POST request needed \n" .
+ " ";
+ return;
+ }
+
+ $post_id = (integer) $post_id;
+
+ $title = !empty($_POST['title']) ? $_POST['title'] : '';
+ $excerpt = !empty($_POST['excerpt']) ? $_POST['excerpt'] : '';
+ $url = !empty($_POST['url']) ? $_POST['url'] : '';
+ $blog_name = !empty($_POST['blog_name']) ? $_POST['blog_name'] : '';
+ $charset = '';
+ $comment = '';
+
+ $err = false;
+ $msg = '';
+
+ if ($this->core->blog === null) {
+ $err = true;
+ $msg = 'No blog.';
+ } elseif ($url == '') {
+ $err = true;
+ $msg = 'URL parameter is required.';
+ } elseif ($blog_name == '') {
+ $err = true;
+ $msg = 'Blog name is required.';
+ }
+
+ if (!$err) {
+ $post = $this->core->blog->getPosts(['post_id' => $post_id, 'post_type' => '']);
+
+ if ($post->isEmpty()) {
+ $err = true;
+ $msg = 'No such post.';
+ } elseif (!$post->trackbacksActive()) {
+ $err = true;
+ $msg = 'Trackbacks are not allowed for this post or weblog.';
+ }
+
+ $url = trim(html::clean($url));
+ if ($this->pingAlreadyDone($post->post_id, $url)) {
+ $err = true;
+ $msg = 'The trackback has already been registered';
+ }
+ }
+
+ if (!$err) {
+ $charset = self::getCharsetFromRequest();
+
+ if (!$charset) {
+ $charset = self::detectCharset($title . ' ' . $excerpt . ' ' . $blog_name);
+ }
+
+ if (strtolower($charset) != 'utf-8') {
+ $title = iconv($charset, 'UTF-8', $title);
+ $excerpt = iconv($charset, 'UTF-8', $excerpt);
+ $blog_name = iconv($charset, 'UTF-8', $blog_name);
+ }
+
+ $title = trim(html::clean($title));
+ $title = html::decodeEntities($title);
+ $title = html::escapeHTML($title);
+ $title = text::cutString($title, 60);
+
+ $excerpt = trim(html::clean($excerpt));
+ $excerpt = html::decodeEntities($excerpt);
+ $excerpt = preg_replace('/\s+/ms', ' ', $excerpt);
+ $excerpt = text::cutString($excerpt, 252);
+ $excerpt = html::escapeHTML($excerpt) . '...';
+
+ $blog_name = trim(html::clean($blog_name));
+ $blog_name = html::decodeEntities($blog_name);
+ $blog_name = html::escapeHTML($blog_name);
+ $blog_name = text::cutString($blog_name, 60);
+
+ try {
+ $this->addBacklink($post_id, $url, $blog_name, $title, $excerpt, $comment);
+ } catch (Exception $e) {
+ $err = 1;
+ $msg = 'Something went wrong : ' . $e->getMessage();
+ }
+ }
+
+ $resp =
+ '' . "\n" .
+ "\n" .
+ ' ' . (integer) $err . " \n";
+
+ if ($msg) {
+ $resp .= ' ' . $msg . " \n";
+ }
+
+ if (!empty($_POST['__debug'])) {
+ $resp .=
+ " \n" .
+ ' ' . $title . " \n" .
+ ' ' . $excerpt . " \n" .
+ ' ' . $url . " \n" .
+ ' ' . $blog_name . " \n" .
+ ' ' . $charset . " \n" .
+ ' ' . $comment . " \n" .
+ " \n";
+ }
+
+ echo $resp . " ";
+ }
+
+ /**
+ Receives a pingback and insert it as a comment of given post.
+
+ @param from_url string Source URL
+ @param to_url string Target URL
+ */
+ public function receivePingback($from_url, $to_url)
+ {
+ try {
+ $posts = $this->getTargetPost($to_url);
+
+ if ($this->pingAlreadyDone($posts->post_id, $from_url)) {
+ throw new Exception(__('Don\'t repeat yourself, please.'), 48);
+ }
+
+ $remote_content = $this->getRemoteContent($from_url);
+
+ # We want a title...
+ if (!preg_match('!([^<].*?) !mis', $remote_content, $m)) {
+ throw new Exception(__('Where\'s your title?'), 0);
+ }
+ $title = trim(html::clean($m[1]));
+ $title = html::decodeEntities($title);
+ $title = html::escapeHTML($title);
+ $title = text::cutString($title, 60);
+
+ preg_match('!]*?>(.*)?!msi', $remote_content, $m);
+ $source = $m[1];
+ $source = preg_replace('![\r\n\s]+!ms', ' ', $source);
+ $source = preg_replace("/<\/*(h\d|p|th|td|li|dt|dd|pre|caption|input|textarea|button)[^>]*>/", "\n\n", $source);
+ $source = strip_tags($source, '');
+ $source = explode("\n\n", $source);
+
+ $excerpt = '';
+ foreach ($source as $line) {
+ if (strpos($line, $to_url) !== false) {
+ if (preg_match("! ]+?" . $to_url . "[^>]*>([^>]+?) !", $line, $m)) {
+ $excerpt = strip_tags($line);
+ break;
+ }
+ }
+ }
+ if ($excerpt) {
+ $excerpt = '(…) ' . text::cutString(html::escapeHTML($excerpt), 200) . ' (…)';
+ } else {
+ $excerpt = '(…)';
+ }
+
+ $this->addBacklink($posts->post_id, $from_url, '', $title, $excerpt, $comment);
+ } catch (Exception $e) {
+ throw new Exception(__('Sorry, an internal problem has occured.'), 0);
+ }
+
+ return __('Thanks, mate. It was a pleasure.');
+ }
+
+ /**
+ Receives a webmention and insert it as a comment of given post.
+
+ NB: plugin Fair Trackback check source content to find url.
+
+ @return null Null on success, else throw an exception
+ */
+ public function receiveWebmention()
+ {
+ $err = $post_id = false;
+ header('Content-Type: text/html; charset=UTF-8');
+
+ try {
+ # Check if post and target are valid URL
+ if (empty($_POST['source']) || empty($_POST['target'])) {
+ throw new Exception('Source or target is not valid', 0);
+ }
+
+ $from_url = urldecode($_POST['source']);
+ $to_url = urldecode($_POST['target']);
+
+ self::checkURLs($from_url, $to_url);
+
+ # Try to find post
+ $posts = $this->getTargetPost($to_url);
+ $post_id = $posts->post_id;
+
+ # Check if it's an updated mention
+ if ($this->pingAlreadyDone($post_id, $from_url)) {
+ $this->delBacklink($post_id, $from_url);
+ }
+
+ # Create a comment for received webmention
+ $remote_content = $this->getRemoteContent($from_url);
+
+ # We want a title...
+ if (!preg_match('!([^<].*?) !mis', $remote_content, $m)) {
+ throw new Exception(__('Where\'s your title?'), 0);
+ }
+ $title = trim(html::clean($m[1]));
+ $title = html::decodeEntities($title);
+ $title = html::escapeHTML($title);
+ $title = text::cutString($title, 60);
+
+ preg_match('!]*?>(.*)?!msi', $remote_content, $m);
+ $source = $m[1];
+ $source = preg_replace('![\r\n\s]+!ms', ' ', $source);
+ $source = preg_replace("/<\/*(h\d|p|th|td|li|dt|dd|pre|caption|input|textarea|button)[^>]*>/", "\n\n", $source);
+ $source = strip_tags($source, '');
+ $source = explode("\n\n", $source);
+
+ $excerpt = '';
+ foreach ($source as $line) {
+ if (strpos($line, $to_url) !== false) {
+ if (preg_match("! ]+?" . $to_url . "[^>]*>([^>]+?) !", $line, $m)) {
+ $excerpt = strip_tags($line);
+ break;
+ }
+ }
+ }
+ if ($excerpt) {
+ $excerpt = '(…) ' . text::cutString(html::escapeHTML($excerpt), 200) . ' (…)';
+ } else {
+ $excerpt = '(…)';
+ }
+
+ $this->addBacklink($post_id, $from_url, '', $title, $excerpt, $comment);
+
+ # All done, thanks
+ $code = $this->core->blog->settings->system->trackbacks_pub ? 200 : 202;
+ http::head($code);
+ return;
+ } catch (Exception $e) {
+ $err = $e->getMessage();
+ }
+
+ http::head(400);
+ echo $err ?: 'Something went wrong.';
+ return;
+ }
+
+ /**
+ Check if a post previously received a ping a from an URL.
+
+ @param post_id integer Post ID
+ @param from_url string Source URL
+ @return boolean
+ */
+ private function pingAlreadyDone($post_id, $from_url)
+ {
+ $params = [
+ 'post_id' => $post_id,
+ 'comment_site' => $from_url,
+ 'comment_trackback' => 1
+ ];
+
+ $rs = $this->core->blog->getComments($params, true);
+ if ($rs && !$rs->isEmpty()) {
+ return ($rs->f(0));
+ }
+
+ return false;
+ }
+
+ /**
+ Create a comment marked as trackback for a given post.
+
+ @param post_id integer Post ID
+ @param url string Discovered URL
+ @param blog name string Source blog name
+ @param title string Comment title
+ @param excerpt string Source excerpt
+ @param comment string Comment content
+ */
+ private function addBacklink($post_id, $url, $blog_name, $title, $excerpt, &$comment)
+ {
+ if (empty($blog_name)) {
+ // Let use title as text link for this backlink
+ $blog_name = ($title ?: 'Anonymous blog');
+ }
+
+ $comment =
+ "\n" .
+ ' ' . ($title ?: $blog_name) . "
\n" .
+ '' . $excerpt . '
';
+
+ $cur = $this->core->con->openCursor($this->core->prefix . 'comment');
+ $cur->comment_author = (string) $blog_name;
+ $cur->comment_site = (string) $url;
+ $cur->comment_content = (string) $comment;
+ $cur->post_id = $post_id;
+ $cur->comment_trackback = 1;
+ $cur->comment_status = $this->core->blog->settings->system->trackbacks_pub ? 1 : -1;
+ $cur->comment_ip = http::realIP();
+
+ # --BEHAVIOR-- publicBeforeTrackbackCreate
+ $this->core->callBehavior('publicBeforeTrackbackCreate', $cur);
+ if ($cur->post_id) {
+ $comment_id = $this->core->blog->addComment($cur);
+
+ # --BEHAVIOR-- publicAfterTrackbackCreate
+ $this->core->callBehavior('publicAfterTrackbackCreate', $cur, $comment_id);
+ }
+ }
+
+ /**
+ Delete previously received comment made from an URL for a given post.
+
+ @param post_id integer Post ID
+ @param url string Source URL
+ */
+ private function delBacklink($post_id, $url)
+ {
+ $this->con->execute(
+ 'DELETE FROM ' . $this->core->prefix . 'comment ' .
+ 'WHERE post_id = ' . ((integer) $post_id) . ' ' .
+ "AND comment_site = '" . $this->core->con->escape((string) $url) . "' " .
+ 'AND comment_trackback = 1 '
+ );
+ }
+
+ /**
+ Find Charset from HTTP headers.
+
+ @param header string Source header
+ @return string
+ */
+ private static function getCharsetFromRequest($header = '')
+ {
+ if (!$header && isset($_SERVER['CONTENT_TYPE'])) {
+ $header = $_SERVER['CONTENT_TYPE'];
+ }
+
+ if ($header) {
+ if (preg_match('|charset=([a-zA-Z0-9-]+)|', $header, $m)) {
+ return $m[1];
+ }
+ }
+
+ return;
+ }
+
+ /**
+ Detect encoding.
+
+ @param content string Source URL
+ @return string
+ */
+ private static function detectCharset($content)
+ {
+ return mb_detect_encoding($content,
+ 'UTF-8,ISO-8859-1,ISO-8859-2,ISO-8859-3,' .
+ 'ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,' .
+ 'ISO-8859-9,ISO-8859-10,ISO-8859-13,ISO-8859-14,ISO-8859-15');
+ }
+
+ /**
+ Retreive local post from a given URL
+
+ @param to_url string Target URL
+ @return string
+ */
+ private function getTargetPost($to_url)
+ {
+ $reg = '!^' . preg_quote($this->core->blog->url) . '(.*)!';
+ $type = $args = $next = '';
+
+ # Are you dumb?
+ if (!preg_match($reg, $to_url, $m)) {
+ throw new Exception(__('Any chance you ping one of my contents? No? Really?'), 0);
+ }
+
+ # Does the targeted URL look like a registered post type?
+ $url_part = $m[1];
+ $p_type = '';
+ $post_types = $this->core->getPostTypes();
+ foreach ($post_types as $k => $v) {
+ $reg = '!^' . preg_quote(str_replace('%s', '', $v['public_url'])) . '(.*)!';
+ if (preg_match($reg, $url_part, $n)) {
+ $p_type = $k;
+ $post_url = $n[1];
+ break;
+ }
+ }
+
+ if (empty($p_type)) {
+ throw new Exception(__('Sorry but you can not ping this type of content.'), 33);
+ }
+
+ # Time to see if we've got a winner...
+ $params = [
+ 'post_type' => $p_type,
+ 'post_url' => $post_url
+ ];
+ $posts = $this->core->blog->getPosts($params);
+
+ # Missed!
+ if ($posts->isEmpty()) {
+ throw new Exception(__('Oops. Kinda "not found" stuff. Please check the target URL twice.'), 33);
+ }
+
+ # Nice try. But, sorry, no.
+ if (!$posts->trackbacksActive()) {
+ throw new Exception(__('Sorry, dude. This entry does not accept pingback at the moment.'), 33);
+ }
+
+ return $posts;
+ }
+
+ /**
+ Returns content of a distant page
+
+ @param from_url string Target URL
+ @return string
+ */
+ private function getRemoteContent($from_url)
+ {
+ $http = self::initHttp($from_url, $from_path);
+
+ # First round : just to be sure the ping comes from an acceptable resource type.
+ $http->setHeadersOnly(true);
+ $http->get($from_path);
+ $c_type = explode(';', $http->getHeader('content-type'));
+
+ # Bad luck. Bye, bye...
+ if (!in_array($c_type[0], ['text/html', 'application/xhtml+xml'])) {
+ throw new Exception(__('Your source URL does not look like a supported content type. Sorry. Bye, bye!'), 0);
+ }
+
+ # Second round : let's go fetch and parse the remote content
+ $http->setHeadersOnly(false);
+ $http->get($from_path);
+ $remote_content = $http->getContent();
+
+ # Convert content charset
+ $charset = self::getCharsetFromRequest($http->getHeader('content-type'));
+ if (!$charset) {
+ $charset = self::detectCharset($remote_content);
+ }
+ if (strtolower($charset) != 'utf-8') {
+ $remote_content = iconv($charset, 'UTF-8', $remote_content);
+ }
+
+ return $remote_content;
+ }
+ //@}
+
+ /// @name Discover
+ //@{
+ /**
+ Returns an array containing all discovered trackbacks URLs in
+ $text .
+
+ @param text string Input text
+ @return array
+ */
+ public function discover($text)
+ {
+ $res = [];
+
+ foreach ($this->getTextLinks($text) as $link) {
+ if (($url = $this->getPingURL($link)) !== null) {
+ $res[] = $url;
+ }
+ }
+
+ return $res;
+ }
+
+ /**
+ Find links into a text.
+
+ @param text string Text to scan
+ @return array
+ */
+ private function getTextLinks($text)
+ {
+ $res = [];
+
+ # href attribute on "a" tags
+ if (preg_match_all('/]+)>/ms', $text, $match, PREG_SET_ORDER)) {
+ for ($i = 0; $i < count($match); $i++) {
+ if (preg_match('/href="((https?:\/)?\/[^"]+)"/ms', $match[$i][1], $matches)) {
+ $res[$matches[1]] = 1;
+ }
+ }
+ }
+ unset($match);
+
+ # cite attributes on "blockquote" and "q" tags
+ if (preg_match_all('/<(blockquote|q) ([^>]+)>/ms', $text, $match, PREG_SET_ORDER)) {
+ for ($i = 0; $i < count($match); $i++) {
+ if (preg_match('/cite="((https?:\/)?\/[^"]+)"/ms', $match[$i][2], $matches)) {
+ $res[$matches[1]] = 1;
+ }
+ }
+ }
+
+ return array_keys($res);
+ }
+
+ /**
+ Check remote header/content to find api trace.
+
+ @param url string URL to scan
+ @return string
+ */
+ private function getPingURL($url)
+ {
+ if (strpos($url, '/') === 0) {
+ $url = http::getHost() . $url;
+ }
+
+ try {
+ $http = self::initHttp($url, $path);
+ $http->get($path);
+ $page_content = $http->getContent();
+ $pb_url = $http->getHeader('x-pingback');
+ $wm_url = $http->getHeader('link');
+ } catch (Exception $e) {
+ return false;
+ }
+
+ # Let's check for an elderly trackback data chunk...
+ $pattern_rdf =
+ '/.*?' .
+ ' ' .
+ '.*?<\/rdf:RDF>' .
+ '/msi';
+
+ preg_match_all($pattern_rdf, $page_content, $rdf_all, PREG_SET_ORDER);
+
+ $url_path = parse_url($url, PHP_URL_PATH);
+ $sanitized_url = str_replace($url_path, html::sanitizeURL($url_path), $url);
+
+ for ($i = 0; $i < count($rdf_all); $i++) {
+ $rdf = $rdf_all[$i][1];
+ if (preg_match('/dc:identifier="' . preg_quote($url, '/') . '"/msi', $rdf) ||
+ preg_match('/dc:identifier="' . preg_quote($sanitized_url, '/') . '"/msi', $rdf)) {
+ if (preg_match('/trackback:ping="(.*?)"/msi', $rdf, $tb_link)) {
+ return $tb_link[1];
+ }
+ }
+ }
+
+ # No trackback ? OK, let see if we've got a X-Pingback header and it's a valid URL, it will be enough
+ if ($pb_url && filter_var($pb_url, FILTER_VALIDATE_URL) && preg_match('!^https?:!', $pb_url)) {
+ return $pb_url . '|' . $url;
+ }
+
+ # No X-Pingback header. A link rel=pingback, maybe ?
+ $pattern_pingback = '! !msi';
+
+ if (preg_match($pattern_pingback, $page_content, $m)) {
+ $pb_url = $m[1];
+ if (filter_var($pb_url, FILTER_VALIDATE_URL) && preg_match('!^https?:!', $pb_url)) {
+ return $pb_url . '|' . $url;
+ }
+ }
+
+ # Nothing, let's try webmention. Only support x/html content
+ if ($wm_url) {
+ $type = explode(';', $http->getHeader('content-type'));
+ if (!in_array($type[0], ['text/html', 'application/xhtml+xml'])) {
+ $wm_url = false;
+ }
+ }
+
+ # Check HTTP headers for a Link: ; rel="webmention"
+ $wm_api = false;
+ if ($wm_url) {
+ if (preg_match('~<((?:https?://)?[^>]+)>; rel="?(?:https?://webmention.org/?|webmention)"?~', $wm_url, $match)) {
+ if (filter_var($match[1], FILTER_VALIDATE_URL) && preg_match('!^https?:!', $match[1])) {
+ $wm_api = $match[1];
+ }
+ }
+ }
+
+ # Else check content for
+ if ($wm_url && !$wm_api) {
+ $content = preg_replace('//Us', '', $page_content);
+ if (preg_match('/<(?:link|a)[ ]+href="([^"]*)"[ ]+rel="[^" ]* ?webmention ?[^" ]*"[ ]*\/?>/i', $content, $match)
+ || preg_match('/<(?:link|a)[ ]+rel="[^" ]* ?webmention ?[^" ]*"[ ]+href="([^"]*)"[ ]*\/?>/i', $content, $match)) {
+ $wm_api = $match[1];
+ }
+ }
+
+ # We have a winner, let's add some tricks to make diference
+ if ($wm_api) {
+ return $wm_api . '|' . $url . '|webmention';
+ }
+
+ return;
+ }
+ //@}
+
+ /**
+ HTTP helper.
+
+ @param url string URL
+ @param path string Path
+ @return object
+ */
+ private static function initHttp($url, &$path)
+ {
+ $client = netHttp::initClient($url, $path);
+ $client->setTimeout(5);
+ $client->setUserAgent('Dotclear - https://dotclear.org/');
+ $client->useGzip(false);
+ $client->setPersistReferers(false);
+
+ return $client;
+ }
+
+ /**
+ URL helper.
+
+ @param from_url string URL a
+ @param to_url string URL b
+ */
+ public static function checkURLs($from_url, $to_url)
+ {
+ if (!(filter_var($from_url, FILTER_VALIDATE_URL) && preg_match('!^https?://!', $from_url))) {
+ throw new Exception(__('No valid source URL provided? Try again!'), 0);
+ }
+
+ if (!(filter_var($to_url, FILTER_VALIDATE_URL) && preg_match('!^https?://!', $to_url))) {
+ throw new Exception(__('No valid target URL provided? Try again!'), 0);
+ }
+
+ if (html::sanitizeURL(urldecode($from_url)) == html::sanitizeURL(urldecode($to_url))) {
+ throw new Exception(__('LOL!'), 0);
+ }
+ }
+}
diff --git a/dotclear._no/inc/core/class.dc.update.php b/dotclear._no/inc/core/class.dc.update.php
new file mode 100644
index 0000000..e93014c
--- /dev/null
+++ b/dotclear._no/inc/core/class.dc.update.php
@@ -0,0 +1,514 @@
+ null,
+ 'href' => null,
+ 'checksum' => null,
+ 'info' => null,
+ 'php' => '5.6',
+ 'notify' => true
+ ];
+
+ protected $cache_ttl = '-6 hours';
+ protected $forced_files = [];
+
+ /**
+ * Constructor
+ *
+ * @param url string Versions file URL
+ * @param subject string Subject to check
+ * @param version string Version type
+ * @param cache_dir string Directory cache path
+ */
+ public function __construct($url, $subject, $version, $cache_dir)
+ {
+ $this->url = $url;
+ $this->subject = $subject;
+ $this->version = $version;
+ $this->cache_file = $cache_dir . '/' . $subject . '-' . $version;
+ }
+
+ /**
+ * Checks for Dotclear updates.
+ * Returns latest version if available or false.
+ *
+ * @param version string Current version to compare
+ * @param nocache boolean Force checking
+ * @return string Latest version if available
+ */
+ public function check($version, $nocache = false)
+ {
+ $this->getVersionInfo($nocache);
+ $v = $this->getVersion();
+ if ($v && version_compare($version, $v, '<')) {
+ return $v;
+ }
+
+ return false;
+ }
+
+ public function getVersionInfo($nocache = false)
+ {
+ # Check cached file
+ if (is_readable($this->cache_file) && filemtime($this->cache_file) > strtotime($this->cache_ttl) && !$nocache) {
+ $c = @file_get_contents($this->cache_file);
+ $c = @unserialize($c);
+ if (is_array($c)) {
+ $this->version_info = $c;
+ return;
+ }
+ }
+
+ $cache_dir = dirname($this->cache_file);
+ $can_write = (!is_dir($cache_dir) && is_writable(dirname($cache_dir)))
+ || (!file_exists($this->cache_file) && is_writable($cache_dir))
+ || is_writable($this->cache_file);
+
+ # If we can't write file, don't bug host with queries
+ if (!$can_write) {
+ return;
+ }
+
+ if (!is_dir($cache_dir)) {
+ try {
+ files::makeDir($cache_dir);
+ } catch (Exception $e) {
+ return;
+ }
+ }
+
+ # Try to get latest version number
+ try
+ {
+ $path = '';
+ $status = 0;
+
+ $http_get = function ($http_url) use (&$status, $path) {
+ $client = netHttp::initClient($http_url, $path);
+ if ($client !== false) {
+ $client->setTimeout(4);
+ $client->setUserAgent($_SERVER['HTTP_USER_AGENT']);
+ $client->get($path);
+ $status = (int) $client->getStatus();
+ }
+ return $client;
+ };
+
+ $client = $http_get($this->url);
+ if ($status >= 400) {
+ // If original URL uses HTTPS, try with HTTP
+ $url_parts = parse_url($client->getRequestURL());
+ if (isset($url_parts['scheme']) && $url_parts['scheme'] == 'https') {
+ // Replace https by http in url
+ $this->url = preg_replace('/^https(?=:\/\/)/i', 'http', $this->url);
+ $client = $http_get($this->url);
+ }
+ }
+ if (!$status || $status >= 400) {
+ throw new Exception();
+ }
+ $this->readVersion($client->getContent());
+ } catch (Exception $e) {
+ return;
+ }
+
+ # Create cache
+ file_put_contents($this->cache_file, serialize($this->version_info));
+ }
+
+ public function getVersion()
+ {
+ return $this->version_info['version'];
+ }
+
+ public function getFileURL()
+ {
+ return $this->version_info['href'];
+ }
+
+ public function getInfoURL()
+ {
+ return $this->version_info['info'];
+ }
+
+ public function getChecksum()
+ {
+ return $this->version_info['checksum'];
+ }
+
+ public function getPHPVersion()
+ {
+ return $this->version_info['php'];
+ }
+
+ public function getNotify()
+ {
+ return $this->version_info['notify'];
+ }
+
+ public function getForcedFiles()
+ {
+ return $this->forced_files;
+ }
+
+ public function setForcedFiles(...$args)
+ {
+ $this->forced_files = $args;
+ }
+
+ /**
+ * Sets notification flag.
+ */
+ public function setNotify($n)
+ {
+
+ if (!is_writable($this->cache_file)) {
+ return;
+ }
+
+ $this->version_info['notify'] = (boolean) $n;
+ file_put_contents($this->cache_file, serialize($this->version_info));
+ }
+
+ public function checkIntegrity($digests_file, $root)
+ {
+ if (!$digests_file) {
+ throw new Exception(__('Digests file not found.'));
+ }
+
+ $changes = $this->md5sum($root, $digests_file);
+
+ if (!empty($changes)) {
+ $e = new Exception('Some files have changed.', self::ERR_FILES_CHANGED);
+ $e->bad_files = $changes;
+ throw $e;
+ }
+
+ return true;
+ }
+
+ /**
+ * Downloads new version to destination $dest.
+ */
+ public function download($dest)
+ {
+ $url = $this->getFileURL();
+
+ if (!$url) {
+ throw new Exception(__('No file to download'));
+ }
+
+ if (!is_writable(dirname($dest))) {
+ throw new Exception(__('Root directory is not writable.'));
+ }
+
+ try
+ {
+ $path = '';
+ $status = 0;
+
+ $http_get = function ($http_url) use (&$status, $dest, $path) {
+ $client = netHttp::initClient($http_url, $path);
+ if ($client !== false) {
+ $client->setTimeout(4);
+ $client->setUserAgent($_SERVER['HTTP_USER_AGENT']);
+ $client->useGzip(false);
+ $client->setPersistReferers(false);
+ $client->setOutput($dest);
+ $client->get($path);
+ $status = (int) $client->getStatus();
+ }
+ return $client;
+ };
+
+ $client = $http_get($url);
+ if ($status >= 400) {
+ // If original URL uses HTTPS, try with HTTP
+ $url_parts = parse_url($client->getRequestURL());
+ if (isset($url_parts['scheme']) && $url_parts['scheme'] == 'https') {
+ // Replace https by http in url
+ $url = preg_replace('/^https(?=:\/\/)/i', 'http', $url);
+ $client = $http_get($url);
+ }
+ }
+ if ($status != 200) {
+ @unlink($dest);
+ throw new Exception();
+ }
+ } catch (Exception $e) {
+ throw new Exception(__('An error occurred while downloading archive.'));
+ }
+ }
+
+ /**
+ * Checks if archive was successfully downloaded.
+ */
+ public function checkDownload($zip)
+ {
+ $cs = $this->getChecksum();
+
+ return $cs && is_readable($zip) && md5_file($zip) == $cs;
+ }
+
+ /**
+ * Backups changed files before an update.
+ */
+ public function backup($zip_file, $zip_digests, $root, $root_digests, $dest)
+ {
+ if (!is_readable($zip_file)) {
+ throw new Exception(__('Archive not found.'));
+ }
+
+ if (!is_readable($root_digests)) {
+ @unlink($zip_file);
+ throw new Exception(__('Unable to read current digests file.'));
+ }
+
+ # Stop everything if a backup already exists and can not be overrided
+ if (!is_writable(dirname($dest)) && !file_exists($dest)) {
+ throw new Exception(__('Root directory is not writable.'));
+ }
+
+ if (file_exists($dest) && !is_writable($dest)) {
+ return false;
+ }
+
+ $b_fp = @fopen($dest, 'wb');
+ if ($b_fp === false) {
+ return false;
+ }
+
+ $zip = new fileUnzip($zip_file);
+ $b_zip = new fileZip($b_fp);
+
+ if (!$zip->hasFile($zip_digests)) {
+ @unlink($zip_file);
+ throw new Exception(__('Downloaded file does not seem to be a valid archive.'));
+ }
+
+ $opts = FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES;
+ $cur_digests = file($root_digests, $opts);
+ $new_digests = explode("\n", $zip->unzip($zip_digests));
+ $new_files = $this->getNewFiles($cur_digests, $new_digests);
+ $zip->close();
+ unset($opts, $cur_digests, $new_digests, $zip);
+
+ $not_readable = [];
+
+ if (!empty($this->forced_files)) {
+ $new_files = array_merge($new_files, $this->forced_files);
+ }
+
+ foreach ($new_files as $file) {
+ if (!$file || !file_exists($root . '/' . $file)) {
+ continue;
+ }
+
+ try {
+ $b_zip->addFile($root . '/' . $file, $file);
+ } catch (Exception $e) {
+ $not_readable[] = $file;
+ }
+ }
+
+ # If only one file is not readable, stop everything now
+ if (!empty($not_readable)) {
+ $e = new Exception('Some files are not readable.', self::ERR_FILES_UNREADABLE);
+ $e->bad_files = $not_readable;
+ throw $e;
+ }
+
+ $b_zip->write();
+ fclose($b_fp);
+ $b_zip->close();
+
+ return true;
+ }
+
+ /**
+ * Upgrade process.
+ */
+ public function performUpgrade($zip_file, $zip_digests, $zip_root, $root, $root_digests)
+ {
+ if (!is_readable($zip_file)) {
+ throw new Exception(__('Archive not found.'));
+ }
+
+ if (!is_readable($root_digests)) {
+ @unlink($zip_file);
+ throw new Exception(__('Unable to read current digests file.'));
+ }
+
+ $zip = new fileUnzip($zip_file);
+
+ if (!$zip->hasFile($zip_digests)) {
+ @unlink($zip_file);
+ throw new Exception(__('Downloaded file does not seem to be a valid archive.'));
+ }
+
+ $opts = FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES;
+ $cur_digests = file($root_digests, $opts);
+ $new_digests = explode("\n", $zip->unzip($zip_digests));
+ $new_files = self::getNewFiles($cur_digests, $new_digests);
+
+ if (!empty($this->forced_files)) {
+ $new_files = array_merge($new_files, $this->forced_files);
+ }
+
+ $zip_files = [];
+ $not_writable = [];
+
+ foreach ($new_files as $file) {
+ if (!$file) {
+ continue;
+ }
+
+ if (!$zip->hasFile($zip_root . '/' . $file)) {
+ @unlink($zip_file);
+ throw new Exception(__('Incomplete archive.'));
+ }
+
+ $dest = $dest_dir = $root . '/' . $file;
+ while (!is_dir($dest_dir = dirname($dest_dir)));
+
+ if ((file_exists($dest) && !is_writable($dest)) ||
+ (!file_exists($dest) && !is_writable($dest_dir))) {
+ $not_writable[] = $file;
+ continue;
+ }
+
+ $zip_files[] = $file;
+ }
+
+ # If only one file is not writable, stop everything now
+ if (!empty($not_writable)) {
+ $e = new Exception('Some files are not writable', self::ERR_FILES_UNWRITALBE);
+ $e->bad_files = $not_writable;
+ throw $e;
+ }
+
+ # Everything's fine, we can write files, then do it now
+ $can_touch = function_exists('touch');
+ foreach ($zip_files as $file) {
+ $zip->unzip($zip_root . '/' . $file, $root . '/' . $file);
+ if ($can_touch) {
+ @touch($root . '/' . $file);
+ }
+ }
+ @unlink($zip_file);
+ }
+
+ protected function getNewFiles($cur_digests, $new_digests)
+ {
+ $cur_md5 = $cur_path = $cur_digests;
+ $new_md5 = $new_path = $new_digests;
+
+ array_walk($cur_md5, [$this, 'parseLine'], 1);
+ array_walk($cur_path, [$this, 'parseLine'], 2);
+ array_walk($new_md5, [$this, 'parseLine'], 1);
+ array_walk($new_path, [$this, 'parseLine'], 2);
+
+ $cur = array_combine($cur_md5, $cur_path);
+ $new = array_combine($new_md5, $new_path);
+
+ return array_values(array_diff_key($new, $cur));
+ }
+
+ protected function readVersion($str)
+ {
+ try
+ {
+ $xml = new SimpleXMLElement($str, LIBXML_NOERROR);
+ $r = $xml->xpath("/versions/subject[@name='" . $this->subject . "']/release[@name='" . $this->version . "']");
+
+ if (!empty($r) && is_array($r)) {
+ $r = $r[0];
+ $this->version_info['version'] = isset($r['version']) ? (string) $r['version'] : null;
+ $this->version_info['href'] = isset($r['href']) ? (string) $r['href'] : null;
+ $this->version_info['checksum'] = isset($r['checksum']) ? (string) $r['checksum'] : null;
+ $this->version_info['info'] = isset($r['info']) ? (string) $r['info'] : null;
+ $this->version_info['php'] = isset($r['php']) ? (string) $r['php'] : null;
+ }
+ } catch (Exception $e) {
+ throw $e;
+ }
+ }
+
+ protected function md5sum($root, $digests_file)
+ {
+ if (!is_readable($digests_file)) {
+ throw new Exception(__('Unable to read digests file.'));
+ }
+
+ $opts = FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES;
+ $contents = file($digests_file, $opts);
+
+ $changes = [];
+
+ foreach ($contents as $digest) {
+ if (!preg_match('#^([\da-f]{32})\s+(.+?)$#', $digest, $m)) {
+ continue;
+ }
+
+ $md5 = $m[1];
+ $filename = $root . '/' . $m[2];
+
+ # Invalid checksum
+ if (!is_readable($filename) || !self::md5_check($filename, $md5)) {
+ $changes[] = substr($m[2], 2);
+ }
+ }
+
+ # No checksum found in digests file
+ if (empty($md5)) {
+ throw new Exception(__('Invalid digests file.'));
+ }
+
+ return $changes;
+ }
+
+ protected function parseLine(&$v, $k, $n)
+ {
+ if (!preg_match('#^([\da-f]{32})\s+(.+?)$#', $v, $m)) {
+ return;
+ }
+
+ $v = $n == 1 ? md5($m[2] . $m[1]) : substr($m[2], 2);
+ }
+
+ protected static function md5_check($filename, $md5)
+ {
+ if (md5_file($filename) == $md5) {
+ return true;
+ } else {
+ $filecontent = file_get_contents($filename);
+ $filecontent = str_replace("\r\n", "\n", $filecontent);
+ $filecontent = str_replace("\r", "\n", $filecontent);
+ if (md5($filecontent) == $md5) {
+ return true;
+ }
+
+ }
+ return false;
+ }
+}
diff --git a/dotclear._no/inc/core/class.dc.utils.php b/dotclear._no/inc/core/class.dc.utils.php
new file mode 100644
index 0000000..e45d38c
--- /dev/null
+++ b/dotclear._no/inc/core/class.dc.utils.php
@@ -0,0 +1,613 @@
+user_id, user_name , user_firstname and
+ user_displayname .
+
+ @param user_id string User ID
+ @param user_name string User's name
+ @param user_firstname string User's first name
+ @param user_displayname string User's display name
+ @return string
+ */
+ public static function getUserCN($user_id, $user_name, $user_firstname, $user_displayname)
+ {
+ if (!empty($user_displayname)) {
+ return $user_displayname;
+ }
+
+ if (!empty($user_name)) {
+ if (!empty($user_firstname)) {
+ return $user_firstname . ' ' . $user_name;
+ } else {
+ return $user_name;
+ }
+ } elseif (!empty($user_firstname)) {
+ return $user_firstname;
+ }
+
+ return $user_id;
+ }
+
+ /**
+ Cleanup a list of IDs
+
+ @param ids mixed ID(s)
+ @return array
+ */
+ public static function cleanIds($ids)
+ {
+ $clean_ids = [];
+
+ if (!is_array($ids)) {
+ $ids = [$ids];
+ }
+
+ foreach ($ids as $id) {
+ $id = abs((integer) $id);
+
+ if (!empty($id)) {
+ $clean_ids[] = $id;
+ }
+ }
+ return $clean_ids;
+ }
+
+ /**
+ * Compare two versions with option of using only main numbers.
+ *
+ * @param string $current_version Current version
+ * @param string $required_version Required version
+ * @param string $operator Comparison operand
+ * @param boolean $strict Use full version
+ * @return boolean True if comparison success
+ */
+ public static function versionsCompare($current_version, $required_version, $operator = '>=', $strict = true)
+ {
+ if ($strict) {
+ $current_version = preg_replace('!-r(\d+)$!', '-p$1', $current_version);
+ $required_version = preg_replace('!-r(\d+)$!', '-p$1', $required_version);
+ } else {
+ $current_version = preg_replace('/^([0-9\.]+)(.*?)$/', '$1', $current_version);
+ $required_version = preg_replace('/^([0-9\.]+)(.*?)$/', '$1', $required_version);
+ }
+
+ return (boolean) version_compare($current_version, $required_version, $operator);
+ }
+
+ private static function appendVersion($src, $v = '')
+ {
+ $src .= (strpos($src, '?') === false ? '?' : '&') . 'v=';
+ if (defined('DC_DEV') && DC_DEV === true) {
+ $src .= md5(uniqid());
+ } else {
+ $src .= ($v === '' ? DC_VERSION : $v);
+ }
+ return $src;
+ }
+
+ public static function cssLoad($src, $media = 'screen', $v = null)
+ {
+ $escaped_src = html::escapeHTML($src);
+ if ($v !== null) {
+ $escaped_src = dcUtils::appendVersion($escaped_src, $v);
+ }
+ return ' ' . "\n";
+ }
+
+ public static function jsLoad($src, $v = null)
+ {
+ $escaped_src = html::escapeHTML($src);
+ if ($v !== null) {
+ $escaped_src = dcUtils::appendVersion($escaped_src, $v);
+ }
+ return '' . "\n";
+ }
+
+ /**
+ * return a list of javascript variables définitions code
+ *
+ * @deprecated 2.15 use dcUtils::jsJson() and getData()/mergeDeep() in javascript
+ *
+ * @param array $vars The variables
+ *
+ * @return string javascript code (inside )
+ */
+ public static function jsVars($vars)
+ {
+ $ret = '\n";
+
+ return $ret;
+ }
+
+ /**
+ * return a javascript variable definition line code
+ *
+ * @deprecated 2.15 use dcUtils::jsJson() and getData()/mergeDeep() in javascript
+ *
+ * @param string $n variable name
+ * @param various $v value
+ *
+ * @return string javascript code
+ */
+ public static function jsVar($n, $v)
+ {
+ return dcUtils::jsVars([$n => $v]);
+ }
+
+ public static function jsJson($id, $vars)
+ {
+ // Use echo dcUtils::jsLoad($core->blog->getPF('util.js')); to use the JS getData() decoder in public mode
+ $ret = '';
+ return $ret;
+ }
+
+ /**
+ * Locale specific array sorting function
+ *
+ * @param array $arr single array of strings
+ * @param string $ns admin/public/lang
+ * @param string $lang language to be used if $ns = 'lang'
+ */
+ public static function lexicalSort(&$arr, $ns = '', $lang = 'en_US')
+ {
+ if ($ns != '') {
+ dcUtils::setLexicalLang($ns, $lang);
+ }
+ return usort($arr, ['dcUtils', 'lexicalSortHelper']);
+ }
+
+ /**
+ * Locale specific array sorting function (preserving keys)
+ *
+ * @param array $arr single array of strings
+ * @param string $ns admin/public/lang
+ * @param string $lang language to be used if $ns = 'lang'
+ */
+ public static function lexicalArraySort(&$arr, $ns = '', $lang = 'en_US')
+ {
+ if ($ns != '') {
+ dcUtils::setLexicalLang($ns, $lang);
+ }
+ return uasort($arr, ['dcUtils', 'lexicalSortHelper']);
+ }
+
+ /**
+ * Locale specific array sorting function (sorting keys)
+ *
+ * @param array $arr single array of strings
+ * @param string $ns admin/public/lang
+ * @param string $lang language to be used if $ns = 'lang'
+ */
+ public static function lexicalKeySort(&$arr, $ns = '', $lang = 'en_US')
+ {
+ if ($ns != '') {
+ dcUtils::setLexicalLang($ns, $lang);
+ }
+ return uksort($arr, ['dcUtils', 'lexicalSortHelper']);
+ }
+
+ public static function setLexicalLang($ns = '', $lang = 'en_US')
+ {
+ global $core;
+
+ // Switch to appropriate locale depending on $ns
+ switch ($ns) {
+ case 'admin':
+ // Set locale with user prefs
+ $user_language = $core->auth->getInfo('user_lang');
+ setlocale(LC_COLLATE, $user_language);
+ break;
+ case 'public':
+ // Set locale with blog params
+ $blog_language = $core->blog->settings->system->lang;
+ setlocale(LC_COLLATE, $blog_language);
+ break;
+ case 'lang':
+ // Set locale with arg
+ setlocale(LC_COLLATE, $lang);
+ break;
+ }
+ }
+
+ private static function lexicalSortHelper($a, $b)
+ {
+ return strcoll(strtolower(dcUtils::removeDiacritics($a)), strtolower(dcUtils::removeDiacritics($b)));
+ }
+
+ // removeDiacritics function (see https://github.com/infralabs/DiacriticsRemovePHP)
+
+ protected static $defaultDiacriticsRemovalMap = [
+ [
+ 'base' => "A",
+ 'letters' => '/(A|Ⓐ|A|À|Á|Â|Ầ|Ấ|Ẫ|Ẩ|Ã|Ā|Ă|Ằ|Ắ|Ẵ|Ẳ|Ȧ|Ǡ|Ä|Ǟ|Ả|Å|Ǻ|Ǎ|Ȁ|Ȃ|Ạ|Ậ|Ặ|Ḁ|Ą|Ⱥ|Ɐ|[\x{0041}\x{24B6}\x{FF21}\x{00C0}\x{00C1}\x{00C2}\x{1EA6}\x{1EA4}\x{1EAA}\x{1EA8}\x{00C3}\x{0100}\x{0102}\x{1EB0}\x{1EAE}\x{1EB4}\x{1EB2}\x{0226}\x{01E0}\x{00C4}\x{01DE}\x{1EA2}\x{00C5}\x{01FA}\x{01CD}\x{0200}\x{0202}\x{1EA0}\x{1EAC}\x{1EB6}\x{1E00}\x{0104}\x{023A}\x{2C6F}])/'
+ ],
+ [
+ 'base' => "AA",
+ 'letters' => '/(Ꜳ|[\x{A732}])/'
+ ],
+ [
+ 'base' => "AE",
+ 'letters' => '/(Æ|Ǽ|Ǣ|[\x{00C6}\x{01FC}\x{01E2}])/'
+ ],
+ [
+ 'base' => "AO",
+ 'letters' => '/(Ꜵ|[\x{A734}])/'
+ ],
+ [
+ 'base' => "AU",
+ 'letters' => '/(Ꜷ|[\x{A736}])/'
+ ],
+ [
+ 'base' => "AV",
+ 'letters' => '/(Ꜹ|Ꜻ|[\x{A738}\x{A73A}])/'
+ ],
+ [
+ 'base' => "AY",
+ 'letters' => '/(Ꜽ|[\x{A73C}])/'
+ ],
+ [
+ 'base' => "B",
+ 'letters' => '/(B|Ⓑ|B|Ḃ|Ḅ|Ḇ|Ƀ|Ƃ|Ɓ|[\x{0042}\x{24B7}\x{FF22}\x{1E02}\x{1E04}\x{1E06}\x{0243}\x{0182}\x{0181}])/'
+ ],
+ [
+ 'base' => "C",
+ 'letters' => '/(C|Ⓒ|C|Ć|Ĉ|Ċ|Č|Ç|Ḉ|Ƈ|Ȼ|Ꜿ|[\x{0043}\x{24B8}\x{FF23}\x{0106}\x{0108}\x{010A}\x{010C}\x{00C7}\x{1E08}\x{0187}\x{023B}\x{A73E}])/'
+ ],
+ [
+ 'base' => "D",
+ 'letters' => '/(D|Ⓓ|D|Ḋ|Ď|Ḍ|Ḑ|Ḓ|Ḏ|Đ|Ƌ|Ɗ|Ɖ|Ꝺ|Ð|[\x{0044}\x{24B9}\x{FF24}\x{1E0A}\x{010E}\x{1E0C}\x{1E10}\x{1E12}\x{1E0E}\x{0110}\x{018B}\x{018A}\x{0189}\x{A779}\x{00D0}])/'
+ ],
+ [
+ 'base' => "DZ",
+ 'letters' => '/(DZ|DŽ|[\x{01F1}\x{01C4}])/'
+ ],
+ [
+ 'base' => "Dz",
+ 'letters' => '/(Dz|Dž|[\x{01F2}\x{01C5}])/'
+ ],
+ [
+ 'base' => "E",
+ 'letters' => '/(E|Ⓔ|E|È|É|Ê|Ề|Ế|Ễ|Ể|Ẽ|Ē|Ḕ|Ḗ|Ĕ|Ė|Ë|Ẻ|Ě|Ȅ|Ȇ|Ẹ|Ệ|Ȩ|Ḝ|Ę|Ḙ|Ḛ|Ɛ|Ǝ|[\x{0045}\x{24BA}\x{FF25}\x{00C8}\x{00C9}\x{00CA}\x{1EC0}\x{1EBE}\x{1EC4}\x{1EC2}\x{1EBC}\x{0112}\x{1E14}\x{1E16}\x{0114}\x{0116}\x{00CB}\x{1EBA}\x{011A}\x{0204}\x{0206}\x{1EB8}\x{1EC6}\x{0228}\x{1E1C}\x{0118}\x{1E18}\x{1E1A}\x{0190}\x{018E}])/'
+ ],
+ [
+ 'base' => "F",
+ 'letters' => '/(F|Ⓕ|F|Ḟ|Ƒ|Ꝼ|[\x{0046}\x{24BB}\x{FF26}\x{1E1E}\x{0191}\x{A77B}])/'
+ ],
+ [
+ 'base' => "G",
+ 'letters' => '/(G|Ⓖ|G|Ǵ|Ĝ|Ḡ|Ğ|Ġ|Ǧ|Ģ|Ǥ|Ɠ|Ꞡ|Ᵹ|Ꝿ|[\x{0047}\x{24BC}\x{FF27}\x{01F4}\x{011C}\x{1E20}\x{011E}\x{0120}\x{01E6}\x{0122}\x{01E4}\x{0193}\x{A7A0}\x{A77D}\x{A77E}])/'
+ ],
+ [
+ 'base' => "H",
+ 'letters' => '/(H|Ⓗ|H|Ĥ|Ḣ|Ḧ|Ȟ|Ḥ|Ḩ|Ḫ|Ħ|Ⱨ|Ⱶ|Ɥ|[\x{0048}\x{24BD}\x{FF28}\x{0124}\x{1E22}\x{1E26}\x{021E}\x{1E24}\x{1E28}\x{1E2A}\x{0126}\x{2C67}\x{2C75}\x{A78D}])/'
+ ],
+ [
+ 'base' => "I",
+ 'letters' => '/(I|Ⓘ|I|Ì|Í|Î|Ĩ|Ī|Ĭ|İ|Ï|Ḯ|Ỉ|Ǐ|Ȉ|Ȋ|Ị|Į|Ḭ|Ɨ|[\x{0049}\x{24BE}\x{FF29}\x{00CC}\x{00CD}\x{00CE}\x{0128}\x{012A}\x{012C}\x{0130}\x{00CF}\x{1E2E}\x{1EC8}\x{01CF}\x{0208}\x{020A}\x{1ECA}\x{012E}\x{1E2C}\x{0197}])/'
+ ],
+ [
+ 'base' => "J",
+ 'letters' => '/(J|Ⓙ|J|Ĵ|Ɉ|[\x{004A}\x{24BF}\x{FF2A}\x{0134}\x{0248}])/'
+ ],
+ [
+ 'base' => "K",
+ 'letters' => '/(K|Ⓚ|K|Ḱ|Ǩ|Ḳ|Ķ|Ḵ|Ƙ|Ⱪ|Ꝁ|Ꝃ|Ꝅ|Ꞣ|[\x{004B}\x{24C0}\x{FF2B}\x{1E30}\x{01E8}\x{1E32}\x{0136}\x{1E34}\x{0198}\x{2C69}\x{A740}\x{A742}\x{A744}\x{A7A2}])/'
+ ],
+ [
+ 'base' => "L",
+ 'letters' => '/(L|Ⓛ|L|Ŀ|Ĺ|Ľ|Ḷ|Ḹ|Ļ|Ḽ|Ḻ|Ł|Ƚ|Ɫ|Ⱡ|Ꝉ|Ꝇ|Ꞁ|[\x{004C}\x{24C1}\x{FF2C}\x{013F}\x{0139}\x{013D}\x{1E36}\x{1E38}\x{013B}\x{1E3C}\x{1E3A}\x{0141}\x{023D}\x{2C62}\x{2C60}\x{A748}\x{A746}\x{A780}])/'
+ ],
+ [
+ 'base' => "LJ",
+ 'letters' => '/(LJ|[\x{01C7}])/'
+ ],
+ [
+ 'base' => "Lj",
+ 'letters' => '/(Lj|[\x{01C8}])/'
+ ],
+ [
+ 'base' => "M",
+ 'letters' => '/(M|Ⓜ|M|Ḿ|Ṁ|Ṃ|Ɱ|Ɯ|[\x{004D}\x{24C2}\x{FF2D}\x{1E3E}\x{1E40}\x{1E42}\x{2C6E}\x{019C}])/'
+ ],
+ [
+ 'base' => "N",
+ 'letters' => '/(N|Ⓝ|N|Ǹ|Ń|Ñ|Ṅ|Ň|Ṇ|Ņ|Ṋ|Ṉ|Ƞ|Ɲ|Ꞑ|Ꞥ|Ŋ|[\x{004E}\x{24C3}\x{FF2E}\x{01F8}\x{0143}\x{00D1}\x{1E44}\x{0147}\x{1E46}\x{0145}\x{1E4A}\x{1E48}\x{0220}\x{019D}\x{A790}\x{A7A4}\x{014A}])/'
+ ],
+ [
+ 'base' => "NJ",
+ 'letters' => '/(NJ|[\x{01CA}])/'
+ ],
+ [
+ 'base' => "Nj",
+ 'letters' => '/(Nj|[\x{01CB}])/'
+ ],
+ [
+ 'base' => "O",
+ 'letters' => '/(O|Ⓞ|O|Ò|Ó|Ô|Ồ|Ố|Ỗ|Ổ|Õ|Ṍ|Ȭ|Ṏ|Ō|Ṑ|Ṓ|Ŏ|Ȯ|Ȱ|Ö|Ȫ|Ỏ|Ő|Ǒ|Ȍ|Ȏ|Ơ|Ờ|Ớ|Ỡ|Ở|Ợ|Ọ|Ộ|Ǫ|Ǭ|Ø|Ǿ|Ɔ|Ɵ|Ꝋ|Ꝍ|[\x{004F}\x{24C4}\x{FF2F}\x{00D2}\x{00D3}\x{00D4}\x{1ED2}\x{1ED0}\x{1ED6}\x{1ED4}\x{00D5}\x{1E4C}\x{022C}\x{1E4E}\x{014C}\x{1E50}\x{1E52}\x{014E}\x{022E}\x{0230}\x{00D6}\x{022A}\x{1ECE}\x{0150}\x{01D1}\x{020C}\x{020E}\x{01A0}\x{1EDC}\x{1EDA}\x{1EE0}\x{1EDE}\x{1EE2}\x{1ECC}\x{1ED8}\x{01EA}\x{01EC}\x{00D8}\x{01FE}\x{0186}\x{019F}\x{A74A}\x{A74C}])/'
+ ],
+ [
+ 'base' => "OE",
+ 'letters' => '/(Œ|[\x{0152}])/'
+ ],
+ [
+ 'base' => "OI",
+ 'letters' => '/(Ƣ|[\x{01A2}])/'
+ ],
+ [
+ 'base' => "OO",
+ 'letters' => '/(Ꝏ|[\x{A74E}])/'
+ ],
+ [
+ 'base' => "OU",
+ 'letters' => '/(Ȣ|[\x{0222}])/'
+ ],
+ [
+ 'base' => "P",
+ 'letters' => '/(P|Ⓟ|P|Ṕ|Ṗ|Ƥ|Ᵽ|Ꝑ|Ꝓ|Ꝕ|[\x{0050}\x{24C5}\x{FF30}\x{1E54}\x{1E56}\x{01A4}\x{2C63}\x{A750}\x{A752}\x{A754}])/'
+ ],
+ [
+ 'base' => "Q",
+ 'letters' => '/(Q|Ⓠ|Q|Ꝗ|Ꝙ|Ɋ|[\x{0051}\x{24C6}\x{FF31}\x{A756}\x{A758}\x{024A}])/'
+ ],
+ [
+ 'base' => "R",
+ 'letters' => '/(R|Ⓡ|R|Ŕ|Ṙ|Ř|Ȑ|Ȓ|Ṛ|Ṝ|Ŗ|Ṟ|Ɍ|Ɽ|Ꝛ|Ꞧ|Ꞃ|[\x{0052}\x{24C7}\x{FF32}\x{0154}\x{1E58}\x{0158}\x{0210}\x{0212}\x{1E5A}\x{1E5C}\x{0156}\x{1E5E}\x{024C}\x{2C64}\x{A75A}\x{A7A6}\x{A782}])/'
+ ],
+ [
+ 'base' => "S",
+ 'letters' => '/(S|Ⓢ|S|ẞ|Ś|Ṥ|Ŝ|Ṡ|Š|Ṧ|Ṣ|Ṩ|Ș|Ş|Ȿ|Ꞩ|Ꞅ|[\x{0053}\x{24C8}\x{FF33}\x{1E9E}\x{015A}\x{1E64}\x{015C}\x{1E60}\x{0160}\x{1E66}\x{1E62}\x{1E68}\x{0218}\x{015E}\x{2C7E}\x{A7A8}\x{A784}])/'
+ ],
+ [
+ 'base' => "T",
+ 'letters' => '/(T|Ⓣ|T|Ṫ|Ť|Ṭ|Ț|Ţ|Ṱ|Ṯ|Ŧ|Ƭ|Ʈ|Ⱦ|Ꞇ|[\x{0054}\x{24C9}\x{FF34}\x{1E6A}\x{0164}\x{1E6C}\x{021A}\x{0162}\x{1E70}\x{1E6E}\x{0166}\x{01AC}\x{01AE}\x{023E}\x{A786}])/'
+ ],
+ [
+ 'base' => "TH",
+ 'letters' => '/(Þ|[\x{00DE}])/'
+ ],
+ [
+ 'base' => "TZ",
+ 'letters' => '/(Ꜩ|[\x{A728}])/'
+ ],
+ [
+ 'base' => "U",
+ 'letters' => '/(U|Ⓤ|U|Ù|Ú|Û|Ũ|Ṹ|Ū|Ṻ|Ŭ|Ü|Ǜ|Ǘ|Ǖ|Ǚ|Ủ|Ů|Ű|Ǔ|Ȕ|Ȗ|Ư|Ừ|Ứ|Ữ|Ử|Ự|Ụ|Ṳ|Ų|Ṷ|Ṵ|Ʉ|[\x{0055}\x{24CA}\x{FF35}\x{00D9}\x{00DA}\x{00DB}\x{0168}\x{1E78}\x{016A}\x{1E7A}\x{016C}\x{00DC}\x{01DB}\x{01D7}\x{01D5}\x{01D9}\x{1EE6}\x{016E}\x{0170}\x{01D3}\x{0214}\x{0216}\x{01AF}\x{1EEA}\x{1EE8}\x{1EEE}\x{1EEC}\x{1EF0}\x{1EE4}\x{1E72}\x{0172}\x{1E76}\x{1E74}\x{0244}])/'
+ ],
+ [
+ 'base' => "V",
+ 'letters' => '/(V|Ⓥ|V|Ṽ|Ṿ|Ʋ|Ꝟ|Ʌ|[\x{0056}\x{24CB}\x{FF36}\x{1E7C}\x{1E7E}\x{01B2}\x{A75E}\x{0245}])/'
+ ],
+ [
+ 'base' => "VY",
+ 'letters' => '/(Ꝡ|[\x{A760}])/'
+ ],
+ [
+ 'base' => "W",
+ 'letters' => '/(W|Ⓦ|W|Ẁ|Ẃ|Ŵ|Ẇ|Ẅ|Ẉ|Ⱳ|[\x{0057}\x{24CC}\x{FF37}\x{1E80}\x{1E82}\x{0174}\x{1E86}\x{1E84}\x{1E88}\x{2C72}])/'
+ ],
+ [
+ 'base' => "X",
+ 'letters' => '/(X|Ⓧ|X|Ẋ|Ẍ|[\x{0058}\x{24CD}\x{FF38}\x{1E8A}\x{1E8C}])/'
+ ],
+ [
+ 'base' => "Y",
+ 'letters' => '/(Y|Ⓨ|Y|Ỳ|Ý|Ŷ|Ỹ|Ȳ|Ẏ|Ÿ|Ỷ|Ỵ|Ƴ|Ɏ|Ỿ|[\x{0059}\x{24CE}\x{FF39}\x{1EF2}\x{00DD}\x{0176}\x{1EF8}\x{0232}\x{1E8E}\x{0178}\x{1EF6}\x{1EF4}\x{01B3}\x{024E}\x{1EFE}])/'
+ ],
+ [
+ 'base' => "Z",
+ 'letters' => '/(Z|Ⓩ|Z|Ź|Ẑ|Ż|Ž|Ẓ|Ẕ|Ƶ|Ȥ|Ɀ|Ⱬ|Ꝣ|[\x{005A}\x{24CF}\x{FF3A}\x{0179}\x{1E90}\x{017B}\x{017D}\x{1E92}\x{1E94}\x{01B5}\x{0224}\x{2C7F}\x{2C6B}\x{A762}])/'
+ ],
+ [
+ 'base' => "a",
+ 'letters' => '/(a|ⓐ|a|ẚ|à|á|â|ầ|ấ|ẫ|ẩ|ã|ā|ă|ằ|ắ|ẵ|ẳ|ȧ|ǡ|ä|ǟ|ả|å|ǻ|ǎ|ȁ|ȃ|ạ|ậ|ặ|ḁ|ą|ⱥ|ɐ|[\x{0061}\x{24D0}\x{FF41}\x{1E9A}\x{00E0}\x{00E1}\x{00E2}\x{1EA7}\x{1EA5}\x{1EAB}\x{1EA9}\x{00E3}\x{0101}\x{0103}\x{1EB1}\x{1EAF}\x{1EB5}\x{1EB3}\x{0227}\x{01E1}\x{00E4}\x{01DF}\x{1EA3}\x{00E5}\x{01FB}\x{01CE}\x{0201}\x{0203}\x{1EA1}\x{1EAD}\x{1EB7}\x{1E01}\x{0105}\x{2C65}\x{0250}])/'
+ ],
+ [
+ 'base' => "aa",
+ 'letters' => '/(ꜳ|[\x{A733}])/'
+ ],
+ [
+ 'base' => "ae",
+ 'letters' => '/(æ|ǽ|ǣ|[\x{00E6}\x{01FD}\x{01E3}])/'
+ ],
+ [
+ 'base' => "ao",
+ 'letters' => '/(ꜵ|[\x{A735}])/'
+ ],
+ [
+ 'base' => "au",
+ 'letters' => '/(ꜷ|[\x{A737}])/'
+ ],
+ [
+ 'base' => "av",
+ 'letters' => '/(ꜹ|ꜻ|[\x{A739}\x{A73B}])/'
+ ],
+ [
+ 'base' => "ay",
+ 'letters' => '/(ꜽ|[\x{A73D}])/'
+ ],
+ [
+ 'base' => "b",
+ 'letters' => '/(b|ⓑ|b|ḃ|ḅ|ḇ|ƀ|ƃ|ɓ|[\x{0062}\x{24D1}\x{FF42}\x{1E03}\x{1E05}\x{1E07}\x{0180}\x{0183}\x{0253}])/'
+ ],
+ [
+ 'base' => "c",
+ 'letters' => '/(c|ⓒ|c|ć|ĉ|ċ|č|ç|ḉ|ƈ|ȼ|ꜿ|ↄ|[\x{0063}\x{24D2}\x{FF43}\x{0107}\x{0109}\x{010B}\x{010D}\x{00E7}\x{1E09}\x{0188}\x{023C}\x{A73F}\x{2184}])/'
+ ],
+ [
+ 'base' => "d",
+ 'letters' => '/(d|ⓓ|d|ḋ|ď|ḍ|ḑ|ḓ|ḏ|đ|ƌ|ɖ|ɗ|ꝺ|ð|[\x{0064}\x{24D3}\x{FF44}\x{1E0B}\x{010F}\x{1E0D}\x{1E11}\x{1E13}\x{1E0F}\x{0111}\x{018C}\x{0256}\x{0257}\x{A77A}\x{00F0}])/'
+ ],
+ [
+ 'base' => "dz",
+ 'letters' => '/(dz|dž|[\x{01F3}\x{01C6}])/'
+ ],
+ [
+ 'base' => "e",
+ 'letters' => '/(e|ⓔ|e|è|é|ê|ề|ế|ễ|ể|ẽ|ē|ḕ|ḗ|ĕ|ė|ë|ẻ|ě|ȅ|ȇ|ẹ|ệ|ȩ|ḝ|ę|ḙ|ḛ|ɇ|ɛ|ǝ|[\x{0065}\x{24D4}\x{FF45}\x{00E8}\x{00E9}\x{00EA}\x{1EC1}\x{1EBF}\x{1EC5}\x{1EC3}\x{1EBD}\x{0113}\x{1E15}\x{1E17}\x{0115}\x{0117}\x{00EB}\x{1EBB}\x{011B}\x{0205}\x{0207}\x{1EB9}\x{1EC7}\x{0229}\x{1E1D}\x{0119}\x{1E19}\x{1E1B}\x{0247}\x{025B}\x{01DD}])/'
+ ],
+ [
+ 'base' => "f",
+ 'letters' => '/(f|ⓕ|f|ḟ|ƒ|ꝼ|[\x{0066}\x{24D5}\x{FF46}\x{1E1F}\x{0192}\x{A77C}])/'
+ ],
+ [
+ 'base' => "g",
+ 'letters' => '/(g|ⓖ|g|ǵ|ĝ|ḡ|ğ|ġ|ǧ|ģ|ǥ|ɠ|ꞡ|ᵹ|ꝿ|[\x{0067}\x{24D6}\x{FF47}\x{01F5}\x{011D}\x{1E21}\x{011F}\x{0121}\x{01E7}\x{0123}\x{01E5}\x{0260}\x{A7A1}\x{1D79}\x{A77F}])/'
+ ],
+ [
+ 'base' => "h",
+ 'letters' => '/(h|ⓗ|h|ĥ|ḣ|ḧ|ȟ|ḥ|ḩ|ḫ|ẖ|ħ|ⱨ|ⱶ|ɥ|[\x{0068}\x{24D7}\x{FF48}\x{0125}\x{1E23}\x{1E27}\x{021F}\x{1E25}\x{1E29}\x{1E2B}\x{1E96}\x{0127}\x{2C68}\x{2C76}\x{0265}])/'
+ ],
+ [
+ 'base' => "hv",
+ 'letters' => '/(ƕ|[\x{0195}])/'
+ ],
+ [
+ 'base' => "i",
+ 'letters' => '/(i|ⓘ|i|ì|í|î|ĩ|ī|ĭ|ï|ḯ|ỉ|ǐ|ȉ|ȋ|ị|į|ḭ|ɨ|ı|[\x{0069}\x{24D8}\x{FF49}\x{00EC}\x{00ED}\x{00EE}\x{0129}\x{012B}\x{012D}\x{00EF}\x{1E2F}\x{1EC9}\x{01D0}\x{0209}\x{020B}\x{1ECB}\x{012F}\x{1E2D}\x{0268}\x{0131}])/'
+ ],
+ [
+ 'base' => "ij",
+ 'letters' => '/(ij|[\x{0133}])/'
+ ],
+ [
+ 'base' => "j",
+ 'letters' => '/(j|ⓙ|j|ĵ|ǰ|ɉ|[\x{006A}\x{24D9}\x{FF4A}\x{0135}\x{01F0}\x{0249}])/'
+ ],
+ [
+ 'base' => "k",
+ 'letters' => '/(k|ⓚ|k|ḱ|ǩ|ḳ|ķ|ḵ|ƙ|ⱪ|ꝁ|ꝃ|ꝅ|ꞣ|[\x{006B}\x{24DA}\x{FF4B}\x{1E31}\x{01E9}\x{1E33}\x{0137}\x{1E35}\x{0199}\x{2C6A}\x{A741}\x{A743}\x{A745}\x{A7A3}])/'
+ ],
+ [
+ 'base' => "l",
+ 'letters' => '/(l|ⓛ|l|ŀ|ĺ|ľ|ḷ|ḹ|ļ|ḽ|ḻ|ł|ƚ|ɫ|ⱡ|ꝉ|ꞁ|ꝇ|[\x{006C}\x{24DB}\x{FF4C}\x{0140}\x{013A}\x{013E}\x{1E37}\x{1E39}\x{013C}\x{1E3D}\x{1E3B}\x{0142}\x{019A}\x{026B}\x{2C61}\x{A749}\x{A781}\x{A747}])/'
+ ],
+ [
+ 'base' => "lj",
+ 'letters' => '/(lj|[\x{01C9}])/'
+ ],
+ [
+ 'base' => "m",
+ 'letters' => '/(m|ⓜ|m|ḿ|ṁ|ṃ|ɱ|ɯ|[\x{006D}\x{24DC}\x{FF4D}\x{1E3F}\x{1E41}\x{1E43}\x{0271}\x{026F}])/'
+ ],
+ [
+ 'base' => "n",
+ 'letters' => '/(n|ⓝ|n|ǹ|ń|ñ|ṅ|ň|ṇ|ņ|ṋ|ṉ|ƞ|ɲ|ʼn|ꞑ|ꞥ|ŋ|[\x{006E}\x{24DD}\x{FF4E}\x{01F9}\x{0144}\x{00F1}\x{1E45}\x{0148}\x{1E47}\x{0146}\x{1E4B}\x{1E49}\x{019E}\x{0272}\x{0149}\x{A791}\x{A7A5}\x{014B}])/'
+ ],
+ [
+ 'base' => "nj",
+ 'letters' => '/(nj|[\x{01CC}])/'
+ ],
+ [
+ 'base' => "o",
+ 'letters' => '/(o|ⓞ|o|ò|ó|ô|ồ|ố|ỗ|ổ|õ|ṍ|ȭ|ṏ|ō|ṑ|ṓ|ŏ|ȯ|ȱ|ö|ȫ|ỏ|ő|ǒ|ȍ|ȏ|ơ|ờ|ớ|ỡ|ở|ợ|ọ|ộ|ǫ|ǭ|ø|ǿ|ɔ|ꝋ|ꝍ|ɵ|[\x{006F}\x{24DE}\x{FF4F}\x{00F2}\x{00F3}\x{00F4}\x{1ED3}\x{1ED1}\x{1ED7}\x{1ED5}\x{00F5}\x{1E4D}\x{022D}\x{1E4F}\x{014D}\x{1E51}\x{1E53}\x{014F}\x{022F}\x{0231}\x{00F6}\x{022B}\x{1ECF}\x{0151}\x{01D2}\x{020D}\x{020F}\x{01A1}\x{1EDD}\x{1EDB}\x{1EE1}\x{1EDF}\x{1EE3}\x{1ECD}\x{1ED9}\x{01EB}\x{01ED}\x{00F8}\x{01FF}\x{0254}\x{A74B}\x{A74D}\x{0275}])/'
+ ],
+ [
+ 'base' => "oe",
+ 'letters' => '/(œ|[\x{0153}])/'
+ ],
+ [
+ 'base' => "oi",
+ 'letters' => '/(ƣ|[\x{01A3}])/'
+ ],
+ [
+ 'base' => "ou",
+ 'letters' => '/(ȣ|[\x{0223}])/'
+ ],
+ [
+ 'base' => "oo",
+ 'letters' => '/(ꝏ|[\x{A74F}])/'
+ ],
+ [
+ 'base' => "p",
+ 'letters' => '/(p|ⓟ|p|ṕ|ṗ|ƥ|ᵽ|ꝑ|ꝓ|ꝕ|[\x{0070}\x{24DF}\x{FF50}\x{1E55}\x{1E57}\x{01A5}\x{1D7D}\x{A751}\x{A753}\x{A755}])/'
+ ],
+ [
+ 'base' => "q",
+ 'letters' => '/(q|ⓠ|q|ɋ|ꝗ|ꝙ|[\x{0071}\x{24E0}\x{FF51}\x{024B}\x{A757}\x{A759}])/'
+ ],
+ [
+ 'base' => "r",
+ 'letters' => '/(r|ⓡ|r|ŕ|ṙ|ř|ȑ|ȓ|ṛ|ṝ|ŗ|ṟ|ɍ|ɽ|ꝛ|ꞧ|ꞃ|[\x{0072}\x{24E1}\x{FF52}\x{0155}\x{1E59}\x{0159}\x{0211}\x{0213}\x{1E5B}\x{1E5D}\x{0157}\x{1E5F}\x{024D}\x{027D}\x{A75B}\x{A7A7}\x{A783}])/'
+ ],
+ [
+ 'base' => "s",
+ 'letters' => '/(s|ⓢ|s|ś|ṥ|ŝ|ṡ|š|ṧ|ṣ|ṩ|ș|ş|ȿ|ꞩ|ꞅ|ẛ|ſ|[\x{0073}\x{24E2}\x{FF53}\x{015B}\x{1E65}\x{015D}\x{1E61}\x{0161}\x{1E67}\x{1E63}\x{1E69}\x{0219}\x{015F}\x{023F}\x{A7A9}\x{A785}\x{1E9B}\x{017F}])/'
+ ],
+ [
+ 'base' => "ss",
+ 'letters' => '/(ß|[\x{00DF}])/'
+ ],
+ [
+ 'base' => "t",
+ 'letters' => '/(t|ⓣ|t|ṫ|ẗ|ť|ṭ|ț|ţ|ṱ|ṯ|ŧ|ƭ|ʈ|ⱦ|ꞇ|[\x{0074}\x{24E3}\x{FF54}\x{1E6B}\x{1E97}\x{0165}\x{1E6D}\x{021B}\x{0163}\x{1E71}\x{1E6F}\x{0167}\x{01AD}\x{0288}\x{2C66}\x{A787}])/'
+ ],
+ [
+ 'base' => "th",
+ 'letters' => '/(þ|[\x{00FE}])/'
+ ],
+ [
+ 'base' => "tz",
+ 'letters' => '/(ꜩ|[\x{A729}])/'
+ ],
+ [
+ 'base' => "u",
+ 'letters' => '/(u|ⓤ|u|ù|ú|û|ũ|ṹ|ū|ṻ|ŭ|ü|ǜ|ǘ|ǖ|ǚ|ủ|ů|ű|ǔ|ȕ|ȗ|ư|ừ|ứ|ữ|ử|ự|ụ|ṳ|ų|ṷ|ṵ|ʉ|[\x{0075}\x{24E4}\x{FF55}\x{00F9}\x{00FA}\x{00FB}\x{0169}\x{1E79}\x{016B}\x{1E7B}\x{016D}\x{00FC}\x{01DC}\x{01D8}\x{01D6}\x{01DA}\x{1EE7}\x{016F}\x{0171}\x{01D4}\x{0215}\x{0217}\x{01B0}\x{1EEB}\x{1EE9}\x{1EEF}\x{1EED}\x{1EF1}\x{1EE5}\x{1E73}\x{0173}\x{1E77}\x{1E75}\x{0289}])/'
+ ],
+ [
+ 'base' => "v",
+ 'letters' => '/(v|ⓥ|v|ṽ|ṿ|ʋ|ꝟ|ʌ|[\x{0076}\x{24E5}\x{FF56}\x{1E7D}\x{1E7F}\x{028B}\x{A75F}\x{028C}])/'
+ ],
+ [
+ 'base' => "vy",
+ 'letters' => '/(ꝡ|[\x{A761}])/'
+ ],
+ [
+ 'base' => "w",
+ 'letters' => '/(w|ⓦ|w|ẁ|ẃ|ŵ|ẇ|ẅ|ẘ|ẉ|ⱳ|[\x{0077}\x{24E6}\x{FF57}\x{1E81}\x{1E83}\x{0175}\x{1E87}\x{1E85}\x{1E98}\x{1E89}\x{2C73}])/'
+ ],
+ [
+ 'base' => "x",
+ 'letters' => '/(x|ⓧ|x|ẋ|ẍ|[\x{0078}\x{24E7}\x{FF58}\x{1E8B}\x{1E8D}])/'
+ ],
+ [
+ 'base' => "y",
+ 'letters' => '/(y|ⓨ|y|ỳ|ý|ŷ|ỹ|ȳ|ẏ|ÿ|ỷ|ẙ|ỵ|ƴ|ɏ|ỿ|[\x{0079}\x{24E8}\x{FF59}\x{1EF3}\x{00FD}\x{0177}\x{1EF9}\x{0233}\x{1E8F}\x{00FF}\x{1EF7}\x{1E99}\x{1EF5}\x{01B4}\x{024F}\x{1EFF}])/'
+ ],
+ [
+ 'base' => "z",
+ 'letters' => '/(z|ⓩ|z|ź|ẑ|ż|ž|ẓ|ẕ|ƶ|ȥ|ɀ|ⱬ|ꝣ|[\x{007A}\x{24E9}\x{FF5A}\x{017A}\x{1E91}\x{017C}\x{017E}\x{1E93}\x{1E95}\x{01B6}\x{0225}\x{0240}\x{2C6C}\x{A763}])/'
+ ]
+ ];
+
+ public static function removeDiacritics($str)
+ {
+ $flags = "um";
+ for ($i = 0; $i < sizeof(dcUtils::$defaultDiacriticsRemovalMap); $i++) {
+ $str = preg_replace(
+ dcUtils::$defaultDiacriticsRemovalMap[$i]['letters'] . $flags,
+ dcUtils::$defaultDiacriticsRemovalMap[$i]['base'],
+ $str);
+ }
+ return $str;
+ }
+}
diff --git a/dotclear._no/inc/core/class.dc.workspace.php b/dotclear._no/inc/core/class.dc.workspace.php
new file mode 100644
index 0000000..9c28550
--- /dev/null
+++ b/dotclear._no/inc/core/class.dc.workspace.php
@@ -0,0 +1,461 @@
+connection Database connection object
+ protected $table; ///< string Preferences table name
+ protected $user_id; ///< string User ID
+
+ protected $global_prefs = []; ///< array Global prefs array
+ protected $local_prefs = []; ///< array Local prefs array
+ protected $prefs = []; ///< array Associative prefs array
+ protected $ws; ///< string Current workspace
+
+ /**
+ Object constructor. Retrieves user prefs and puts them in $prefs
+ array. Local (user) prefs have a highest priority than global prefs.
+
+ @param name string ID for this workspace
+ */
+ public function __construct(&$core, $user_id, $name, $rs = null)
+ {
+ if (preg_match('/^[a-zA-Z][a-zA-Z0-9]+$/', $name)) {
+ $this->ws = $name;
+ } else {
+ throw new Exception(sprintf(__('Invalid dcWorkspace: %s'), $name));
+ }
+
+ $this->con = &$core->con;
+ $this->table = $core->prefix . 'pref';
+ $this->user_id = &$user_id;
+
+ try { $this->getPrefs($rs);} catch (Exception $e) {
+ if (version_compare($core->getVersion('core'), '2.3', '>')) {
+ trigger_error(__('Unable to retrieve prefs:') . ' ' . $this->con->error(), E_USER_ERROR);
+ }
+ }
+ }
+
+ private function getPrefs($rs = null)
+ {
+ if ($rs == null) {
+ $strReq = 'SELECT user_id, pref_id, pref_value, ' .
+ 'pref_type, pref_label, pref_ws ' .
+ 'FROM ' . $this->table . ' ' .
+ "WHERE (user_id = '" . $this->con->escape($this->user_id) . "' " .
+ 'OR user_id IS NULL) ' .
+ "AND pref_ws = '" . $this->con->escape($this->ws) . "' " .
+ 'ORDER BY pref_id ASC ';
+
+ try {
+ $rs = $this->con->select($strReq);
+ } catch (Exception $e) {
+ throw $e;
+ }
+ }
+ while ($rs->fetch()) {
+ if ($rs->f('pref_ws') != $this->ws) {
+ break;
+ }
+ $id = trim($rs->f('pref_id'));
+ $value = $rs->f('pref_value');
+ $type = $rs->f('pref_type');
+
+ if ($type == 'array') {
+ $value = @json_decode($value, true);
+ } else {
+ if ($type == 'float' || $type == 'double') {
+ $type = 'float';
+ } elseif ($type != 'boolean' && $type != 'integer') {
+ $type = 'string';
+ }
+ }
+
+ settype($value, $type);
+
+ $array = $rs->user_id ? 'local' : 'global';
+
+ $this->{$array . '_prefs'}[$id] = [
+ 'ws' => $this->ws,
+ 'value' => $value,
+ 'type' => $type,
+ 'label' => (string) $rs->f('pref_label'),
+ 'global' => $rs->user_id == ''
+ ];
+ }
+
+ $this->prefs = $this->global_prefs;
+
+ foreach ($this->local_prefs as $id => $v) {
+ $this->prefs[$id] = $v;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns true if a pref exist, else false
+ *
+ * @param string $id The identifier
+ * @param boolean $global The global
+ *
+ * @return boolean
+ */
+ public function prefExists($id, $global = false)
+ {
+ $array = $global ? 'global' : 'local';
+ return isset($this->{$array . '_prefs'}[$id]);
+ }
+
+ /**
+ Returns pref value if exists.
+
+ @param n string Pref name
+ @return mixed
+ */
+ public function get($n)
+ {
+ if (isset($this->prefs[$n]['value'])) {
+ return $this->prefs[$n]['value'];
+ }
+
+ return;
+ }
+
+ /**
+ Returns global pref value if exists.
+
+ @param n string Pref name
+ @return mixed
+ */
+ public function getGlobal($n)
+ {
+ if (isset($this->global_prefs[$n]['value'])) {
+ return $this->global_prefs[$n]['value'];
+ }
+
+ return;
+ }
+
+ /**
+ Returns local pref value if exists.
+
+ @param n string Pref name
+ @return mixed
+ */
+ public function getLocal($n)
+ {
+ if (isset($this->local_prefs[$n]['value'])) {
+ return $this->local_prefs[$n]['value'];
+ }
+
+ return;
+ }
+ /**
+ Magic __get method.
+ @copydoc ::get
+ */
+ public function __get($n)
+ {
+ return $this->get($n);
+ }
+
+ /**
+ Sets a pref in $prefs property. This sets the pref for script
+ execution time only and if pref exists.
+
+ @param n string Pref name
+ @param v mixed Pref value
+ */
+ public function set($n, $v)
+ {
+ if (isset($this->prefs[$n])) {
+ $this->prefs[$n]['value'] = $v;
+ }
+ }
+
+ /**
+ Magic __set method.
+ @copydoc ::set
+ */
+ public function __set($n, $v)
+ {
+ $this->set($n, $v);
+ }
+
+ /**
+ Creates or updates a pref.
+
+ $type could be 'string', 'integer', 'float', 'boolean' or null. If $type is
+ null and pref exists, it will keep current pref type.
+
+ $value_change allow you to not change pref. Useful if you need to change
+ a pref label or type and don't want to change its value.
+
+ @param id string Pref ID
+ @param value mixed Pref value
+ @param type string Pref type
+ @param label string Pref label
+ @param value_change boolean Change pref value or not
+ @param global boolean Pref is global
+ */
+ public function put($id, $value, $type = null, $label = null, $value_change = true, $global = false)
+ {
+ if (!preg_match('/^[a-zA-Z][a-zA-Z0-9_]+$/', $id)) {
+ throw new Exception(sprintf(__('%s is not a valid pref id'), $id));
+ }
+
+ # We don't want to change pref value
+ if (!$value_change) {
+ if (!$global && $this->prefExists($id, false)) {
+ $value = $this->local_prefs[$id]['value'];
+ } elseif ($this->prefExists($id, true)) {
+ $value = $this->global_prefs[$id]['value'];
+ }
+ }
+
+ # Pref type
+ if ($type == 'double') {
+ $type = 'float';
+ } elseif ($type === null) {
+ if (!$global && $this->prefExists($id, false)) {
+ $type = $this->local_prefs[$id]['type'];
+ } elseif ($this->prefExists($id, true)) {
+ $type = $this->global_prefs[$id]['type'];
+ } else {
+ if (is_array($value)) {
+ $type = 'array';
+ } else {
+ $type = 'string';
+ }
+ }
+ } elseif ($type != 'boolean' && $type != 'integer' && $type != 'float' && $type != 'array') {
+ $type = 'string';
+ }
+
+ # We don't change label
+ if ($label == null) {
+ if (!$global && $this->prefExists($id, false)) {
+ $label = $this->local_prefs[$id]['label'];
+ } elseif ($this->prefExists($id, true)) {
+ $label = $this->global_prefs[$id]['label'];
+ }
+ }
+
+ if ($type != 'array') {
+ settype($value, $type);
+ } else {
+ $value = json_encode($value);
+ }
+
+ $cur = $this->con->openCursor($this->table);
+ $cur->pref_value = ($type == 'boolean') ? (string) (integer) $value : (string) $value;
+ $cur->pref_type = $type;
+ $cur->pref_label = $label;
+
+ #If we are local, compare to global value
+ if (!$global && $this->prefExists($id, true)) {
+ $g = $this->global_prefs[$id];
+ $same_pref = $g['ws'] == $this->ws && $g['value'] == $value
+ && $g['type'] == $type && $g['label'] == $label;
+
+ # Drop pref if same value as global
+ if ($same_pref && $this->prefExists($id, false)) {
+ $this->drop($id);
+ } elseif ($same_pref) {
+ return;
+ }
+ }
+
+ if ($this->prefExists($id, $global) && $this->ws == $this->prefs[$id]['ws']) {
+ if ($global) {
+ $where = 'WHERE user_id IS NULL ';
+ } else {
+ $where = "WHERE user_id = '" . $this->con->escape($this->user_id) . "' ";
+ }
+
+ $cur->update($where . "AND pref_id = '" . $this->con->escape($id) . "' AND pref_ws = '" . $this->con->escape($this->ws) . "' ");
+ } else {
+ $cur->pref_id = $id;
+ $cur->user_id = $global ? null : $this->user_id;
+ $cur->pref_ws = $this->ws;
+
+ $cur->insert();
+ }
+ }
+
+ /**
+ Rename an existing pref in a Workspace
+
+ @param $oldId string Current pref name
+ @param $newId string New pref name
+ @return boolean
+ */
+ public function rename($oldId, $newId)
+ {
+ if (!$this->ws) {
+ throw new Exception(__('No workspace specified'));
+ }
+
+ if (!array_key_exists($oldId, $this->prefs) || array_key_exists($newId, $this->prefs)) {
+ return false;
+ }
+
+ // Rename the pref in the prefs array
+ $this->prefs[$newId] = $this->prefs[$oldId];
+ unset($this->prefs[$oldId]);
+
+ // Rename the pref in the database
+ $strReq = 'UPDATE ' . $this->table .
+ " SET pref_id = '" . $this->con->escape($newId) . "' " .
+ " WHERE pref_ws = '" . $this->con->escape($this->ws) . "' " .
+ " AND pref_id = '" . $this->con->escape($oldId) . "' ";
+ $this->con->execute($strReq);
+ return true;
+ }
+
+ /**
+ Removes an existing pref. Workspace
+
+ @param id string Pref ID
+ @param force_global boolean Force global pref drop
+ */
+ public function drop($id, $force_global = false)
+ {
+ if (!$this->ws) {
+ throw new Exception(__('No workspace specified'));
+ }
+
+ $strReq = 'DELETE FROM ' . $this->table . ' ';
+
+ if (($force_global) || ($this->user_id === null)) {
+ $strReq .= 'WHERE user_id IS NULL ';
+ $global = true;
+ } else {
+ $strReq .= "WHERE user_id = '" . $this->con->escape($this->user_id) . "' ";
+ $global = false;
+ }
+
+ $strReq .= "AND pref_id = '" . $this->con->escape($id) . "' ";
+ $strReq .= "AND pref_ws = '" . $this->con->escape($this->ws) . "' ";
+
+ $this->con->execute($strReq);
+
+ if ($this->prefExists($id, $global)) {
+ $array = $global ? 'global' : 'local';
+ unset($this->{$array . '_prefs'}[$id]);
+ }
+
+ $this->prefs = $this->global_prefs;
+ foreach ($this->local_prefs as $id => $v) {
+ $this->prefs[$id] = $v;
+ }
+ }
+
+ /**
+ * Removes every existing specific pref. in a workspace
+ *
+ * @param string $id Pref ID
+ * @param boolean $global Remove global pref too
+ */
+ public function dropEvery($id, $global = false)
+ {
+ if (!$this->ws) {
+ throw new Exception(__('No workspace specified'));
+ }
+
+ $strReq = 'DELETE FROM ' . $this->table . ' ';
+ if (!$global) {
+ $strReq .= 'WHERE user_id IS NOT NULL ';
+ }
+ $strReq .= "AND pref_id = '" . $this->con->escape($id) . "' ";
+ $strReq .= "AND pref_ws = '" . $this->con->escape($this->ws) . "' ";
+
+ $this->con->execute($strReq);
+ }
+
+ /**
+ Removes all existing pref. in a Workspace
+
+ @param force_global boolean Force global pref drop
+ */
+ public function dropAll($force_global = false)
+ {
+ if (!$this->ws) {
+ throw new Exception(__('No workspace specified'));
+ }
+
+ $strReq = 'DELETE FROM ' . $this->table . ' ';
+
+ if (($force_global) || ($this->user_id === null)) {
+ $strReq .= 'WHERE user_id IS NULL ';
+ $global = true;
+ } else {
+ $strReq .= "WHERE user_id = '" . $this->con->escape($this->user_id) . "' ";
+ $global = false;
+ }
+
+ $strReq .= "AND pref_ws = '" . $this->con->escape($this->ws) . "' ";
+
+ $this->con->execute($strReq);
+
+ $array = $global ? 'global' : 'local';
+ unset($this->{$array . '_prefs'});
+ $this->{$array . '_prefs'} = [];
+
+ $array = $global ? 'local' : 'global';
+ $this->prefs = $this->{$array . '_prefs'};
+ }
+
+ /**
+ Returns $ws property content.
+
+ @return string
+ */
+ public function dumpWorkspace()
+ {
+ return $this->ws;
+ }
+
+ /**
+ Returns $prefs property content.
+
+ @return array
+ */
+ public function dumpPrefs()
+ {
+ return $this->prefs;
+ }
+
+ /**
+ Returns $local_prefs property content.
+
+ @return array
+ */
+ public function dumpLocalPrefs()
+ {
+ return $this->local_prefs;
+ }
+
+ /**
+ Returns $global_prefs property content.
+
+ @return array
+ */
+ public function dumpGlobalPrefs()
+ {
+ return $this->global_prefs;
+ }
+
+}
diff --git a/dotclear._no/inc/core/class.dc.xmlrpc.php b/dotclear._no/inc/core/class.dc.xmlrpc.php
new file mode 100644
index 0000000..101b925
--- /dev/null
+++ b/dotclear._no/inc/core/class.dc.xmlrpc.php
@@ -0,0 +1,1621 @@
+core = &$core;
+ $this->blog_id = $blog_id;
+
+ # Blogger methods
+ $this->addCallback('blogger.newPost', [$this, 'blogger_newPost'],
+ ['string', 'string', 'string', 'string', 'string', 'string', 'integer'],
+ 'New post');
+
+ $this->addCallback('blogger.editPost', [$this, 'blogger_editPost'],
+ ['boolean', 'string', 'string', 'string', 'string', 'string', 'integer'],
+ 'Edit a post');
+
+ $this->addCallback('blogger.getPost', [$this, 'blogger_getPost'],
+ ['struct', 'string', 'integer', 'string', 'string'],
+ 'Return a posts by ID');
+
+ $this->addCallback('blogger.deletePost', [$this, 'blogger_deletePost'],
+ ['string', 'string', 'string', 'string', 'string', 'integer'],
+ 'Delete a post');
+
+ $this->addCallback('blogger.getRecentPosts', [$this, 'blogger_getRecentPosts'],
+ ['array', 'string', 'string', 'string', 'string', 'integer'],
+ 'Return a list of recent posts');
+
+ $this->addCallback('blogger.getUsersBlogs', [$this, 'blogger_getUserBlogs'],
+ ['struct', 'string', 'string', 'string'],
+ "Return user's blog");
+
+ $this->addCallback('blogger.getUserInfo', [$this, 'blogger_getUserInfo'],
+ ['struct', 'string', 'string', 'string'],
+ 'Return User Info');
+
+ # Metaweblog methods
+ $this->addCallback('metaWeblog.newPost', [$this, 'mw_newPost'],
+ ['string', 'string', 'string', 'string', 'struct', 'boolean'],
+ 'Creates a new post, and optionnaly publishes it.');
+
+ $this->addCallback('metaWeblog.editPost', [$this, 'mw_editPost'],
+ ['boolean', 'string', 'string', 'string', 'struct', 'boolean'],
+ 'Updates information about an existing entry');
+
+ $this->addCallback('metaWeblog.getPost', [$this, 'mw_getPost'],
+ ['struct', 'string', 'string', 'string'],
+ 'Returns information about a specific post');
+
+ $this->addCallback('metaWeblog.getRecentPosts', [$this, 'mw_getRecentPosts'],
+ ['array', 'string', 'string', 'string', 'integer'],
+ 'List of most recent posts in the system');
+
+ $this->addCallback('metaWeblog.getCategories', [$this, 'mw_getCategories'],
+ ['array', 'string', 'string', 'string'],
+ 'List of all categories defined in the weblog');
+
+ $this->addCallback('metaWeblog.newMediaObject', [$this, 'mw_newMediaObject'],
+ ['struct', 'string', 'string', 'string', 'struct'],
+ 'Upload a file on the web server');
+
+ # MovableType methods
+ $this->addCallback('mt.getRecentPostTitles', [$this, 'mt_getRecentPostTitles'],
+ ['array', 'string', 'string', 'string', 'integer'],
+ 'List of most recent posts in the system');
+
+ $this->addCallback('mt.getCategoryList', [$this, 'mt_getCategoryList'],
+ ['array', 'string', 'string', 'string'],
+ 'List of all categories defined in the weblog');
+
+ $this->addCallback('mt.getPostCategories', [$this, 'mt_getPostCategories'],
+ ['array', 'string', 'string', 'string'],
+ 'List of all categories to which the post is assigned');
+
+ $this->addCallback('mt.setPostCategories', [$this, 'mt_setPostCategories'],
+ ['boolean', 'string', 'string', 'string', 'array'],
+ 'Sets the categories for a post');
+
+ $this->addCallback('mt.publishPost', [$this, 'mt_publishPost'],
+ ['boolean', 'string', 'string', 'string'],
+ 'Retrieve pings list for a post');
+
+ $this->addCallback('mt.supportedMethods', [$this, 'listMethods'],
+ [], 'Retrieve information about the XML-RPC methods supported by the server.');
+
+ $this->addCallback('mt.supportedTextFilters', [$this, 'mt_supportedTextFilters'],
+ [], 'Retrieve information about supported text filters.');
+
+ # WordPress methods
+ $this->addCallback('wp.getUsersBlogs', [$this, 'wp_getUsersBlogs'],
+ ['array', 'string', 'string'],
+ 'Retrieve the blogs of the user.');
+
+ $this->addCallback('wp.getPage', [$this, 'wp_getPage'],
+ ['struct', 'integer', 'integer', 'string', 'string'],
+ 'Get the page identified by the page ID.');
+
+ $this->addCallback('wp.getPages', [$this, 'wp_getPages'],
+ ['array', 'integer', 'string', 'string', 'integer'],
+ 'Get an array of all the pages on a blog.');
+
+ $this->addCallback('wp.newPage', [$this, 'wp_newPage'],
+ ['integer', 'integer', 'string', 'string', 'struct', 'boolean'],
+ 'Create a new page.');
+
+ $this->addCallback('wp.deletePage', [$this, 'wp_deletePage'],
+ ['boolean', 'integer', 'string', 'string', 'integer'],
+ 'Removes a page from the blog.');
+
+ $this->addCallback('wp.editPage', [$this, 'wp_editPage'],
+ ['boolean', 'integer', 'integer', 'string', 'string', 'struct', 'boolean'],
+ 'Make changes to a blog page.');
+
+ $this->addCallback('wp.getPageList', [$this, 'wp_getPageList'],
+ ['array', 'integer', 'string', 'string'],
+ 'Get an array of all the pages on a blog. Just the minimum details, lighter than wp.getPages.');
+
+ $this->addCallback('wp.getAuthors', [$this, 'wp_getAuthors'],
+ ['array', 'integer', 'string', 'string'],
+ 'Get an array of users for the blog.');
+
+ $this->addCallback('wp.getCategories', [$this, 'wp_getCategories'],
+ ['array', 'integer', 'string', 'string'],
+ 'Get an array of available categories on a blog.');
+
+ $this->addCallback('wp.getTags', [$this, 'wp_getTags'],
+ ['array', 'integer', 'string', 'string'],
+ 'Get list of all tags for the blog.');
+
+ $this->addCallback('wp.newCategory', [$this, 'wp_newCategory'],
+ ['integer', 'integer', 'string', 'string', 'struct'],
+ 'Create a new category.');
+
+ $this->addCallback('wp.deleteCategory', [$this, 'wp_deleteCategory'],
+ ['boolean', 'integer', 'string', 'string', 'integer'],
+ 'Delete a category with a given ID.');
+
+ $this->addCallback('wp.suggestCategories', [$this, 'wp_suggestCategories'],
+ ['array', 'integer', 'string', 'string', 'string', 'integer'],
+ 'Get an array of categories that start with a given string.');
+
+ $this->addCallback('wp.uploadFile', [$this, 'wp_uploadFile'],
+ ['struct', 'integer', 'string', 'string', 'struct'],
+ 'Upload a file');
+
+ $this->addCallback('wp.getPostStatusList', [$this, 'wp_getPostStatusList'],
+ ['array', 'integer', 'string', 'string'],
+ 'Retrieve all of the post statuses.');
+
+ $this->addCallback('wp.getPageStatusList', [$this, 'wp_getPageStatusList'],
+ ['array', 'integer', 'string', 'string'],
+ 'Retrieve all of the pages statuses.');
+
+ $this->addCallback('wp.getPageTemplates', [$this, 'wp_getPageTemplates'],
+ ['struct', 'integer', 'string', 'string'],
+ 'Retrieve page templates.');
+
+ $this->addCallback('wp.getOptions', [$this, 'wp_getOptions'],
+ ['struct', 'integer', 'string', 'string', 'array'],
+ 'Retrieve blog options');
+
+ $this->addCallback('wp.setOptions', [$this, 'wp_setOptions'],
+ ['struct', 'integer', 'string', 'string', 'struct'],
+ 'Update blog options');
+
+ $this->addCallback('wp.getComment', [$this, 'wp_getComment'],
+ ['struct', 'integer', 'string', 'string', 'integer'],
+ "Gets a comment, given it's comment ID.");
+
+ $this->addCallback('wp.getCommentCount', [$this, 'wp_getCommentCount'],
+ ['array', 'integer', 'string', 'string', 'integer'],
+ 'Retrieve comment count.');
+
+ $this->addCallback('wp.getComments', [$this, 'wp_getComments'],
+ ['array', 'integer', 'string', 'string', 'struct'],
+ 'Gets a set of comments for a given post.');
+
+ $this->addCallback('wp.deleteComment', [$this, 'wp_deleteComment'],
+ ['boolean', 'integer', 'string', 'string', 'integer'],
+ 'Delete a comment with given ID.');
+
+ $this->addCallback('wp.editComment', [$this, 'wp_editComment'],
+ ['boolean', 'integer', 'string', 'string', 'integer', 'struct'],
+ 'Edit a comment with given ID.');
+
+ $this->addCallback('wp.newComment', [$this, 'wp_newComment'],
+ ['integer', 'integer', 'string', 'string', 'integer', 'struct'],
+ 'Create a new comment for a given post ID.');
+
+ $this->addCallback('wp.getCommentStatusList', [$this, 'wp_getCommentStatusList'],
+ ['array', 'integer', 'string', 'string'],
+ 'Retrieve all of the comment statuses.');
+
+ # Pingback support
+ $this->addCallback('pingback.ping', [$this, 'pingback_ping'],
+ ['string', 'string', 'string'],
+ 'Notify a link to a post.');
+ }
+
+ public function serve($data = false, $encoding = 'UTF-8')
+ {
+ parent::serve(false, $encoding);
+ }
+
+ public function call($methodname, $args)
+ {
+ try {
+ $rsp = @parent::call($methodname, $args);
+ $this->debugTrace($methodname, $args, $rsp);
+ return $rsp;
+ } catch (Exception $e) {
+ $this->debugTrace($methodname, $args, [$e->getMessage(), $e->getCode()]);
+ throw $e;
+ }
+ }
+
+ private function debugTrace($methodname, $args, $rsp)
+ {
+ if (!$this->debug) {
+ return;
+ }
+
+ if (($fp = @fopen($this->debug_file, 'a')) !== false) {
+ fwrite($fp, '[' . date('r') . ']' . ' ' . $methodname);
+
+ if ($this->trace_args) {
+ fwrite($fp, "\n- args ---\n" . var_export($args, 1));
+ }
+
+ if ($this->trace_response) {
+ fwrite($fp, "\n- response ---\n" . var_export($rsp, 1));
+ }
+ fwrite($fp, "\n");
+ fclose($fp);
+ }
+ }
+
+ /* Internal methods
+ --------------------------------------------------- */
+ private function setUser($user_id, $pwd)
+ {
+ if (empty($pwd) || $this->core->auth->checkUser($user_id, $pwd) !== true) {
+ throw new Exception('Login error');
+ }
+
+ return true;
+ }
+
+ private function setBlog($bypass = false)
+ {
+ if (!$this->blog_id) {
+ throw new Exception('No blog ID given.');
+ }
+
+ if ($this->blog_loaded) {
+ return true;
+ }
+
+ $this->core->setBlog($this->blog_id);
+ $this->blog_loaded = true;
+
+ if (!$this->core->blog->id) {
+ $this->core->blog = null;
+ throw new Exception('Blog does not exist.');
+ }
+
+ if (!$bypass &&
+ (!$this->core->blog->settings->system->enable_xmlrpc ||
+ !$this->core->auth->check('usage,contentadmin', $this->core->blog->id))) {
+ $this->core->blog = null;
+ throw new Exception('Not enough permissions on this blog.');
+ }
+
+ foreach ($this->core->plugins->getModules() as $id => $m) {
+ $this->core->plugins->loadNsFile($id, 'xmlrpc');
+ }
+
+ return true;
+ }
+
+ private function getPostRS($post_id, $user, $pwd, $post_type = 'post')
+ {
+ $this->setUser($user, $pwd);
+ $this->setBlog();
+ $rs = $this->core->blog->getPosts([
+ 'post_id' => (integer) $post_id,
+ 'post_type' => $post_type
+ ]);
+
+ if ($rs->isEmpty()) {
+ throw new Exception('This entry does not exist');
+ }
+
+ return $rs;
+ }
+
+ private function getCatID($cat_url)
+ {
+ $rs = $this->core->blog->getCategories(['cat_url' => $cat_url]);
+
+ return $rs->isEmpty() ? null : $rs->cat_id;
+ }
+
+ /* Generic methods
+ --------------------------------------------------- */
+ private function newPost($blog_id, $user, $pwd, $content, $struct = [], $publish = true)
+ {
+ $this->setUser($user, $pwd);
+ $this->setBlog();
+
+ $title = !empty($struct['title']) ? $struct['title'] : '';
+ $excerpt = !empty($struct['mt_excerpt']) ? $struct['mt_excerpt'] : '';
+ $description = !empty($struct['description']) ? $struct['description'] : null;
+ $dateCreated = !empty($struct['dateCreated']) ? $struct['dateCreated'] : null;
+ $open_comment = isset($struct['mt_allow_comments']) ? $struct['mt_allow_comments'] : 1;
+ $open_tb = isset($struct['mt_allow_pings']) ? $struct['mt_allow_pings'] : 1;
+
+ if ($description !== null) {
+ $content = $description;
+ }
+
+ if (!$title) {
+ $title = text::cutString(html::clean($content), 25) . '...';
+ }
+
+ $excerpt_xhtml = $this->core->callFormater('xhtml', $excerpt);
+ $content_xhtml = $this->core->callFormater('xhtml', $content);
+
+ if (empty($content)) {
+ throw new Exception('Cannot create an empty entry');
+ }
+
+ $cur = $this->core->con->openCursor($this->core->prefix . 'post');
+
+ $cur->user_id = $this->core->auth->userID();
+ $cur->post_lang = $this->core->auth->getInfo('user_lang');
+ $cur->post_title = trim($title);
+ $cur->post_content = $content;
+ $cur->post_excerpt = $excerpt;
+ $cur->post_content_xhtml = $content_xhtml;
+ $cur->post_excerpt_xhtml = $excerpt_xhtml;
+ $cur->post_open_comment = (integer) ($open_comment == 1);
+ $cur->post_open_tb = (integer) ($open_tb == 1);
+ $cur->post_status = (integer) $publish;
+ $cur->post_format = 'xhtml';
+
+ if ($dateCreated) {
+ if ($dateCreated instanceof xmlrpcDate) {
+ $cur->post_dt = date('Y-m-d H:i:00', $dateCreated->getTimestamp());
+ } elseif (is_string($dateCreated) && @strtotime($dateCreated)) {
+ $cur->post_dt = date('Y-m-d H:i:00', strtotime($dateCreated));
+ }
+ }
+
+ # Categories in an array
+ if (isset($struct['categories']) && is_array($struct['categories'])) {
+ $categories = $struct['categories'];
+ $cat_id = !empty($categories[0]) ? $categories[0] : null;
+
+ $cur->cat_id = $this->getCatID($cat_id);
+ }
+
+ if (isset($struct['wp_slug'])) {
+ $cur->post_url = $struct['wp_slug'];
+ }
+
+ if (isset($struct['wp_password'])) {
+ $cur->post_password = $struct['wp_password'];
+ }
+
+ $cur->post_type = 'post';
+ if (!empty($struct['post_type'])) {
+ $cur->post_type = $struct['post_type'];
+ }
+
+ if ($cur->post_type == 'post') {
+ # --BEHAVIOR-- xmlrpcBeforeNewPost
+ $this->core->callBehavior('xmlrpcBeforeNewPost', $this, $cur, $content, $struct, $publish);
+
+ $post_id = $this->core->blog->addPost($cur);
+
+ # --BEHAVIOR-- xmlrpcAfterNewPost
+ $this->core->callBehavior('xmlrpcAfterNewPost', $this, $post_id, $cur, $content, $struct, $publish);
+ } elseif ($cur->post_type == 'page') {
+ if (isset($struct['wp_page_order'])) {
+ $cur->post_position = (integer) $struct['wp_page_order'];
+ }
+
+ $this->core->blog->settings->system->post_url_format = '{t}';
+
+ $post_id = $this->core->blog->addPost($cur);
+ } else {
+ throw new Exception('Invalid post type', 401);
+ }
+
+ return (string) $post_id;
+ }
+
+ private function editPost($post_id, $user, $pwd, $content, $struct = [], $publish = true)
+ {
+ $post_id = (integer) $post_id;
+
+ $post_type = 'post';
+ if (!empty($struct['post_type'])) {
+ $post_type = $struct['post_type'];
+ }
+
+ $post = $this->getPostRS($post_id, $user, $pwd, $post_type);
+
+ $title = (!empty($struct['title'])) ? $struct['title'] : '';
+ $excerpt = (!empty($struct['mt_excerpt'])) ? $struct['mt_excerpt'] : '';
+ $description = (!empty($struct['description'])) ? $struct['description'] : null;
+ $dateCreated = !empty($struct['dateCreated']) ? $struct['dateCreated'] : null;
+ $open_comment = (isset($struct['mt_allow_comments'])) ? $struct['mt_allow_comments'] : 1;
+ $open_tb = (isset($struct['mt_allow_pings'])) ? $struct['mt_allow_pings'] : 1;
+
+ if ($description !== null) {
+ $content = $description;
+ }
+
+ if (!$title) {
+ $title = text::cutString(html::clean($content), 25) . '...';
+ }
+
+ $excerpt_xhtml = $this->core->callFormater('xhtml', $excerpt);
+ $content_xhtml = $this->core->callFormater('xhtml', $content);
+
+ if (empty($content)) {
+ throw new Exception('Cannot create an empty entry');
+ }
+
+ $cur = $this->core->con->openCursor($this->core->prefix . 'post');
+
+ $cur->post_type = $post_type;
+ $cur->post_title = trim($title);
+ $cur->post_content = $content;
+ $cur->post_excerpt = $excerpt;
+ $cur->post_content_xhtml = $content_xhtml;
+ $cur->post_excerpt_xhtml = $excerpt_xhtml;
+ $cur->post_open_comment = (integer) ($open_comment == 1);
+ $cur->post_open_tb = (integer) ($open_tb == 1);
+ $cur->post_status = (integer) $publish;
+ $cur->post_format = 'xhtml';
+ $cur->post_url = $post->post_url;
+
+ if ($dateCreated) {
+ if ($dateCreated instanceof xmlrpcDate) {
+ $cur->post_dt = date('Y-m-d H:i:00', $dateCreated->getTimestamp());
+ } elseif (is_string($dateCreated) && @strtotime($dateCreated)) {
+ $cur->post_dt = date('Y-m-d H:i:00', strtotime($dateCreated));
+ }
+ } else {
+ $cur->post_dt = $post->post_dt;
+ }
+
+ # Categories in an array
+ if (isset($struct['categories']) && is_array($struct['categories'])) {
+ $categories = $struct['categories'];
+ $cat_id = !empty($categories[0]) ? $categories[0] : null;
+
+ $cur->cat_id = $this->getCatID($cat_id);
+ }
+
+ if (isset($struct['wp_slug'])) {
+ $cur->post_url = $struct['wp_slug'];
+ }
+
+ if (isset($struct['wp_password'])) {
+ $cur->post_password = $struct['wp_password'];
+ }
+
+ if ($cur->post_type == 'post') {
+ # --BEHAVIOR-- xmlrpcBeforeEditPost
+ $this->core->callBehavior('xmlrpcBeforeEditPost', $this, $post_id, $cur, $content, $struct, $publish);
+
+ $this->core->blog->updPost($post_id, $cur);
+
+ # --BEHAVIOR-- xmlrpcAfterEditPost
+ $this->core->callBehavior('xmlrpcAfterEditPost', $this, $post_id, $cur, $content, $struct, $publish);
+ } elseif ($cur->post_type == 'page') {
+ if (isset($struct['wp_page_order'])) {
+ $cur->post_position = (integer) $struct['wp_page_order'];
+ }
+
+ $this->core->blog->settings->system->post_url_format = '{t}';
+
+ $this->core->blog->updPost($post_id, $cur);
+ } else {
+ throw new Exception('Invalid post type', 401);
+ }
+
+ return true;
+ }
+
+ private function getPost($post_id, $user, $pwd, $type = 'mw')
+ {
+ $post_id = (integer) $post_id;
+
+ $post = $this->getPostRS($post_id, $user, $pwd);
+
+ $res = new ArrayObject();
+
+ $res['dateCreated'] = new xmlrpcDate($post->getTS());
+ $res['userid'] = $post->user_id;
+ $res['postid'] = $post->post_id;
+
+ if ($post->cat_id) {
+ $res['categories'] = [$post->cat_url];
+ }
+
+ if ($type == 'blogger') {
+ $res['content'] = $post->post_content_xhtml;
+ }
+
+ if ($type == 'mt' || $type == 'mw') {
+ $res['title'] = $post->post_title;
+ }
+
+ if ($type == 'mw') {
+ $res['description'] = $post->post_content_xhtml;
+ $res['link'] = $res['permaLink'] = $post->getURL();
+ $res['mt_excerpt'] = $post->post_excerpt_xhtml;
+ $res['mt_text_more'] = '';
+ $res['mt_allow_comments'] = (integer) $post->post_open_comment;
+ $res['mt_allow_pings'] = (integer) $post->post_open_tb;
+ $res['mt_convert_breaks'] = '';
+ $res['mt_keywords'] = '';
+ }
+
+ # --BEHAVIOR-- xmlrpcGetPostInfo
+ $this->core->callBehavior('xmlrpcGetPostInfo', $this, $type, [&$res]);
+
+ return $res;
+ }
+
+ private function deletePost($post_id, $user, $pwd)
+ {
+ $post_id = (integer) $post_id;
+
+ $this->getPostRS($post_id, $user, $pwd);
+ $this->core->blog->delPost($post_id);
+
+ return true;
+ }
+
+ private function getRecentPosts($blog_id, $user, $pwd, $nb_post, $type = 'mw')
+ {
+ $this->setUser($user, $pwd);
+ $this->setBlog();
+
+ $nb_post = (integer) $nb_post;
+
+ if ($nb_post > 50) {
+ throw new Exception('Cannot retrieve more than 50 entries');
+ }
+
+ $params = [];
+ $params['limit'] = $nb_post;
+
+ $posts = $this->core->blog->getPosts($params);
+
+ $res = [];
+ while ($posts->fetch()) {
+ $tres = [];
+
+ $tres['dateCreated'] = new xmlrpcDate($posts->getTS());
+ $tres['userid'] = $posts->user_id;
+ $tres['postid'] = $posts->post_id;
+
+ if ($posts->cat_id) {
+ $tres['categories'] = [$posts->cat_url];
+ }
+
+ if ($type == 'blogger') {
+ $tres['content'] = $posts->post_content_xhtml;
+ }
+
+ if ($type == 'mt' || $type == 'mw') {
+ $tres['title'] = $posts->post_title;
+ }
+
+ if ($type == 'mw') {
+ $tres['description'] = $posts->post_content_xhtml;
+ $tres['link'] = $tres['permaLink'] = $posts->getURL();
+ $tres['mt_excerpt'] = $posts->post_excerpt_xhtml;
+ $tres['mt_text_more'] = '';
+ $tres['mt_allow_comments'] = (integer) $posts->post_open_comment;
+ $tres['mt_allow_pings'] = (integer) $posts->post_open_tb;
+ $tres['mt_convert_breaks'] = '';
+ $tres['mt_keywords'] = '';
+ }
+
+ # --BEHAVIOR-- xmlrpcGetPostInfo
+ $this->core->callBehavior('xmlrpcGetPostInfo', $this, $type, [&$tres]);
+
+ $res[] = $tres;
+ }
+
+ return $res;
+ }
+
+ private function getUserBlogs($user, $pwd)
+ {
+ $this->setUser($user, $pwd);
+ $this->setBlog();
+
+ return [[
+ 'url' => $this->core->blog->url,
+ 'blogid' => '1',
+ 'blogName' => $this->core->blog->name
+ ]];
+ }
+
+ private function getUserInfo($user, $pwd)
+ {
+ $this->setUser($user, $pwd);
+
+ return [
+ 'userid' => $this->core->auth->userID(),
+ 'firstname' => $this->core->auth->getInfo('user_firstname'),
+ 'lastname' => $this->core->auth->getInfo('user_name'),
+ 'nickname' => $this->core->auth->getInfo('user_displayname'),
+ 'email' => $this->core->auth->getInfo('user_email'),
+ 'url' => $this->core->auth->getInfo('user_url')
+ ];
+ }
+
+ private function getCategories($blog_id, $user, $pwd)
+ {
+ $this->setUser($user, $pwd);
+ $this->setBlog();
+ $rs = $this->core->blog->getCategories();
+
+ $res = [];
+
+ $l = $rs->level;
+ $stack = ['', $rs->cat_url];
+
+ while ($rs->fetch()) {
+ $d = $rs->level - $l;
+ if ($d == 0) {
+ array_pop($stack);
+ $parent = end($stack);
+ } elseif ($d > 0) {
+ $parent = end($stack);
+ } elseif ($d < 0) {
+ $D = abs($d);
+ for ($i = 0; $i <= $D; $i++) {
+ array_pop($stack);
+ }
+ $parent = end($stack);
+ }
+
+ $res[] = [
+ 'categoryId' => $rs->cat_url,
+ 'parentId' => $parent,
+ 'description' => $rs->cat_title,
+ 'categoryName' => $rs->cat_url,
+ 'htmlUrl' => $this->core->blog->url .
+ $this->core->url->getURLFor('category', $rs->cat_url),
+ 'rssUrl' => $this->core->blog->url .
+ $this->core->url->getURLFor('feed', 'category/' . $rs->cat_url . '/rss2')
+ ];
+
+ $stack[] = $rs->cat_url;
+ $l = $rs->level;
+ }
+
+ return $res;
+ }
+
+ private function getPostCategories($post_id, $user, $pwd)
+ {
+ $post_id = (integer) $post_id;
+
+ $post = $this->getPostRS($post_id, $user, $pwd);
+
+ return [
+ [
+ 'categoryName' => $post->cat_url,
+ 'categoryId' => (string) $post->cat_url,
+ 'isPrimary' => true
+ ]
+ ];
+ }
+
+ private function setPostCategories($post_id, $user, $pwd, $categories)
+ {
+ $post_id = (integer) $post_id;
+
+ $post = $this->getPostRS($post_id, $user, $pwd);
+
+ $cat_id = (!empty($categories[0]['categoryId'])) ? $categories[0]['categoryId'] : null;
+
+ foreach ($categories as $v) {
+ if (isset($v['isPrimary']) && $v['isPrimary']) {
+ $cat_id = $v['categoryId'];
+ break;
+ }
+ }
+
+ # w.bloggar sends -1 for no category.
+ if ($cat_id == -1) {
+ $cat_id = null;
+ }
+
+ if ($cat_id) {
+ $cat_id = $this->getCatID($cat_id);
+ }
+
+ $this->core->blog->updPostCategory($post_id, (integer) $cat_id);
+
+ return true;
+ }
+
+ private function publishPost($post_id, $user, $pwd)
+ {
+ $post_id = (integer) $post_id;
+
+ $this->getPostRS($post_id, $user, $pwd);
+
+ # --BEHAVIOR-- xmlrpcBeforePublishPost
+ $this->core->callBehavior('xmlrpcBeforePublishPost', $this, $post_id);
+
+ $this->core->blog->updPostStatus($post_id, 1);
+
+ # --BEHAVIOR-- xmlrpcAfterPublishPost
+ $this->core->callBehavior('xmlrpcAfterPublishPost', $this, $post_id);
+
+ return true;
+ }
+
+ private function newMediaObject($blog_id, $user, $pwd, $file)
+ {
+ if (empty($file['name'])) {
+ throw new Exception('No file name');
+ }
+
+ if (empty($file['bits'])) {
+ throw new Exception('No file content');
+ }
+
+ $file_name = $file['name'];
+ $file_bits = $file['bits'];
+
+ $this->setUser($user, $pwd);
+ $this->setBlog();
+
+ $media = new dcMedia($this->core);
+
+ $dir_name = path::clean(dirname($file_name));
+ $file_name = basename($file_name);
+
+ $dir_name = preg_replace('!^/!', '', $dir_name);
+ if ($dir_name != '') {
+ $dir = explode('/', $dir_name);
+ $cwd = './';
+ foreach ($dir as $v) {
+ $v = files::tidyFileName($v);
+ $cwd .= $v . '/';
+ $media->makeDir($v);
+ $media->chdir($cwd);
+ }
+ }
+
+ $media_id = $media->uploadBits($file_name, $file_bits);
+
+ $f = $media->getFile($media_id);
+ return [
+ 'file' => $file_name,
+ 'url' => $f->file_url,
+ 'type' => files::getMimeType($file_name)
+ ];
+ }
+
+ private function translateWpStatus($s)
+ {
+ $status = [
+ 'draft' => -2,
+ 'pending' => -2,
+ 'private' => 0,
+ 'publish' => 1,
+ 'scheduled' => -1
+ ];
+
+ if (is_int($s)) {
+ $status = array_flip($status);
+ return isset($status[$s]) ? $status[$s] : $status[-2];
+ } else {
+ return isset($status[$s]) ? $status[$s] : $status['pending'];
+ }
+ }
+
+ private function translateWpCommentstatus($s)
+ {
+ $status = [
+ 'hold' => -1,
+ 'approve' => 0,
+ 'spam' => -2
+ ];
+
+ if (is_int($s)) {
+ $status = array_flip($status);
+ return isset($status[$s]) ? $status[$s] : $status[0];
+ } else {
+ return isset($status[$s]) ? $status[$s] : $status['approve'];
+ }
+ }
+
+ private function translateWpOptions($options = [])
+ {
+ $timezone = 0;
+ if ($this->core->blog->settings->system->blog_timezone) {
+ $timezone = dt::getTimeOffset($this->core->blog->settings->system->blog_timezone) / 3600;
+ }
+
+ $res = [
+ 'software_name' => [
+ 'desc' => 'Software Name',
+ 'readonly' => true,
+ 'value' => 'Dotclear'
+ ],
+ 'software_version' => [
+ 'desc' => 'Software Version',
+ 'readonly' => true,
+ 'value' => DC_VERSION
+ ],
+ 'blog_url' => [
+ 'desc' => 'Blog URL',
+ 'readonly' => true,
+ 'value' => $this->core->blog->url
+ ],
+ 'time_zone' => [
+ 'desc' => 'Time Zone',
+ 'readonly' => true,
+ 'value' => (string) $timezone
+ ],
+ 'blog_title' => [
+ 'desc' => 'Blog Title',
+ 'readonly' => false,
+ 'value' => $this->core->blog->name
+ ],
+ 'blog_tagline' => [
+ 'desc' => 'Blog Tagline',
+ 'readonly' => false,
+ 'value' => $this->core->blog->desc
+ ],
+ 'date_format' => [
+ 'desc' => 'Date Format',
+ 'readonly' => false,
+ 'value' => $this->core->blog->settings->system->date_format
+ ],
+ 'time_format' => [
+ 'desc' => 'Time Format',
+ 'readonly' => false,
+ 'value' => $this->core->blog->settings->system->time_format
+ ]
+ ];
+
+ if (!empty($options)) {
+ $r = [];
+ foreach ($options as $v) {
+ if (isset($res[$v])) {
+ $r[$v] = $res[$v];
+ }
+ }
+ return $r;
+ }
+
+ return $res;
+ }
+
+ private function getPostStatusList($blog_id, $user, $pwd)
+ {
+ $this->setUser($user, $pwd);
+ $this->setBlog();
+
+ return [
+ 'draft' => 'Draft',
+ 'pending' => 'Pending Review',
+ 'private' => 'Private',
+ 'publish' => 'Published',
+ 'scheduled' => 'Scheduled'
+ ];
+ }
+
+ private function getPageStatusList($blog_id, $user, $pwd)
+ {
+ $this->setUser($user, $pwd);
+ $this->setBlog();
+ $this->checkPagesPermission();
+
+ return [
+ 'draft' => 'Draft',
+ 'private' => 'Private',
+ 'published' => 'Published',
+ 'scheduled' => 'Scheduled'
+ ];
+ }
+
+ private function checkPagesPermission()
+ {
+ if (!$this->core->plugins->moduleExists('pages')) {
+ throw new Exception('Pages management is not available on this blog.');
+ }
+
+ if (!$this->core->auth->check('pages,contentadmin', $this->core->blog->id)) {
+ throw new Exception('Not enough permissions to edit pages.', 401);
+ }
+ }
+
+ private function getPages($blog_id, $user, $pwd, $limit = null, $id = null)
+ {
+ $this->setUser($user, $pwd);
+ $this->setBlog();
+ $this->checkPagesPermission();
+
+ $params = [
+ 'post_type' => 'page',
+ 'order' => 'post_position ASC, post_title ASC'
+ ];
+
+ if ($id) {
+ $params['post_id'] = (integer) $id;
+ }
+ if ($limit) {
+ $params['limit'] = $limit;
+ }
+
+ $posts = $this->core->blog->getPosts($params);
+
+ $res = [];
+ while ($posts->fetch()) {
+ $tres = [
+ "dateCreated" => new xmlrpcDate($posts->getTS()),
+ "userid" => $posts->user_id,
+ "page_id" => $posts->post_id,
+ "page_status" => $this->translateWpStatus((integer) $posts->post_status),
+ "description" => $posts->post_content_xhtml,
+ "title" => $posts->post_title,
+ "link" => $posts->getURL(),
+ "permaLink" => $posts->getURL(),
+ "categories" => [],
+ "excerpt" => $posts->post_excerpt_xhtml,
+ "text_more" => '',
+ "mt_allow_comments" => (integer) $posts->post_open_comment,
+ "mt_allow_pings" => (integer) $posts->post_open_tb,
+ "wp_slug" => $posts->post_url,
+ "wp_password" => $posts->post_password,
+ "wp_author" => $posts->getAuthorCN(),
+ "wp_page_parent_id" => 0,
+ "wp_page_parent_title" => '',
+ "wp_page_order" => $posts->post_position,
+ "wp_author_id" => $posts->user_id,
+ "wp_author_display_name" => $posts->getAuthorCN(),
+ "date_created_gmt" => new xmlrpcDate(dt::iso8601($posts->getTS(), $posts->post_tz)),
+ "custom_fields" => [],
+ "wp_page_template" => 'default'
+ ];
+
+ # --BEHAVIOR-- xmlrpcGetPageInfo
+ $this->core->callBehavior('xmlrpcGetPageInfo', $this, [&$tres]);
+
+ $res[] = $tres;
+ }
+
+ return $res;
+ }
+
+ private function newPage($blog_id, $user, $pwd, $struct, $publish)
+ {
+ $this->setUser($user, $pwd);
+ $this->setBlog();
+ $this->checkPagesPermission();
+
+ $struct['post_type'] = 'page';
+
+ return $this->newPost($blog_id, $user, $pwd, null, $struct, $publish);
+ }
+
+ private function editPage($page_id, $user, $pwd, $struct, $publish)
+ {
+ $this->setUser($user, $pwd);
+ $this->setBlog();
+ $this->checkPagesPermission();
+
+ $struct['post_type'] = 'page';
+
+ return $this->editPost($page_id, $user, $pwd, null, $struct, $publish);
+ }
+
+ private function deletePage($page_id, $user, $pwd)
+ {
+ $this->setUser($user, $pwd);
+ $this->setBlog();
+ $this->checkPagesPermission();
+
+ $page_id = (integer) $page_id;
+
+ $this->getPostRS($page_id, $user, $pwd, 'page');
+ $this->core->blog->delPost($page_id);
+
+ return true;
+ }
+
+ private function getAuthors($user, $pwd)
+ {
+ $this->setUser($user, $pwd);
+ $this->setBlog();
+
+ $rs = $this->core->getBlogPermissions($this->core->blog->id);
+ $res = [];
+
+ foreach ($rs as $k => $v) {
+ $res[] = [
+ 'user_id' => $k,
+ 'user_login' => $k,
+ 'display_name' => dcUtils::getUserCN($k, $v['name'], $v['firstname'], $v['displayname'])
+ ];
+ }
+ return $res;
+ }
+
+ private function getTags($user, $pwd)
+ {
+ $this->setUser($user, $pwd);
+ $this->setBlog();
+
+ $tags = $this->core->meta->getMeta('tag');
+ $tags->sort('meta_id_lower', 'asc');
+
+ $res = [];
+ $url = $this->core->blog->url .
+ $this->core->url->getURLFor('tag', '%s');
+ $f_url = $this->core->blog->url .
+ $this->core->url->getURLFor('tag_feed', '%s');
+ while ($tags->fetch()) {
+ $res[] = [
+ 'tag_id' => $tags->meta_id,
+ 'name' => $tags->meta_id,
+ 'count' => $tags->count,
+ 'slug' => $tags->meta_id,
+ 'html_url' => sprintf($url, $tags->meta_id),
+ 'rss_url' => sprintf($f_url, $tags->meta_id)
+ ];
+ }
+ return $res;
+ }
+
+ private function newCategory($user, $pwd, $struct)
+ {
+ $this->setUser($user, $pwd);
+ $this->setBlog();
+
+ if (empty($struct['name'])) {
+ throw new Exception('You mus give a category name.');
+ }
+
+ $cur = $this->core->con->openCursor($this->core->prefix . 'category');
+ $cur->cat_title = $struct['name'];
+
+ if (!empty($struct['slug'])) {
+ $cur->cat_url = $struct['slug'];
+ }
+ if (!empty($struct['category_description'])) {
+ $cur->cat_desc = $struct['category_description'];
+ if (html::clean($cur->cat_desc) == $cur->cat_desc) {
+ $cur->cat_desc = '' . $cur->cat_desc . '
';
+ }
+ }
+
+ $parent = !empty($struct['category_parent']) ? (integer) $struct['category_parent'] : 0;
+
+ $id = $this->core->blog->addCategory($cur, $parent);
+ $rs = $this->core->blog->getCategory($id);
+ return $rs->cat_url;
+ }
+
+ private function deleteCategory($user, $pwd, $cat_id)
+ {
+ $this->setUser($user, $pwd);
+ $this->setBlog();
+
+ $c = $this->core->blog->getCategories(['cat_url' => $cat_id]);
+ if ($c->isEmpty()) {
+ throw new Exception(__('This category does not exist.'));
+ }
+ $cat_id = $c->cat_id;
+ unset($c);
+
+ $this->core->blog->delCategory((integer) $cat_id);
+ return true;
+ }
+
+ private function searchCategories($user, $pwd, $category, $limit)
+ {
+ $this->setUser($user, $pwd);
+ $this->setBlog();
+
+ $strReq = 'SELECT cat_id, cat_title, cat_url ' .
+ 'FROM ' . $this->core->prefix . 'category ' .
+ "WHERE blog_id = '" . $this->core->con->escape($this->core->blog->id) . "' " .
+ "AND LOWER(cat_title) LIKE LOWER('%" . $this->core->con->escape($category) . "%') " .
+ ($limit > 0 ? $this->core->con->limit($limit) : '');
+
+ $rs = $this->core->con->select($strReq);
+
+ $res = [];
+ while ($rs->fetch()) {
+ $res[] = [
+ 'category_id' => $rs->cat_url,
+ 'category_name' => $rs->cat_url
+ ];
+ }
+ return $res;
+ }
+
+ private function countComments($user, $pwd, $post_id)
+ {
+ $this->setUser($user, $pwd);
+ $this->setBlog();
+
+ $res = [
+ 'approved' => 0,
+ 'awaiting_moderation' => 0,
+ 'spam' => 0,
+ 'total' => 0
+ ];
+ $rs = $this->core->blog->getComments(['post_id' => $post_id]);
+
+ while ($rs->fetch()) {
+ $res['total']++;
+ if ($rs->comment_status == 1) {
+ $res['approved']++;
+ } elseif ($rs->comment_status == -2) {
+ $res['spam']++;
+ } else {
+ $res['awaiting_moderation']++;
+ }
+ }
+ return $res;
+ }
+
+ private function getComments($user, $pwd, $struct, $id = null)
+ {
+ $this->setUser($user, $pwd);
+ $this->setBlog();
+
+ $params = [];
+
+ if (!empty($struct['status'])) {
+ $params['comment_status'] = $this->translateWpCommentstatus($struct['status']);
+ }
+
+ if (!empty($struct['post_id'])) {
+ $params['post_id'] = (integer) $struct['post_id'];
+ }
+
+ if (isset($id)) {
+ $params['comment_id'] = $id;
+ }
+
+ $offset = !empty($struct['offset']) ? (integer) $struct['offset'] : 0;
+ $limit = !empty($struct['number']) ? (integer) $struct['number'] : 10;
+ $params['limit'] = [$offset, $limit];
+
+ $rs = $this->core->blog->getComments($params);
+ $res = [];
+ while ($rs->fetch()) {
+ $res[] = [
+ 'date_created_gmt' => new xmlrpcDate($rs->getTS()),
+ 'user_id' => $rs->user_id,
+ 'comment_id' => $rs->comment_id,
+ 'parent' => 0,
+ 'status' => $this->translateWpCommentstatus((integer) $rs->comment_status),
+ 'content' => $rs->comment_content,
+ 'link' => $rs->getPostURL() . '#c' . $rs->comment_id,
+ 'post_id' => $rs->post_id,
+ 'post_title' => $rs->post_title,
+ 'author' => $rs->comment_author,
+ 'author_url' => $rs->comment_site,
+ 'author_email' => $rs->comment_email,
+ 'author_ip' => $rs->comment_ip
+ ];
+ }
+ return $res;
+ }
+
+ private function addComment($user, $pwd, $post_id, $struct)
+ {
+ $this->setUser($user, $pwd);
+ $this->setBlog();
+
+ if (empty($struct['content'])) {
+ throw new Exception('Sorry, you cannot post an empty comment', 401);
+ }
+
+ if (is_numeric($post_id)) {
+ $p['post_id'] = $post_id;
+ } else {
+ $p['post_url'] = $post_id;
+ }
+ $rs = $this->core->blog->getPosts($p);
+ if ($rs->isEmpty()) {
+ throw new Exception('Sorry, no such post.', 404);
+ }
+
+ $cur = $this->core->con->openCursor($this->core->prefix . 'comment');
+
+ $cur->comment_author = $this->core->auth->getInfo('user_cn');
+ $cur->comment_email = $this->core->auth->getInfo('user_email');
+ $cur->comment_site = $this->core->auth->getInfo('user_url');
+
+ $cur->comment_content = $struct['content'];
+ $cur->post_id = (integer) $post_id;
+
+ $id = $this->core->blog->addComment($cur);
+ return $id;
+ }
+
+ private function updComment($user, $pwd, $comment_id, $struct)
+ {
+ $this->setUser($user, $pwd);
+ $this->setBlog();
+
+ $cur = $this->core->con->openCursor($this->core->prefix . 'comment');
+
+ if (isset($struct['status'])) {
+ $cur->comment_status = $this->translateWpCommentstatus($struct['status']);
+ }
+
+ if (isset($struct['date_created_gmt'])) {
+ if ($struct['date_created_gmt'] instanceof xmlrpcDate) {
+ $cur->comment_dt = date('Y-m-d H:i:00', $struct['date_created_gmt']->getTimestamp());
+ } elseif (is_string($struct['date_created_gmt']) && @strtotime($struct['date_created_gmt'])) {
+ $cur->comment_dt = date('Y-m-d H:i:00', strtotime($struct['date_created_gmt']));
+ }
+ $cur->comment_dt = $struct['date_created_gmt'];
+ }
+
+ if (isset($struct['content'])) {
+ $cur->comment_content = $struct['content'];
+ }
+
+ if (isset($struct['author'])) {
+ $cur->comment_author = $struct['author'];
+ }
+
+ if (isset($struct['author_url'])) {
+ $cur->comment_site = $struct['author_url'];
+ }
+
+ if (isset($struct['author_email'])) {
+ $cur->comment_email = $struct['author_email'];
+ }
+
+ $this->core->blog->updComment($comment_id, $cur);
+ return true;
+ }
+
+ private function delComment($user, $pwd, $comment_id)
+ {
+ $this->setUser($user, $pwd);
+ $this->setBlog();
+
+ $this->core->blog->delComment($comment_id);
+ return true;
+ }
+
+ /* Blogger methods
+ --------------------------------------------------- */
+ public function blogger_newPost($appkey, $blogid, $username, $password, $content, $publish)
+ {
+ return $this->newPost($blogid, $username, $password, $content, [], $publish);
+ }
+
+ public function blogger_editPost($appkey, $postid, $username, $password, $content, $publish)
+ {
+ return $this->editPost($postid, $username, $password, $content, [], $publish);
+ }
+
+ public function blogger_getPost($appkey, $postid, $username, $password)
+ {
+ return $this->getPost($postid, $username, $password, 'blogger');
+ }
+
+ public function blogger_deletePost($appkey, $postid, $username, $password, $publish)
+ {
+ return $this->deletePost($postid, $username, $password);
+ }
+
+ public function blogger_getRecentPosts($appkey, $blogid, $username, $password, $numberOfPosts)
+ {
+ return $this->getRecentPosts($blogid, $username, $password, $numberOfPosts, 'blogger');
+ }
+
+ public function blogger_getUserBlogs($appkey, $username, $password)
+ {
+ return $this->getUserBlogs($username, $password);
+ }
+
+ public function blogger_getUserInfo($appkey, $username, $password)
+ {
+ return $this->getUserInfo($username, $password);
+ }
+
+ /* Metaweblog methods
+ ------------------------------------------------------- */
+ public function mw_newPost($blogid, $username, $password, $content, $publish)
+ {
+ return $this->newPost($blogid, $username, $password, '', $content, $publish);
+ }
+
+ public function mw_editPost($postid, $username, $password, $content, $publish)
+ {
+ return $this->editPost($postid, $username, $password, '', $content, $publish);
+ }
+
+ public function mw_getPost($postid, $username, $password)
+ {
+ return $this->getPost($postid, $username, $password, 'mw');
+ }
+
+ public function mw_getRecentPosts($blogid, $username, $password, $numberOfPosts)
+ {
+ return $this->getRecentPosts($blogid, $username, $password, $numberOfPosts, 'mw');
+ }
+
+ public function mw_getCategories($blogid, $username, $password)
+ {
+ return $this->getCategories($blogid, $username, $password);
+ }
+
+ public function mw_newMediaObject($blogid, $username, $password, $file)
+ {
+ return $this->newMediaObject($blogid, $username, $password, $file);
+ }
+
+ /* MovableType methods
+ --------------------------------------------------- */
+ public function mt_getRecentPostTitles($blogid, $username, $password, $numberOfPosts)
+ {
+ return $this->getRecentPosts($blogid, $username, $password, $numberOfPosts, 'mt');
+ }
+
+ public function mt_getCategoryList($blogid, $username, $password)
+ {
+ return $this->getCategories($blogid, $username, $password);
+ }
+
+ public function mt_getPostCategories($postid, $username, $password)
+ {
+ return $this->getPostCategories($postid, $username, $password);
+ }
+
+ public function mt_setPostCategories($postid, $username, $password, $categories)
+ {
+ return $this->setPostCategories($postid, $username, $password, $categories);
+ }
+
+ public function mt_publishPost($postid, $username, $password)
+ {
+ return $this->publishPost($postid, $username, $password);
+ }
+
+ public function mt_supportedTextFilters()
+ {
+ return [];
+ }
+
+ /* WordPress methods
+ --------------------------------------------------- */
+ public function wp_getUsersBlogs($username, $password)
+ {
+ return $this->getUserBlogs($username, $password);
+ }
+
+ public function wp_getPage($blogid, $pageid, $username, $password)
+ {
+ $res = $this->getPages($blogid, $username, $password, null, $pageid);
+
+ if (empty($res)) {
+ throw new Exception('Sorry, no such page', 404);
+ }
+
+ return $res[0];
+ }
+
+ public function wp_getPages($blogid, $username, $password, $num = 10)
+ {
+ return $this->getPages($blogid, $username, $password, $num);
+ }
+
+ public function wp_newPage($blogid, $username, $password, $content, $publish)
+ {
+ return $this->newPage($blogid, $username, $password, $content, $publish);
+ }
+
+ public function wp_deletePage($blogid, $username, $password, $pageid)
+ {
+ return $this->deletePage($pageid, $username, $password);
+ }
+
+ public function wp_editPage($blogid, $pageid, $username, $password, $content, $publish)
+ {
+ return $this->editPage($pageid, $username, $password, $content, $publish);
+ }
+
+ public function wp_getPageList($blogid, $username, $password)
+ {
+ $A = $this->getPages($blogid, $username, $password);
+ $res = [];
+ foreach ($A as $v) {
+ $res[] = [
+ 'page_id' => $v['page_id'],
+ 'page_title' => $v['title'],
+ 'page_parent_id' => $v['wp_page_parent_id'],
+ 'dateCreated' => $v['dateCreated'],
+ 'date_created_gmt' => $v['date_created_gmt']
+ ];
+ }
+ return $res;
+ }
+
+ public function wp_getAuthors($blogid, $username, $password)
+ {
+ return $this->getAuthors($username, $password);
+ }
+
+ public function wp_getCategories($blogid, $username, $password)
+ {
+ return $this->getCategories($blogid, $username, $password);
+ }
+
+ public function wp_getTags($blogid, $username, $password)
+ {
+ return $this->getTags($username, $password);
+ }
+
+ public function wp_newCategory($blogid, $username, $password, $content)
+ {
+ return $this->newCategory($username, $password, $content);
+ }
+
+ public function wp_deleteCategory($blogid, $username, $password, $categoryid)
+ {
+ return $this->deleteCategory($username, $password, $categoryid);
+ }
+
+ public function wp_suggestCategories($blogid, $username, $password, $category, $max_results = 0)
+ {
+ return $this->searchCategories($username, $password, $category, $max_results);
+ }
+
+ public function wp_uploadFile($blogid, $username, $password, $file)
+ {
+ return $this->newMediaObject($blogid, $username, $password, $file);
+ }
+
+ public function wp_getPostStatusList($blogid, $username, $password)
+ {
+ return $this->getPostStatusList($blogid, $username, $password);
+ }
+
+ public function wp_getPageStatusList($blogid, $username, $password)
+ {
+ return $this->getPostStatusList($blogid, $username, $password);
+ }
+
+ public function wp_getPageTemplates($blogid, $username, $password)
+ {
+ return ['Default' => 'default'];
+ }
+
+ public function wp_getOptions($blogid, $username, $password, $options = [])
+ {
+ $this->setUser($username, $password);
+ $this->setBlog();
+
+ return $this->translateWpOptions($options);
+ }
+
+ public function wp_setOptions($blogid, $username, $password, $options)
+ {
+ $this->setUser($username, $password);
+ $this->setBlog();
+
+ if (!$this->core->auth->check('admin', $this->core->blog->id)) {
+ throw new Exception('Not enough permissions to edit options.', 401);
+ }
+
+ $opt = $this->translateWpOptions();
+
+ $done = [];
+ $blog_changes = false;
+ $cur = $this->core->con->openCursor($this->core->prefix . 'blog');
+
+ $this->core->blog->settings->addNamespace('system');
+
+ foreach ($options as $name => $value) {
+ if (!isset($opt[$name]) || $opt[$name]['readonly']) {
+ continue;
+ }
+
+ switch ($name) {
+ case 'blog_title':
+ $blog_changes = true;
+ $cur->blog_name = $value;
+ $done[] = $name;
+ break;
+ case 'blog_tagline':
+ $blog_changes = true;
+ $cur->blog_desc = $value;
+ $done[] = $name;
+ break;
+ case 'date_format':
+ $this->core->blog->settings->system->put('date_format', $value);
+ $done[] = $name;
+ break;
+ case 'time_format':
+ $this->core->blog->settings->system->put('time_format', $value);
+ $done[] = $name;
+ break;
+ }
+ }
+
+ if ($blog_changes) {
+ $this->core->updBlog($this->core->blog->id, $cur);
+ $this->core->setBlog($this->core->blog->id);
+ }
+
+ return $this->translateWpOptions($done);
+ }
+
+ public function wp_getComment($blogid, $username, $password, $commentid)
+ {
+ $res = $this->getComments($username, $password, [], $commentid);
+
+ if (empty($res)) {
+ throw new Exception('Sorry, no such comment', 404);
+ }
+
+ return $res[0];
+ }
+
+ public function wp_getCommentCount($blogid, $username, $password, $postid)
+ {
+ return $this->countComments($username, $password, $postid);
+ }
+
+ public function wp_getComments($blogid, $username, $password, $struct)
+ {
+ return $this->getComments($username, $password, $struct);
+ }
+
+ public function wp_deleteComment($blogid, $username, $password, $commentid)
+ {
+ return $this->delComment($username, $password, $commentid);
+ }
+
+ public function wp_editComment($blogid, $username, $password, $commentid, $content)
+ {
+ return $this->updComment($username, $password, $commentid, $content);
+ }
+
+ public function wp_newComment($blogid, $username, $password, $postid, $content)
+ {
+ return $this->addComment($username, $password, $postid, $content);
+ }
+
+ public function wp_getCommentStatusList($blogid, $username, $password)
+ {
+ $this->setUser($username, $password);
+ $this->setBlog();
+
+ return [
+ 'hold' => 'Unapproved',
+ 'approve' => 'Approved',
+ 'spam' => 'Spam'
+ ];
+ }
+
+ /* Pingback support
+ --------------------------------------------------- */
+ public function pingback_ping($from_url, $to_url)
+ {
+ dcTrackback::checkURLs($from_url, $to_url);
+
+ $args = ['type' => 'pingback', 'from_url' => $from_url, 'to_url' => $to_url];
+
+ # Time to get things done...
+ $this->setBlog(true);
+
+ # --BEHAVIOR-- publicBeforeReceiveTrackback
+ $this->core->callBehavior('publicBeforeReceiveTrackback', $this->core, $args);
+
+ $tb = new dcTrackback($this->core);
+ return $tb->receivePingback($from_url, $to_url);
+ }
+}
diff --git a/dotclear._no/inc/core_error.php b/dotclear._no/inc/core_error.php
new file mode 100644
index 0000000..74409f8
--- /dev/null
+++ b/dotclear._no/inc/core_error.php
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+ Dotclear - Error
+
+
+
+
+
+
Dotclear
+
+
+
+
diff --git a/dotclear._no/inc/css/print.css b/dotclear._no/inc/css/print.css
new file mode 100644
index 0000000..d32caf6
--- /dev/null
+++ b/dotclear._no/inc/css/print.css
@@ -0,0 +1,38 @@
+body {
+ font : 10pt serif;
+ margin: 0;
+ color: #000;
+ background: #fff;
+}
+
+#prelude, #sidebar, .pagination, #comment-form {
+ display: none;
+}
+
+p {
+ margin: 0.2em 0 0.8em 0;
+ line-height: 1.3em;
+}
+
+h1,h2,h3,h4,h5, h6 {
+ margin: 1em 0 0.2em 0;
+ font-weight: bold;
+}
+h1 { font-size: 160%; }
+h2 { font-size: 140%; }
+h3 { font-size: 120%; }
+h4 { font-size: 100%; }
+h5 { font-size: 90%; }
+h6 { font-size: 80%; }
+
+a {
+ color: #00f;
+ text-decoration: none;
+ border-bottom: 1px solid #999;
+}
+
+.post-content a[href^="http"]:after, #comments a[href^="http"]:after,
+#trackbacks a[href^="http"]:after {
+ content: " ("attr(href)") ";
+ color: #333;
+}
diff --git a/dotclear._no/inc/dbschema/db-schema.php b/dotclear._no/inc/dbschema/db-schema.php
new file mode 100644
index 0000000..cbd97ee
--- /dev/null
+++ b/dotclear._no/inc/dbschema/db-schema.php
@@ -0,0 +1,281 @@
+blog
+ ->blog_id('varchar', 32, false)
+ ->blog_uid('varchar', 32, false)
+ ->blog_creadt('timestamp', 0, false, 'now()')
+ ->blog_upddt('timestamp', 0, false, 'now()')
+ ->blog_url('varchar', 255, false)
+ ->blog_name('varchar', 255, false)
+ ->blog_desc('text', 0, true)
+ ->blog_status('smallint', 0, false, 1)
+
+ ->primary('pk_blog', 'blog_id')
+;
+
+$_s->category
+ ->cat_id('bigint', 0, false)
+ ->blog_id('varchar', 32, false)
+ ->cat_title('varchar', 255, false)
+ ->cat_url('varchar', 255, false)
+ ->cat_desc('text', 0, true)
+ ->cat_position('integer', 0, true, 0)
+ ->cat_lft('integer', 0, true)
+ ->cat_rgt('integer', 0, true)
+
+ ->primary('pk_category', 'cat_id')
+
+ ->unique('uk_cat_url', 'cat_url', 'blog_id')
+;
+
+$_s->session
+ ->ses_id('varchar', 40, false)
+ ->ses_time('integer', 0, false, 0)
+ ->ses_start('integer', 0, false, 0)
+ ->ses_value('text', 0, false)
+
+ ->primary('pk_session', 'ses_id')
+;
+
+$_s->setting
+ ->setting_id('varchar', 255, false)
+ ->blog_id('varchar', 32, true)
+ ->setting_ns('varchar', 32, false, "'system'")
+ ->setting_value('text', 0, true, null)
+ ->setting_type('varchar', 8, false, "'string'")
+ ->setting_label('text', 0, true)
+
+ ->unique('uk_setting', 'setting_ns', 'setting_id', 'blog_id')
+;
+
+$_s->user
+ ->user_id('varchar', 32, false)
+ ->user_super('smallint', 0, true)
+ ->user_status('smallint', 0, false, 1)
+ ->user_pwd('varchar', 255, false)
+ ->user_change_pwd('smallint', 0, false, 0)
+ ->user_recover_key('varchar', 32, true, null)
+ ->user_name('varchar', 255, true, null)
+ ->user_firstname('varchar', 255, true, null)
+ ->user_displayname('varchar', 255, true, null)
+ ->user_email('varchar', 255, true, null)
+ ->user_url('varchar', 255, true, null)
+ ->user_desc('text', 0, true)
+ ->user_default_blog('varchar', 32, true, null)
+ ->user_options('text', 0, true)
+ ->user_lang('varchar', 5, true, null)
+ ->user_tz('varchar', 128, false, "'UTC'")
+ ->user_post_status('smallint', 0, false, -2)
+ ->user_creadt('timestamp', 0, false, 'now()')
+ ->user_upddt('timestamp', 0, false, 'now()')
+
+ ->primary('pk_user', 'user_id')
+;
+
+$_s->permissions
+ ->user_id('varchar', 32, false)
+ ->blog_id('varchar', 32, false)
+ ->permissions('text', 0, true)
+
+ ->primary('pk_permissions', 'user_id', 'blog_id')
+;
+
+$_s->post
+ ->post_id('bigint', 0, false)
+ ->blog_id('varchar', 32, false)
+ ->user_id('varchar', 32, false)
+ ->cat_id('bigint', 0, true)
+ ->post_dt('timestamp', 0, false, 'now()')
+ ->post_tz('varchar', 128, false, "'UTC'")
+ ->post_creadt('timestamp', 0, false, 'now()')
+ ->post_upddt('timestamp', 0, false, 'now()')
+ ->post_password('varchar', 32, true, null)
+ ->post_type('varchar', 32, false, "'post'")
+ ->post_format('varchar', 32, false, "'xhtml'")
+ ->post_url('varchar', 255, false)
+ ->post_lang('varchar', 5, true, null)
+ ->post_title('varchar', 255, true, null)
+ ->post_excerpt('text', 0, true, null)
+ ->post_excerpt_xhtml('text', 0, true, null)
+ ->post_content('text', 0, true, null)
+ ->post_content_xhtml('text', 0, false)
+ ->post_notes('text', 0, true, null)
+ ->post_meta('text', 0, true, null)
+ ->post_words('text', 0, true, null)
+ ->post_status('smallint', 0, false, 0)
+ ->post_firstpub('smallint', 0, false, 0)
+ ->post_selected('smallint', 0, false, 0)
+ ->post_position('integer', 0, false, 0)
+ ->post_open_comment('smallint', 0, false, 0)
+ ->post_open_tb('smallint', 0, false, 0)
+ ->nb_comment('integer', 0, false, 0)
+ ->nb_trackback('integer', 0, false, 0)
+
+ ->primary('pk_post', 'post_id')
+
+ ->unique('uk_post_url', 'post_url', 'post_type', 'blog_id')
+;
+
+$_s->media
+ ->media_id('bigint', 0, false)
+ ->user_id('varchar', 32, false)
+ ->media_path('varchar', 255, false)
+ ->media_title('varchar', 255, false)
+ ->media_file('varchar', 255, false)
+ ->media_dir('varchar', 255, false, "'.'")
+ ->media_meta('text', 0, true, null)
+ ->media_dt('timestamp', 0, false, 'now()')
+ ->media_creadt('timestamp', 0, false, 'now()')
+ ->media_upddt('timestamp', 0, false, 'now()')
+ ->media_private('smallint', 0, false, 0)
+
+ ->primary('pk_media', 'media_id')
+;
+
+$_s->post_media
+ ->media_id('bigint', 0, false)
+ ->post_id('bigint', 0, false)
+ ->link_type('varchar', 32, false, "'attachment'")
+
+ ->primary('pk_post_media', 'media_id', 'post_id', 'link_type')
+;
+
+$_s->log
+ ->log_id('bigint', 0, false)
+ ->user_id('varchar', 32, true)
+ ->blog_id('varchar', 32, true)
+ ->log_table('varchar', 255, false)
+ ->log_dt('timestamp', 0, false, 'now()')
+ ->log_ip('varchar', 39, false)
+ ->log_msg('text', 0, true, null)
+
+ ->primary('pk_log', 'log_id')
+;
+
+$_s->version
+ ->module('varchar', 64, false)
+ ->version('varchar', 32, false)
+
+ ->primary('pk_version', 'module')
+;
+
+$_s->ping
+ ->post_id('bigint', 0, false)
+ ->ping_url('varchar', 255, false)
+ ->ping_dt('timestamp', 0, false, 'now()')
+
+ ->primary('pk_ping', 'post_id', 'ping_url')
+;
+
+$_s->comment
+ ->comment_id('bigint', 0, false)
+ ->post_id('bigint', 0, false)
+ ->comment_dt('timestamp', 0, false, 'now()')
+ ->comment_tz('varchar', 128, false, "'UTC'")
+ ->comment_upddt('timestamp', 0, false, 'now()')
+ ->comment_author('varchar', 255, true, null)
+ ->comment_email('varchar', 255, true, null)
+ ->comment_site('varchar', 255, true, null)
+ ->comment_content('text', 0, true)
+ ->comment_words('text', 0, true, null)
+ ->comment_ip('varchar', 39, true, null)
+ ->comment_status('smallint', 0, true, 0)
+ ->comment_spam_status('varchar', 128, true, 0)
+ ->comment_spam_filter('varchar', 32, true, null)
+ ->comment_trackback('smallint', 0, false, 0)
+
+ ->primary('pk_comment', 'comment_id')
+;
+
+$_s->meta
+ ->meta_id('varchar', 255, false)
+ ->meta_type('varchar', 64, false)
+ ->post_id('bigint', 0, false)
+
+ ->primary('pk_meta', 'meta_id', 'meta_type', 'post_id')
+;
+
+$_s->pref
+ ->pref_id('varchar', 255, false)
+ ->user_id('varchar', 32, true)
+ ->pref_ws('varchar', 32, false, "'system'")
+ ->pref_value('text', 0, true, null)
+ ->pref_type('varchar', 8, false, "'string'")
+ ->pref_label('text', 0, true)
+
+ ->unique('uk_pref', 'pref_ws', 'pref_id', 'user_id')
+;
+
+/* References indexes
+-------------------------------------------------------- */
+$_s->category->index('idx_category_blog_id', 'btree', 'blog_id');
+$_s->category->index('idx_category_cat_lft_blog_id', 'btree', 'blog_id', 'cat_lft');
+$_s->category->index('idx_category_cat_rgt_blog_id', 'btree', 'blog_id', 'cat_rgt');
+$_s->setting->index('idx_setting_blog_id', 'btree', 'blog_id');
+$_s->user->index('idx_user_user_default_blog', 'btree', 'user_default_blog');
+$_s->permissions->index('idx_permissions_blog_id', 'btree', 'blog_id');
+$_s->post->index('idx_post_cat_id', 'btree', 'cat_id');
+$_s->post->index('idx_post_user_id', 'btree', 'user_id');
+$_s->post->index('idx_post_blog_id', 'btree', 'blog_id');
+$_s->media->index('idx_media_user_id', 'btree', 'user_id');
+$_s->post_media->index('idx_post_media_post_id', 'btree', 'post_id');
+$_s->post_media->index('idx_post_media_media_id', 'btree', 'media_id');
+$_s->log->index('idx_log_user_id', 'btree', 'user_id');
+$_s->comment->index('idx_comment_post_id', 'btree', 'post_id');
+$_s->meta->index('idx_meta_post_id', 'btree', 'post_id');
+$_s->meta->index('idx_meta_meta_type', 'btree', 'meta_type');
+$_s->pref->index('idx_pref_user_id', 'btree', 'user_id');
+
+/* Performance indexes
+-------------------------------------------------------- */
+$_s->comment->index('idx_comment_post_id_dt_status', 'btree', 'post_id', 'comment_dt', 'comment_status');
+$_s->post->index('idx_post_post_dt', 'btree', 'post_dt');
+$_s->post->index('idx_post_post_dt_post_id', 'btree', 'post_dt', 'post_id');
+$_s->post->index('idx_blog_post_post_dt_post_id', 'btree', 'blog_id', 'post_dt', 'post_id');
+$_s->post->index('idx_blog_post_post_status', 'btree', 'blog_id', 'post_status');
+$_s->blog->index('idx_blog_blog_upddt', 'btree', 'blog_upddt');
+$_s->user->index('idx_user_user_super', 'btree', 'user_super');
+
+/* Foreign keys
+-------------------------------------------------------- */
+$_s->category->reference('fk_category_blog', 'blog_id', 'blog', 'blog_id', 'cascade', 'cascade');
+$_s->setting->reference('fk_setting_blog', 'blog_id', 'blog', 'blog_id', 'cascade', 'cascade');
+$_s->user->reference('fk_user_default_blog', 'user_default_blog', 'blog', 'blog_id', 'cascade', 'set null');
+$_s->permissions->reference('fk_permissions_blog', 'blog_id', 'blog', 'blog_id', 'cascade', 'cascade');
+$_s->permissions->reference('fk_permissions_user', 'user_id', 'user', 'user_id', 'cascade', 'cascade');
+$_s->post->reference('fk_post_category', 'cat_id', 'category', 'cat_id', 'cascade', 'set null');
+$_s->post->reference('fk_post_user', 'user_id', 'user', 'user_id', 'cascade', 'cascade');
+$_s->post->reference('fk_post_blog', 'blog_id', 'blog', 'blog_id', 'cascade', 'cascade');
+$_s->media->reference('fk_media_user', 'user_id', 'user', 'user_id', 'cascade', 'cascade');
+$_s->post_media->reference('fk_media', 'media_id', 'media', 'media_id', 'cascade', 'cascade');
+$_s->post_media->reference('fk_media_post', 'post_id', 'post', 'post_id', 'cascade', 'cascade');
+$_s->ping->reference('fk_ping_post', 'post_id', 'post', 'post_id', 'cascade', 'cascade');
+$_s->comment->reference('fk_comment_post', 'post_id', 'post', 'post_id', 'cascade', 'cascade');
+$_s->log->reference('fk_log_blog', 'blog_id', 'blog', 'blog_id', 'cascade', 'set null');
+$_s->meta->reference('fk_meta_post', 'post_id', 'post', 'post_id', 'cascade', 'cascade');
+$_s->pref->reference('fk_pref_user', 'user_id', 'user', 'user_id', 'cascade', 'cascade');
+
+/* PostgreSQL specific indexes
+-------------------------------------------------------- */
+if ($_s->driver() == 'pgsql') {
+ $_s->setting->index('idx_setting_blog_id_null', 'btree', '(blog_id IS NULL)');
+ $_s->media->index('idx_media_media_path', 'btree', 'media_path', 'media_dir');
+ $_s->pref->index('idx_pref_user_id_null', 'btree', '(user_id IS NULL)');
+}
diff --git a/dotclear._no/inc/dbschema/upgrade-cli.php b/dotclear._no/inc/dbschema/upgrade-cli.php
new file mode 100644
index 0000000..f79c9c4
--- /dev/null
+++ b/dotclear._no/inc/dbschema/upgrade-cli.php
@@ -0,0 +1,49 @@
+#!/usr/bin/env php
+con->begin();
+ try {
+ $changes = dcUpgrade::dotclearUpgrade($core);
+ } catch (Exception $e) {
+ $core->con->rollback();
+ throw $e;
+ }
+ $core->con->commit();
+ echo 'Upgrade process successfully completed (' . $changes . "). \n";
+ exit(0);
+} catch (Exception $e) {
+ echo $e->getMessage() . "\n";
+ exit(1);
+}
+?>
diff --git a/dotclear._no/inc/dbschema/upgrade.php b/dotclear._no/inc/dbschema/upgrade.php
new file mode 100644
index 0000000..79485c1
--- /dev/null
+++ b/dotclear._no/inc/dbschema/upgrade.php
@@ -0,0 +1,831 @@
+getVersion('core');
+
+ if ($version === null) {
+ return false;
+ }
+
+ if (version_compare($version, DC_VERSION, '<') == 1 || strpos(DC_VERSION, 'dev')) {
+ try
+ {
+ if ($core->con->driver() == 'sqlite') {
+ return false; // Need to find a way to upgrade sqlite database
+ }
+
+ # Database upgrade
+ $_s = new dbStruct($core->con, $core->prefix);
+ require dirname(__FILE__) . '/db-schema.php';
+
+ $si = new dbStruct($core->con, $core->prefix);
+ $changes = $si->synchronize($_s);
+
+ /* Some other upgrades
+ ------------------------------------ */
+ $cleanup_sessions = self::growUp($core, $version);
+
+ # Drop content from session table if changes or if needed
+ if ($changes != 0 || $cleanup_sessions) {
+ $core->con->execute('DELETE FROM ' . $core->prefix . 'session ');
+ }
+
+ # Empty templates cache directory
+ try {
+ $core->emptyTemplatesCache();
+ } catch (Exception $e) {}
+
+ return $changes;
+ } catch (Exception $e) {
+ throw new Exception(__('Something went wrong with auto upgrade:') .
+ ' ' . $e->getMessage());
+ }
+ }
+
+ # No upgrade?
+ return false;
+ }
+
+ public static function growUp($core, $version)
+ {
+ if ($version === null) {
+ return false;
+ }
+
+ $cleanup_sessions = false; // update it in a step that needed sessions to be removed
+
+ # Populate media_dir field (since 2.0-beta3.3)
+ if (version_compare($version, '2.0-beta3.3', '<')) {
+ $strReq = 'SELECT media_id, media_file FROM ' . $core->prefix . 'media ';
+ $rs_m = $core->con->select($strReq);
+ while ($rs_m->fetch()) {
+ $cur = $core->con->openCursor($core->prefix . 'media');
+ $cur->media_dir = dirname($rs_m->media_file);
+ $cur->update('WHERE media_id = ' . (integer) $rs_m->media_id);
+ }
+ }
+
+ if (version_compare($version, '2.0-beta7.3', '<')) {
+ # Blowup becomes default theme
+ $strReq = 'UPDATE ' . $core->prefix . 'setting ' .
+ "SET setting_value = '%s' " .
+ "WHERE setting_id = 'theme' " .
+ "AND setting_value = '%s' " .
+ 'AND blog_id IS NOT NULL ';
+ $core->con->execute(sprintf($strReq, 'blueSilence', 'default'));
+ $core->con->execute(sprintf($strReq, 'default', 'blowup'));
+ }
+
+ if (version_compare($version, '2.1-alpha2-r2383', '<')) {
+ $schema = dbSchema::init($core->con);
+ $schema->dropUnique($core->prefix . 'category', $core->prefix . 'uk_cat_title');
+
+ # Reindex categories
+ $rs = $core->con->select(
+ 'SELECT cat_id, cat_title, blog_id ' .
+ 'FROM ' . $core->prefix . 'category ' .
+ 'ORDER BY blog_id ASC , cat_position ASC '
+ );
+ $cat_blog = $rs->blog_id;
+ $i = 2;
+ while ($rs->fetch()) {
+ if ($cat_blog != $rs->blog_id) {
+ $i = 2;
+ }
+ $core->con->execute(
+ 'UPDATE ' . $core->prefix . 'category SET '
+ . 'cat_lft = ' . ($i++) . ', cat_rgt = ' . ($i++) . ' ' .
+ 'WHERE cat_id = ' . (integer) $rs->cat_id
+ );
+ $cat_blog = $rs->blog_id;
+ }
+ }
+
+ if (version_compare($version, '2.1.6', '<=')) {
+ # ie7js has been upgraded
+ $ie7files = [
+ 'ie7-base64.php ',
+ 'ie7-content.htc',
+ 'ie7-core.js',
+ 'ie7-css2-selectors.js',
+ 'ie7-css3-selectors.js',
+ 'ie7-css-strict.js',
+ 'ie7-dhtml.js',
+ 'ie7-dynamic-attributes.js',
+ 'ie7-fixed.js',
+ 'ie7-graphics.js',
+ 'ie7-html4.js',
+ 'ie7-ie5.js',
+ 'ie7-layout.js',
+ 'ie7-load.htc',
+ 'ie7-object.htc',
+ 'ie7-overflow.js',
+ 'ie7-quirks.js',
+ 'ie7-server.css',
+ 'ie7-standard-p.js',
+ 'ie7-xml-extras.js'
+ ];
+ foreach ($ie7files as $f) {
+ @unlink(DC_ROOT . '/admin/js/ie7/' . $f);
+ }
+ }
+
+ if (version_compare($version, '2.2-alpha1-r3043', '<')) {
+ # metadata has been integrated to the core.
+ $core->plugins->loadModules(DC_PLUGINS_ROOT);
+ if ($core->plugins->moduleExists('metadata')) {
+ $core->plugins->deleteModule('metadata');
+ }
+
+ # Tags template class has been renamed
+ $sqlstr =
+ 'SELECT blog_id, setting_id, setting_value ' .
+ 'FROM ' . $core->prefix . 'setting ' .
+ 'WHERE (setting_id = \'widgets_nav\' OR setting_id = \'widgets_extra\') ' .
+ 'AND setting_ns = \'widgets\';';
+ $rs = $core->con->select($sqlstr);
+ while ($rs->fetch()) {
+ $widgetsettings = base64_decode($rs->setting_value);
+ $widgetsettings = str_replace('s:11:"tplMetadata"', 's:7:"tplTags"', $widgetsettings);
+ $cur = $core->con->openCursor($core->prefix . 'setting');
+ $cur->setting_value = base64_encode($widgetsettings);
+ $sqlstr = 'WHERE setting_id = \'' . $rs->setting_id . '\' AND setting_ns = \'widgets\' ' .
+ 'AND blog_id ' .
+ ($rs->blog_id == null ? 'is NULL' : '= \'' . $core->con->escape($rs->blog_id) . '\'');
+ $cur->update($sqlstr);
+ }
+ }
+
+ if (version_compare($version, '2.3', '<')) {
+ # Add global favorites
+ $init_fav = [];
+
+ $init_fav['new_post'] = ['new_post', 'New entry', 'post.php',
+ 'images/menu/edit.png', 'images/menu/edit-b.png',
+ 'usage,contentadmin', null, null];
+ $init_fav['newpage'] = ['newpage', 'New page', 'plugin.php?p=pages&act=page',
+ 'index.php?pf=pages/icon-np.png', 'index.php?pf=pages/icon-np-big.png',
+ 'contentadmin,pages', null, null];
+ $init_fav['media'] = ['media', 'Media manager', 'media.php',
+ 'images/menu/media.png', 'images/menu/media-b.png',
+ 'media,media_admin', null, null];
+ $init_fav['widgets'] = ['widgets', 'Presentation widgets', 'plugin.php?p=widgets',
+ 'index.php?pf=widgets/icon.png', 'index.php?pf=widgets/icon-big.png',
+ 'admin', null, null];
+ $init_fav['blog_theme'] = ['blog_theme', 'Blog appearance', 'blog_theme.php',
+ 'images/menu/themes.png', 'images/menu/blog-theme-b.png',
+ 'admin', null, null];
+
+ $count = 0;
+ foreach ($init_fav as $k => $f) {
+ $t = ['name' => $f[0], 'title' => $f[1], 'url' => $f[2], 'small-icon' => $f[3],
+ 'large-icon' => $f[4], 'permissions' => $f[5], 'id' => $f[6], 'class' => $f[7]];
+ $sqlstr = 'INSERT INTO ' . $core->prefix . 'pref (pref_id, user_id, pref_ws, pref_value, pref_type, pref_label) VALUES (' .
+ '\'' . sprintf("g%03s", $count) . '\',NULL,\'favorites\',\'' . serialize($t) . '\',\'string\',NULL);';
+ $core->con->execute($sqlstr);
+ $count++;
+ }
+
+ # A bit of housecleaning for no longer needed files
+ $remfiles = [
+ 'admin/style/cat-bg.png',
+ 'admin/style/footer-bg.png',
+ 'admin/style/head-logo.png',
+ 'admin/style/tab-bg.png',
+ 'admin/style/tab-c-l.png',
+ 'admin/style/tab-c-r.png',
+ 'admin/style/tab-l-l.png',
+ 'admin/style/tab-l-r.png',
+ 'admin/style/tab-n-l.png',
+ 'admin/style/tab-n-r.png',
+ 'inc/clearbricks/_common.php',
+ 'inc/clearbricks/common/lib.crypt.php',
+ 'inc/clearbricks/common/lib.date.php',
+ 'inc/clearbricks/common/lib.files.php',
+ 'inc/clearbricks/common/lib.form.php',
+ 'inc/clearbricks/common/lib.html.php',
+ 'inc/clearbricks/common/lib.http.php',
+ 'inc/clearbricks/common/lib.l10n.php',
+ 'inc/clearbricks/common/lib.text.php',
+ 'inc/clearbricks/common/tz.dat',
+ 'inc/clearbricks/common/_main.php',
+ 'inc/clearbricks/dblayer/class.cursor.php',
+ 'inc/clearbricks/dblayer/class.mysql.php',
+ 'inc/clearbricks/dblayer/class.pgsql.php',
+ 'inc/clearbricks/dblayer/class.sqlite.php',
+ 'inc/clearbricks/dblayer/dblayer.php',
+ 'inc/clearbricks/dbschema/class.dbschema.php',
+ 'inc/clearbricks/dbschema/class.dbstruct.php',
+ 'inc/clearbricks/dbschema/class.mysql.dbschema.php',
+ 'inc/clearbricks/dbschema/class.pgsql.dbschema.php',
+ 'inc/clearbricks/dbschema/class.sqlite.dbschema.php',
+ 'inc/clearbricks/diff/lib.diff.php',
+ 'inc/clearbricks/diff/lib.unified.diff.php',
+ 'inc/clearbricks/filemanager/class.filemanager.php',
+ 'inc/clearbricks/html.filter/class.html.filter.php',
+ 'inc/clearbricks/html.validator/class.html.validator.php',
+ 'inc/clearbricks/image/class.image.meta.php',
+ 'inc/clearbricks/image/class.image.tools.php',
+ 'inc/clearbricks/mail/class.mail.php',
+ 'inc/clearbricks/mail/class.socket.mail.php',
+ 'inc/clearbricks/net/class.net.socket.php',
+ 'inc/clearbricks/net.http/class.net.http.php',
+ 'inc/clearbricks/net.http.feed/class.feed.parser.php',
+ 'inc/clearbricks/net.http.feed/class.feed.reader.php',
+ 'inc/clearbricks/net.xmlrpc/class.net.xmlrpc.php',
+ 'inc/clearbricks/pager/class.pager.php',
+ 'inc/clearbricks/rest/class.rest.php',
+ 'inc/clearbricks/session.db/class.session.db.php',
+ 'inc/clearbricks/template/class.template.php',
+ 'inc/clearbricks/text.wiki2xhtml/class.wiki2xhtml.php',
+ 'inc/clearbricks/url.handler/class.url.handler.php',
+ 'inc/clearbricks/zip/class.unzip.php',
+ 'inc/clearbricks/zip/class.zip.php',
+ 'themes/default/tpl/.htaccess',
+ 'themes/default/tpl/404.html',
+ 'themes/default/tpl/archive.html',
+ 'themes/default/tpl/archive_month.html',
+ 'themes/default/tpl/category.html',
+ 'themes/default/tpl/home.html',
+ 'themes/default/tpl/post.html',
+ 'themes/default/tpl/search.html',
+ 'themes/default/tpl/tag.html',
+ 'themes/default/tpl/tags.html',
+ 'themes/default/tpl/user_head.html',
+ 'themes/default/tpl/_flv_player.html',
+ 'themes/default/tpl/_footer.html',
+ 'themes/default/tpl/_head.html',
+ 'themes/default/tpl/_mp3_player.html',
+ 'themes/default/tpl/_top.html'
+ ];
+ $remfolders = [
+ 'inc/clearbricks/common',
+ 'inc/clearbricks/dblayer',
+ 'inc/clearbricks/dbschema',
+ 'inc/clearbricks/diff',
+ 'inc/clearbricks/filemanager',
+ 'inc/clearbricks/html.filter',
+ 'inc/clearbricks/html.validator',
+ 'inc/clearbricks/image',
+ 'inc/clearbricks/mail',
+ 'inc/clearbricks/net',
+ 'inc/clearbricks/net.http',
+ 'inc/clearbricks/net.http.feed',
+ 'inc/clearbricks/net.xmlrpc',
+ 'inc/clearbricks/pager',
+ 'inc/clearbricks/rest',
+ 'inc/clearbricks/session.db',
+ 'inc/clearbricks/template',
+ 'inc/clearbricks/text.wiki2xhtml',
+ 'inc/clearbricks/url.handler',
+ 'inc/clearbricks/zip',
+ 'inc/clearbricks',
+ 'themes/default/tpl'
+ ];
+
+ foreach ($remfiles as $f) {
+ @unlink(DC_ROOT . '/' . $f);
+ }
+ foreach ($remfolders as $f) {
+ @rmdir(DC_ROOT . '/' . $f);
+ }
+ }
+
+ if (version_compare($version, '2.3.1', '<')) {
+ # Remove unecessary file
+ @unlink(DC_ROOT . '/' . 'inc/libs/clearbricks/.hgignore');
+ }
+
+ if (version_compare($version, '2.5', '<=')) {
+ # Try to disable daInstaller plugin if it has been installed outside the default plugins directory
+ $path = explode(PATH_SEPARATOR, DC_PLUGINS_ROOT);
+ $default = path::real(dirname(__FILE__) . '/../../plugins/');
+ foreach ($path as $root) {
+ if (!is_dir($root) || !is_readable($root)) {
+ continue;
+ }
+ if (substr($root, -1) != '/') {
+ $root .= '/';
+ }
+ if (($p = @dir($root)) === false) {
+ continue;
+ }
+ if (path::real($root) == $default) {
+ continue;
+ }
+ if (($d = @dir($root . 'daInstaller')) === false) {
+ continue;
+ }
+ $f = $root . '/daInstaller/_disabled';
+ if (!file_exists($f)) {
+ @file_put_contents($f, '');
+ }
+ }
+ }
+
+ if (version_compare($version, '2.5.1', '<=')) {
+ // Flash enhanced upload no longer needed
+ @unlink(DC_ROOT . '/' . 'inc/swf/swfupload.swf');
+ }
+
+ if (version_compare($version, '2.6', '<=')) {
+ // README has been replaced by README.md and CONTRIBUTING.md
+ @unlink(DC_ROOT . '/' . 'README');
+
+ // trackbacks are now merged into posts
+ @unlink(DC_ROOT . '/' . 'admin/trackbacks.php');
+
+ # daInstaller has been integrated to the core.
+ # Try to remove it
+ $path = explode(PATH_SEPARATOR, DC_PLUGINS_ROOT);
+ foreach ($path as $root) {
+ if (!is_dir($root) || !is_readable($root)) {
+ continue;
+ }
+ if (substr($root, -1) != '/') {
+ $root .= '/';
+ }
+ if (($p = @dir($root)) === false) {
+ continue;
+ }
+ if (($d = @dir($root . 'daInstaller')) === false) {
+ continue;
+ }
+ files::deltree($root . '/daInstaller');
+ }
+
+ # Some settings change, prepare db queries
+ $strReqFormat = 'INSERT INTO ' . $core->prefix . 'setting';
+ $strReqFormat .= ' (setting_id,setting_ns,setting_value,setting_type,setting_label)';
+ $strReqFormat .= ' VALUES(\'%s\',\'system\',\'%s\',\'string\',\'%s\')';
+
+ $strReqSelect = 'SELECT count(1) FROM ' . $core->prefix . 'setting';
+ $strReqSelect .= ' WHERE setting_id = \'%s\'';
+ $strReqSelect .= ' AND setting_ns = \'system\'';
+ $strReqSelect .= ' AND blog_id IS NULL';
+
+ # Add date and time formats
+ $date_formats = ['%Y-%m-%d', '%m/%d/%Y', '%d/%m/%Y', '%Y/%m/%d', '%d.%m.%Y', '%b %e %Y', '%e %b %Y', '%Y %b %e',
+ '%a, %Y-%m-%d', '%a, %m/%d/%Y', '%a, %d/%m/%Y', '%a, %Y/%m/%d', '%B %e, %Y', '%e %B, %Y', '%Y, %B %e', '%e. %B %Y',
+ '%A, %B %e, %Y', '%A, %e %B, %Y', '%A, %Y, %B %e', '%A, %Y, %B %e', '%A, %e. %B %Y'];
+ $time_formats = ['%H:%M', '%I:%M', '%l:%M', '%Hh%M', '%Ih%M', '%lh%M'];
+ if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
+ $date_formats = array_map(function ($f) {return str_replace('%e', '%#d', $f);}, $date_formats);
+ }
+
+ $rs = $core->con->select(sprintf($strReqSelect, 'date_formats'));
+ if ($rs->f(0) == 0) {
+ $strReq = sprintf($strReqFormat, 'date_formats', serialize($date_formats), 'Date formats examples');
+ $core->con->execute($strReq);
+ }
+ $rs = $core->con->select(sprintf($strReqSelect, 'time_formats'));
+ if ($rs->f(0) == 0) {
+ $strReq = sprintf($strReqFormat, 'time_formats', serialize($time_formats), 'Time formats examples');
+ $core->con->execute($strReq);
+ }
+
+ # Add repository URL for themes and plugins as daInstaller move to core
+ $rs = $core->con->select(sprintf($strReqSelect, 'store_plugin_url'));
+ if ($rs->f(0) == 0) {
+ $strReq = sprintf($strReqFormat, 'store_plugin_url', 'http://update.dotaddict.org/dc2/plugins.xml', 'Plugins XML feed location');
+ $core->con->execute($strReq);
+ }
+ $rs = $core->con->select(sprintf($strReqSelect, 'store_theme_url'));
+ if ($rs->f(0) == 0) {
+ $strReq = sprintf($strReqFormat, 'store_theme_url', 'http://update.dotaddict.org/dc2/themes.xml', 'Themes XML feed location');
+ $core->con->execute($strReq);
+ }
+ }
+
+ if (version_compare($version, '2.7', '<=')) {
+ # Some new settings should be initialized, prepare db queries
+ $strReqFormat = 'INSERT INTO ' . $core->prefix . 'setting';
+ $strReqFormat .= ' (setting_id,setting_ns,setting_value,setting_type,setting_label)';
+ $strReqFormat .= ' VALUES(\'%s\',\'system\',\'%s\',\'string\',\'%s\')';
+
+ $strReqCount = 'SELECT count(1) FROM ' . $core->prefix . 'setting';
+ $strReqCount .= ' WHERE setting_id = \'%s\'';
+ $strReqCount .= ' AND setting_ns = \'system\'';
+ $strReqCount .= ' AND blog_id IS NULL';
+
+ $strReqSelect = 'SELECT setting_value FROM ' . $core->prefix . 'setting';
+ $strReqSelect .= ' WHERE setting_id = \'%s\'';
+ $strReqSelect .= ' AND setting_ns = \'system\'';
+ $strReqSelect .= ' AND blog_id IS NULL';
+
+ # Add nb of posts for home (first page), copying nb of posts on every page
+ $rs = $core->con->select(sprintf($strReqCount, 'nb_post_for_home'));
+ if ($rs->f(0) == 0) {
+ $rs = $core->con->select(sprintf($strReqSelect, 'nb_post_per_page'));
+ $strReq = sprintf($strReqFormat, 'nb_post_for_home', $rs->f(0), 'Nb of posts on home (first page only)');
+ $core->con->execute($strReq);
+ }
+ }
+
+ if (version_compare($version, '2.8.1', '<=')) {
+ # switch from jQuery 1.11.1 to 1.11.2
+ $strReq = 'UPDATE ' . $core->prefix . 'setting ' .
+ " SET setting_value = '1.11.3' " .
+ " WHERE setting_id = 'jquery_version' " .
+ " AND setting_ns = 'system' " .
+ " AND setting_value = '1.11.1' ";
+ $core->con->execute($strReq);
+ # Some new settings should be initialized, prepare db queries
+ $strReq = 'INSERT INTO ' . $core->prefix . 'setting' .
+ ' (setting_id,setting_ns,setting_value,setting_type,setting_label)' .
+ ' VALUES(\'%s\',\'system\',\'%s\',\'boolean\',\'%s\')';
+ $core->con->execute(sprintf($strReq, 'no_search', '0', 'Disable internal search system'));
+ }
+
+ if (version_compare($version, '2.9', '<=')) {
+ # Some new settings should be initialized, prepare db queries
+ $strReq = 'INSERT INTO ' . $core->prefix . 'setting' .
+ ' (setting_id,setting_ns,setting_value,setting_type,setting_label)' .
+ ' VALUES(\'%s\',\'system\',\'%s\',\'%s\',\'%s\')';
+ $core->con->execute(
+ sprintf($strReq, 'media_video_width', '400', 'integer', 'Media video insertion width'));
+ $core->con->execute(
+ sprintf($strReq, 'media_video_height', '300', 'integer', 'Media video insertion height'));
+ $core->con->execute(
+ sprintf($strReq, 'media_flash_fallback', '1', 'boolean', 'Flash player fallback for audio and video media'));
+
+ # Some settings and prefs should be moved from string to array
+ self::settings2array('system', 'date_formats');
+ self::settings2array('system', 'time_formats');
+ self::settings2array('antispam', 'antispam_filters');
+ self::settings2array('pings', 'pings_uris');
+ self::settings2array('system', 'simpleMenu');
+ self::prefs2array('dashboard', 'favorites');
+ }
+
+ if (version_compare($version, '2.9.1', '<=')) {
+ # Some settings and prefs should be moved from string to array
+ self::prefs2array('dashboard', 'favorites');
+ self::prefs2array('interface', 'media_last_dirs');
+ }
+
+ if (version_compare($version, '2.10', '<')) {
+ @unlink(DC_ROOT . '/' . 'admin/js/jsUpload/vendor/jquery.ui.widget.js');
+ @rmdir(DC_ROOT . '/' . 'admin/js/jsUpload/vendor');
+
+ # Create new var directory and its .htaccess file
+ @files::makeDir(DC_VAR);
+ $f = DC_VAR . '/.htaccess';
+ if (!file_exists($f)) {
+ @file_put_contents($f, 'Require all denied' . "\n" . 'Deny from all' . "\n");
+ }
+
+ # Some new settings should be initialized, prepare db queries
+ $strReq = 'INSERT INTO ' . $core->prefix . 'setting' .
+ ' (setting_id,setting_ns,setting_value,setting_type,setting_label)' .
+ ' VALUES(\'%s\',\'system\',\'%s\',\'%s\',\'%s\')';
+ # Import feed control
+ $core->con->execute(
+ sprintf($strReq, 'import_feed_url_control', true, 'boolean', 'Control feed URL before import'));
+ $core->con->execute(
+ sprintf($strReq, 'import_feed_no_private_ip', true, 'boolean', 'Prevent import feed from private IP'));
+ $core->con->execute(
+ sprintf($strReq, 'import_feed_ip_regexp', '', 'string', 'Authorize import feed only from this IP regexp'));
+ $core->con->execute(
+ sprintf($strReq, 'import_feed_port_regexp', '/^(80|443)$/', 'string', 'Authorize import feed only from this port regexp'));
+ # CSP directive (admin part)
+ $core->con->execute(
+ sprintf($strReq, 'csp_admin_on', true, 'boolean', 'Send CSP header (admin)'));
+ $core->con->execute(
+ sprintf($strReq, 'csp_admin_default', "''self''", 'string', 'CSP default-src directive'));
+ $core->con->execute(
+ sprintf($strReq, 'csp_admin_script', "''self'' ''unsafe-inline'' ''unsafe-eval''", 'string', 'CSP script-src directive'));
+ $core->con->execute(
+ sprintf($strReq, 'csp_admin_style', "''self'' ''unsafe-inline''", 'string', 'CSP style-src directive'));
+ $core->con->execute(
+ sprintf($strReq, 'csp_admin_img', "''self'' data: media.dotaddict.org", 'string', 'CSP img-src directive'));
+ }
+
+ if (version_compare($version, '2.11', '<')) {
+ // Remove the CSP report file from it's old place
+ @unlink(DC_ROOT . '/admin/csp_report.txt');
+
+ # Some new settings should be initialized, prepare db queries
+ $strReq = 'INSERT INTO ' . $core->prefix . 'setting' .
+ ' (setting_id,setting_ns,setting_value,setting_type,setting_label)' .
+ ' VALUES(\'%s\',\'system\',\'%s\',\'%s\',\'%s\')';
+ $core->con->execute(
+ sprintf($strReq, 'csp_admin_report_only', false, 'boolean', 'CSP Report only violations (admin)'));
+
+ // SQlite Clearbricks driver does not allow using single quote at beginning or end of a field value
+ // so we have to use neutral values (localhost and 127.0.0.1) for some CSP directives
+ $csp_prefix = $core->con->driver() == 'sqlite' ? 'localhost ' : ''; // Hack for SQlite Clearbricks driver
+ $csp_suffix = $core->con->driver() == 'sqlite' ? ' 127.0.0.1' : ''; // Hack for SQlite Clearbricks driver
+
+ # Try to fix some CSP directive wrongly stored for SQLite drivers
+ $strReq = 'UPDATE ' . $core->prefix . 'setting ' .
+ " SET setting_value = '" . $csp_prefix . "''self''" . $csp_suffix . "' " .
+ " WHERE setting_id = 'csp_admin_default' " .
+ " AND setting_ns = 'system' " .
+ " AND setting_value = 'self' ";
+ $core->con->execute($strReq);
+ $strReq = 'UPDATE ' . $core->prefix . 'setting ' .
+ " SET setting_value = '" . $csp_prefix . "''self'' ''unsafe-inline'' ''unsafe-eval''" . $csp_suffix . "' " .
+ " WHERE setting_id = 'csp_admin_script' " .
+ " AND setting_ns = 'system' " .
+ " AND setting_value = 'self'' ''unsafe-inline'' ''unsafe-eval' ";
+ $core->con->execute($strReq);
+ $strReq = 'UPDATE ' . $core->prefix . 'setting ' .
+ " SET setting_value = '" . $csp_prefix . "''self'' ''unsafe-inline''" . $csp_suffix . "' " .
+ " WHERE setting_id = 'csp_admin_style' " .
+ " AND setting_ns = 'system' " .
+ " AND setting_value = 'self'' ''unsafe-inline' ";
+ $core->con->execute($strReq);
+ $strReq = 'UPDATE ' . $core->prefix . 'setting ' .
+ " SET setting_value = '" . $csp_prefix . "''self'' data: media.dotaddict.org blob:' " .
+ " WHERE setting_id = 'csp_admin_img' " .
+ " AND setting_ns = 'system' " .
+ " AND setting_value = 'self'' data: media.dotaddict.org' ";
+ $core->con->execute($strReq);
+
+ # Update CSP img-src default directive
+ $strReq = 'UPDATE ' . $core->prefix . 'setting ' .
+ " SET setting_value = '" . $csp_prefix . "''self'' data: media.dotaddict.org blob:' " .
+ " WHERE setting_id = 'csp_admin_img' " .
+ " AND setting_ns = 'system' " .
+ " AND setting_value = '''self'' data: media.dotaddict.org' ";
+ $core->con->execute($strReq);
+
+ # Update first publication on published posts
+ $strReq = 'UPDATE ' . $core->prefix . 'post ' .
+ 'SET post_firstpub = 1 ' .
+ 'WHERE post_status = 1 ';
+ $core->con->execute($strReq);
+
+ # A bit of housecleaning for no longer needed files
+ $remfiles = [
+ 'admin/js/jquery/jquery.modal.js',
+ 'admin/style/modal/close.png',
+ 'admin/style/modal/loader.gif',
+ 'admin/style/modal/modal.css',
+ 'admin/js/dragsort-tablerows.js',
+ 'admin/js/tool-man/cookies.js',
+ 'admin/js/tool-man/coordinates.js',
+ 'admin/js/tool-man/core.js',
+ 'admin/js/tool-man/css.js',
+ 'admin/js/tool-man/drag.js',
+ 'admin/js/tool-man/dragsort.js',
+ 'admin/js/tool-man/events.js',
+ 'admin/js/ie7/IE7.js',
+ 'admin/js/ie7/IE8.js',
+ 'admin/js/ie7/IE9.js',
+ 'admin/js/ie7/blank.gif',
+ 'admin/js/ie7/ie7-hashchange.js',
+ 'admin/js/ie7/ie7-recalc.js',
+ 'admin/js/ie7/ie7-squish.js',
+ 'admin/style/iesucks.css',
+ 'plugins/tags/js/jquery.autocomplete.js',
+ 'theme/ductile/ie.css'
+ ];
+ $remfolders = [
+ 'admin/style/modal',
+ 'admin/js/tool-man',
+ 'admin/js/ie7'
+ ];
+
+ foreach ($remfiles as $f) {
+ @unlink(DC_ROOT . '/' . $f);
+ }
+ foreach ($remfolders as $f) {
+ @rmdir(DC_ROOT . '/' . $f);
+ }
+ }
+
+ if (version_compare($version, '2.12', '<')) {
+ # switch from jQuery 2.2.0 to 2.2.4
+ $strReq = 'UPDATE ' . $core->prefix . 'setting ' .
+ " SET setting_value = '2.2.4' " .
+ " WHERE setting_id = 'jquery_version' " .
+ " AND setting_ns = 'system' " .
+ " AND setting_value = '2.2.0' ";
+ $core->con->execute($strReq);
+ }
+
+ if (version_compare($version, '2.12.2', '<')) {
+ // SQlite Clearbricks driver does not allow using single quote at beginning or end of a field value
+ // so we have to use neutral values (localhost and 127.0.0.1) for some CSP directives
+ $csp_prefix = $core->con->driver() == 'sqlite' ? 'localhost ' : ''; // Hack for SQlite Clearbricks driver
+
+ # Update CSP img-src default directive
+ $strReq = 'UPDATE ' . $core->prefix . 'setting ' .
+ " SET setting_value = '" . $csp_prefix . "''self'' data: http://media.dotaddict.org blob:' " .
+ " WHERE setting_id = 'csp_admin_img' " .
+ " AND setting_ns = 'system' " .
+ " AND setting_value = '" . $csp_prefix . "''self'' data: media.dotaddict.org blob:' ";
+ $core->con->execute($strReq);
+ }
+
+ if (version_compare($version, '2.14', '<')) {
+ // File not more needed
+ @unlink(DC_ROOT . '/' . 'admin/js/jquery/jquery.bgFade.js');
+ }
+
+ if (version_compare($version, '2.14.3', '<')) {
+ # Update flie exclusion upload regex
+ $strReq = 'UPDATE ' . $core->prefix . 'setting ' .
+ " SET setting_value = '/\\.(phps?|pht(ml)?|phl|.?html?|xml|js|htaccess)[0-9]*$/i' " .
+ " WHERE setting_id = 'media_exclusion' " .
+ " AND setting_ns = 'system' " .
+ " AND (setting_value = '/\\.php[0-9]*$/i' " .
+ " OR setting_value = '/\\.php$/i') " .
+ " OR setting_value = '/\\.(phps?|pht(ml)?|phl)[0-9]*$/i' " .
+ " OR setting_value = '/\\.(phps?|pht(ml)?|phl|s?html?|js)[0-9]*$/i'" .
+ " OR setting_value = '/\\.(phps?|pht(ml)?|phl|s?html?|js|htaccess)[0-9]*$/i'";
+ $core->con->execute($strReq);
+ }
+
+ if (version_compare($version, '2.15', '<')) {
+ # switch from jQuery 1.11.3 to 1.12.4
+ $strReq = 'UPDATE ' . $core->prefix . 'setting ' .
+ " SET setting_value = '1.12.4' " .
+ " WHERE setting_id = 'jquery_version' " .
+ " AND setting_ns = 'system' " .
+ " AND setting_value = '1.11.3' ";
+ $core->con->execute($strReq);
+
+ # A bit of housecleaning for no longer needed files
+ $remfiles = [
+ 'plugins/dcLegacyEditor/tpl/index.tpl',
+ 'plugins/dcCKEditor/tpl/index.tpl'
+ ];
+ foreach ($remfiles as $f) {
+ @unlink(DC_ROOT . '/' . $f);
+ }
+
+ }
+
+ if (version_compare($version, '2.15.1', '<')) {
+ // Remove unsafe-inline from CSP script directives
+ $strReq = 'UPDATE ' . $core->prefix . 'setting ' .
+ " SET setting_value = REPLACE(setting_value, '''unsafe-inline''', '') " .
+ " WHERE setting_id = 'csp_admin_script' " .
+ " AND setting_ns = 'system' ";
+ $core->con->execute($strReq);
+ }
+
+ if (version_compare($version, '2.16', '<')) {
+ // Update DotAddict plugins store URL
+ $strReq = 'UPDATE ' . $core->prefix . 'setting ' .
+ " SET setting_value = REPLACE(setting_value, 'http://update.dotaddict.org', 'https://update.dotaddict.org') " .
+ " WHERE setting_id = 'store_plugin_url' " .
+ " AND setting_ns = 'system' ";
+ $core->con->execute($strReq);
+ // Update DotAddict themes store URL
+ $strReq = 'UPDATE ' . $core->prefix . 'setting ' .
+ " SET setting_value = REPLACE(setting_value, 'http://update.dotaddict.org', 'https://update.dotaddict.org') " .
+ " WHERE setting_id = 'store_theme_url' " .
+ " AND setting_ns = 'system' ";
+ $core->con->execute($strReq);
+ // Update CSP img-src default directive for media.dotaddict.org
+ $strReq = 'UPDATE ' . $core->prefix . 'setting ' .
+ " SET setting_value = REPLACE(setting_value, 'http://media.dotaddict.org', 'https://media.dotaddict.org') " .
+ " WHERE setting_id = 'csp_admin_img' " .
+ " AND setting_ns = 'system' ";
+ $core->con->execute($strReq);
+ // Set default jQuery loading for blog
+ $strReq = 'INSERT INTO ' . $core->prefix . 'setting' .
+ ' (setting_id,setting_ns,setting_value,setting_type,setting_label)' .
+ ' VALUES(\'%s\',\'system\',\'%s\',\'%s\',\'%s\')';
+ $core->con->execute(
+ sprintf($strReq, 'jquery_needed', true, 'boolean', 'Load jQuery library'));
+
+ # A bit of housecleaning for no longer needed files
+ $remfiles = [
+ // Oldest jQuery public lib
+ 'inc/js/1.4.2/jquery.js',
+ 'inc/js/1.4.2/jquery.cookie.js',
+ 'inc/js/1.11.1/jquery.js',
+ 'inc/js/1.11.1/jquery.cookie.js',
+ 'inc/js/1.11.3/jquery.js',
+ 'inc/js/1.11.3/jquery.cookie.js',
+ 'inc/js/1.12.4/jquery.js',
+ 'inc/js/1.12.4/jquery.cookie.js',
+ 'inc/js/2.2.0/jquery.js',
+ 'inc/js/2.2.0/jquery.cookie.js',
+ 'inc/js/2.2.4/jquery.js',
+ 'inc/js/2.2.4/jquery.cookie.js',
+ 'inc/js/3.3.1/jquery.js',
+ 'inc/js/3.3.1/jquery.cookie.js',
+ // jQuery farbtastic Color picker
+ 'admin/js/color-picker.js',
+ 'admin/js/jquery/jquery.farbtastic.js',
+ 'admin/style/farbtastic/farbtastic.css',
+ 'admin/style/farbtastic/marker.png',
+ 'admin/style/farbtastic/mask.png',
+ 'admin/style/farbtastic/wheel.png'
+ ];
+ $remfolders = [
+ // Oldest jQuery public lib
+ 'inc/js/1.4.2',
+ 'inc/js/1.11.1',
+ 'inc/js/1.11.3',
+ 'inc/js/1.12.4',
+ 'inc/js/2.2.0',
+ 'inc/js/2.2.4',
+ 'inc/js/3.3.1',
+ // jQuery farbtastic Color picker
+ 'admin/style/farbtastic'
+ ];
+ foreach ($remfiles as $f) {
+ @unlink(DC_ROOT . '/' . $f);
+ }
+ foreach ($remfolders as $f) {
+ @rmdir(DC_ROOT . '/' . $f);
+ }
+ }
+
+ $core->setVersion('core', DC_VERSION);
+ $core->blogDefaults();
+
+ return $cleanup_sessions;
+ }
+
+ /**
+ * Convert old-fashion serialized array setting to new-fashion json encoded array
+ * @param $ns namespace
+ * @param $setting setting name (id)
+ */
+ public static function settings2array($ns, $setting)
+ {
+ global $core;
+
+ $strReqSelect =
+ "SELECT setting_id,blog_id,setting_ns,setting_type,setting_value FROM " . $core->prefix . "setting " .
+ "WHERE setting_id = '%s' " .
+ "AND setting_ns = '%s' " .
+ "AND setting_type = 'string'";
+ $rs = $core->con->select(sprintf($strReqSelect, $setting, $ns));
+ while ($rs->fetch()) {
+ $value = @unserialize($rs->setting_value);
+ if (!$value) {
+ $value = [];
+ }
+ settype($value, 'array');
+ $value = json_encode($value);
+ $rs2 = "UPDATE " . $core->prefix . "setting " .
+ "SET setting_type='array', setting_value = '" . $core->con->escape($value) . "' " .
+ "WHERE setting_id='" . $core->con->escape($rs->setting_id) . "' " .
+ "AND setting_ns='" . $core->con->escape($rs->setting_ns) . "' ";
+ if ($rs->blog_id == '') {
+ $rs2 .= "AND blog_id IS null";
+ } else {
+ $rs2 .= "AND blog_id = '" . $core->con->escape($rs->blog_id) . "'";
+ }
+ $core->con->execute($rs2);
+ }
+ }
+
+ /**
+ * Convert old-fashion serialized array pref to new-fashion json encoded array
+ * @param $ws workspace
+ * @param $pref pref name (id)
+ */
+ public static function prefs2array($ws, $pref)
+ {
+ global $core;
+
+ $strReqSelect =
+ "SELECT pref_id,user_id,pref_ws,pref_type,pref_value FROM " . $core->prefix . "pref " .
+ "WHERE pref_id = '%s' " .
+ "AND pref_ws = '%s' " .
+ "AND pref_type = 'string'";
+ $rs = $core->con->select(sprintf($strReqSelect, $pref, $ws));
+ while ($rs->fetch()) {
+ $value = @unserialize($rs->pref_value);
+ if (!$value) {
+ $value = [];
+ }
+ settype($value, 'array');
+ $value = json_encode($value);
+ $rs2 = "UPDATE " . $core->prefix . "pref " .
+ "SET pref_type='array', pref_value = '" . $core->con->escape($value) . "' " .
+ "WHERE pref_id='" . $core->con->escape($rs->pref_id) . "' " .
+ "AND pref_ws='" . $core->con->escape($rs->pref_ws) . "' ";
+ if ($rs->user_id == '') {
+ $rs2 .= "AND user_id IS null";
+ } else {
+ $rs2 .= "AND user_id = '" . $core->con->escape($rs->user_id) . "'";
+ }
+ $core->con->execute($rs2);
+ }
+ }
+}
diff --git a/dotclear._no/inc/digests b/dotclear._no/inc/digests
new file mode 100644
index 0000000..2fd569d
--- /dev/null
+++ b/dotclear._no/inc/digests
@@ -0,0 +1,1532 @@
+69176657327fd02cd3ad9bbe9e06b399 ./inc/js/jquery/3.4.1/jquery.js
+54e5998271cc291e16c785e4914601a3 ./inc/js/util.js
+e84670ad7eb58f3e8a4f77a7a209897f ./inc/js/post.js
+92b23cb12187ed316a73b342ade6626a ./inc/css/print.css
+05a5f5558655cdf8d10f7c8c3dfb7330 ./inc/core/class.dc.media.php
+1911fbe32734435adb6d386ef095ab0c ./inc/core/class.dc.workspace.php
+f88333048cbbd83358f805b44da841dd ./inc/core/class.dc.plugins.php
+18b855ee0f4573eaa498f1619d7c8a91 ./inc/core/class.dc.store.php
+5c7b48c69277950f4ab280c20e4ce9e2 ./inc/core/class.dc.update.php
+c8a82b55c1c9f885c26f37a95e3252ea ./inc/core/class.dc.log.php
+ddfd0f634946941cc081acc071216888 ./inc/core/class.dc.store.parser.php
+22b80de5e06d46ee9e3a3963c087d621 ./inc/core/class.dc.namespace.php
+32c84d57ee9f9e86e6d19f21dad02fd0 ./inc/core/class.dc.meta.php
+19b9ea2ef2ef20ad0500275732b8c89d ./inc/core/class.dc.categories.php
+a1c13511deca1a0aa88214481ec5d3a2 ./inc/core/class.dc.xmlrpc.php
+c834e46638182c68bbcd5dadf86cc0d7 ./inc/core/class.dc.error.php
+28295af80b776f1a00c571b8be6a845c ./inc/core/class.dc.utils.php
+74b476e6e31ab14d46d7465086ad479f ./inc/core/class.dc.rest.php
+a9e3ff86215151f2682194a7f8e4a4e3 ./inc/core/class.dc.themes.php
+27cf5c81db3799a406144ae6cd0a5086 ./inc/core/class.dc.auth.php
+c47d446d923fad977440a0dba83992fd ./inc/core/class.dc.store.reader.php
+54496baa0b7e67d304b07a8d535bcdf1 ./inc/core/class.dc.rs.extensions.php
+fa77d99f6ea31a5e7f952800e08558e1 ./inc/core/class.dc.sql.statement.php
+360e1d62ebc85d9e17af585319c6143c ./inc/core/class.dc.trackback.php
+24a2183b113375fa50c6793c37605645 ./inc/core/class.dc.settings.php
+6064e74072a441d0598c6b7c0deadfd0 ./inc/core/class.dc.postmedia.php
+27a0a2fa024288f183f1e592457c115a ./inc/core/class.dc.prefs.php
+fcb7591ba284d073dce36b664648432d ./inc/core/class.dc.blog.php
+b5dfc7077325e21ee6e16c7c667ed1d3 ./inc/core/class.dc.modules.php
+0860f1629dfe90c6d28be9a7bd66ee57 ./inc/core/class.dc.core.php
+d7981770ec4132da77aa49f6662850b9 ./inc/libs/clearbricks/ext/incutio.ixr_library.php
+24008d84e2118123f95c58b593fd7bfb ./inc/libs/clearbricks/net/class.net.socket.php
+a84fa02f72253491cc52ecebade6238e ./inc/libs/clearbricks/zip/class.unzip.php
+61991b6a51dae0200502c40681bf55ee ./inc/libs/clearbricks/zip/class.zip.php
+b4e303fc1ca182383d7dea3e98d42b28 ./inc/libs/clearbricks/diff/lib.tidy.diff.php
+778de5c2bbb7ca2c37db41476c1c1861 ./inc/libs/clearbricks/diff/lib.diff.php
+108c3619853283c4cac68439fa81fe76 ./inc/libs/clearbricks/mail/class.socket.mail.php
+07ad001828aeb71123a83717b6e6a4d2 ./inc/libs/clearbricks/mail/class.mail.php
+1e1f4257fe0f4f5df3b58c97a73d57cf ./inc/libs/clearbricks/rest/class.rest.php
+1a400216170e0cdd4e4b842964ae6c70 ./inc/libs/clearbricks/dblayer/class.pgsql.php
+09ba4d8034de054813217b8d2d6aaf8a ./inc/libs/clearbricks/dblayer/class.mysql.php
+689038b9b70933562458cf0757de86dc ./inc/libs/clearbricks/dblayer/class.sqlite.php
+ee5515dd7fa0fc3815ff84aa3b66c454 ./inc/libs/clearbricks/dblayer/dblayer.php
+251dde5ba6ce14c727270514fd82f97a ./inc/libs/clearbricks/dblayer/class.cursor.php
+49bca265edb052297e3c9b9a8ac5f4eb ./inc/libs/clearbricks/dblayer/class.mysqli.php
+3828c471e963c76807e34d23e769648d ./inc/libs/clearbricks/dblayer/class.mysqlimb4.php
+e145e9dbabf2d93b0280393834cee318 ./inc/libs/clearbricks/html.validator/class.html.validator.php
+1b9f259dc4436c3fb8aa0e967ceb0aa3 ./inc/libs/clearbricks/html.filter/class.html.filter.php
+6af80e49cb72c745396eaa62b3e7035e ./inc/libs/clearbricks/Makefile
+b234ee4d69f5fce4486a80fdaf4a4263 ./inc/libs/clearbricks/LICENSE
+1938019c6ebf93cee27f0f9e89c63ec2 ./inc/libs/clearbricks/image/class.image.tools.php
+6e77a0986fc110ef681f9f6ae393d3d4 ./inc/libs/clearbricks/image/class.image.meta.php
+8c348463af8f71bbbbc6daba0e3e0a07 ./inc/libs/clearbricks/filemanager/class.filemanager.php
+81915411aa6d426adfaf31aa98c18c01 ./inc/libs/clearbricks/pager/class.pager.php
+b2d74bdc8e963d55826ecf2a0befb96f ./inc/libs/clearbricks/README.md
+100bc628beb60c7909b542178f0b6611 ./inc/libs/clearbricks/net.http.feed/class.feed.parser.php
+6d3530a91f467e34456b6c54ef15a41c ./inc/libs/clearbricks/net.http.feed/class.feed.reader.php
+61b7f5d058f5bbef9f3601672120f6a2 ./inc/libs/clearbricks/net.xmlrpc/class.net.xmlrpc.php
+ef0e6a0d7c0122601811cfa03299b1e2 ./inc/libs/clearbricks/dbschema/class.dbschema.php
+a7376627c8d7cfde9ea2e13d14898635 ./inc/libs/clearbricks/dbschema/class.mysql.dbschema.php
+b8ff5b59d29b544561735acd28c9835f ./inc/libs/clearbricks/dbschema/class.sqlite.dbschema.php
+7e987de855c1a63de436b1f06f77dd62 ./inc/libs/clearbricks/dbschema/class.dbstruct.php
+0857757c7a823eb6a323dc33468f5d57 ./inc/libs/clearbricks/dbschema/class.mysqli.dbschema.php
+50079a7f24fce5c7b0662bf7dbfe68a7 ./inc/libs/clearbricks/dbschema/class.mysqlimb4.dbschema.php
+ecf6517f6359a8abac366695a4c08255 ./inc/libs/clearbricks/dbschema/class.pgsql.dbschema.php
+8818a5096136fc2f39a4739aaf7a23ba ./inc/libs/clearbricks/template/class.tplnodevalueparent.php
+d854ec72b6f082b54ffcad6943a92cc7 ./inc/libs/clearbricks/template/class.tplnodevalue.php
+94086287b49e96217a12be6f4a29ce17 ./inc/libs/clearbricks/template/class.template.php
+1e69cd8804b3af60ea60e9a18d654220 ./inc/libs/clearbricks/template/class.tplnodeblock.php
+b8fbe3ea98de698149bd9045c4d304df ./inc/libs/clearbricks/template/class.tplnodeblockdef.php
+4b6f1386e91934c379d0d1fcca4c570f ./inc/libs/clearbricks/template/class.tplnodetext.php
+ac8d43154cfb65dc99ec11fb214093f3 ./inc/libs/clearbricks/template/class.tplnode.php
+338ec60a70bff9ac5af0da7243ba4761 ./inc/libs/clearbricks/_common.php
+339ed42b6b02d92bff7e98e724fc4995 ./inc/libs/clearbricks/url.handler/class.url.handler.php
+8ac86c7ba2de76340e22390b31528723 ./inc/libs/clearbricks/.editorconfig
+e969237c3afdcbd0524bfb67ff186c2d ./inc/libs/clearbricks/debian/dirs
+a165b0c48efd38c3c2725a282922c6f1 ./inc/libs/clearbricks/debian/docs
+e6536f05fea33e5895e3a1e55af12942 ./inc/libs/clearbricks/debian/mkdcl.php
+b0bd77efcf7f3ba705ca57fccd3479f7 ./inc/libs/clearbricks/debian/control
+30ecf234a276835fe14c19f5a54dcbf8 ./inc/libs/clearbricks/debian/rules
+771304f18d0aca7eb424be239480264e ./inc/libs/clearbricks/debian/changelog
+48a24b70a0b376535542b996af517398 ./inc/libs/clearbricks/debian/compat
+ac1d5466f3f1e8c07fca0801cd6224f4 ./inc/libs/clearbricks/debian/copyright
+434ce05244c0732555d30a13f068d355 ./inc/libs/clearbricks/common/lib.form.php
+954e9176c96cf833dcf3b59985f04500 ./inc/libs/clearbricks/common/lib.text.php
+53f8e535da21c1f1ec0246a643d01993 ./inc/libs/clearbricks/common/tz.dat
+0e9add0dc1abf4a31c88a64fa0f6a17f ./inc/libs/clearbricks/common/lib.l10n.php
+b21e3962efd0b8d5e922021caf69354f ./inc/libs/clearbricks/common/lib.crypt.php
+5096397fd4bd30af6886069e15495ddd ./inc/libs/clearbricks/common/_main.php
+9e0ab30bb64215bcad4c177ea2ba9b79 ./inc/libs/clearbricks/common/lib.date.php
+bdcfcd16e2885cd00213feb221f7a908 ./inc/libs/clearbricks/common/lib.html.php
+02acf74558da8ce5739d1fa7420670a9 ./inc/libs/clearbricks/common/lib.files.php
+42995f59a99233daddff0b9d03cb9ad6 ./inc/libs/clearbricks/common/lib.json.php
+8dd1a1a4cfe2cc79195c8042e8641654 ./inc/libs/clearbricks/common/lib.http.php
+2f96e5527ace7109d1ec72b5f06f6d1e ./inc/libs/clearbricks/.doxygen.conf
+7d2ab2b1856476a513c3f98d9244b05e ./inc/libs/clearbricks/net.http/class.net.http.php
+6ffd3e80eb0a0ec7d6ae77fa53ab545b ./inc/libs/clearbricks/net.nntp/class.net.nntp.php
+21b11425778a7e43c1bac5c711ec7d95 ./inc/libs/clearbricks/net.nntp/class.nntp.message.php
+bc41847ead5bef334d142f8f17e1c35b ./inc/libs/clearbricks/text.wiki2xhtml/class.wiki2xhtml.php
+9b7fa546996b9550bb4b5fdac63fd994 ./inc/libs/clearbricks/xmlsql/class.xmlsql.php
+296df999460067373fa99cc3dd0417a0 ./inc/libs/clearbricks/session.db/class.session.db.php
+b3d391118dd1472e62ce0f6671617047 ./inc/libs/clearbricks/mail.mime/class.mime.message.php
+7b5a6634ca8e248e4753a4d92d58841f ./inc/libs/clearbricks/mail.convert/class.mail.convert.php
+086c6dad52a92a7314ffc287db510d2d ./inc/config.php.in
+74eda6cbe589671a20c4b8ac74319472 ./inc/admin/lib.admincombos.php
+cdace24dbc43708a08c42ee9b48bff6e ./inc/admin/lib.pager.php
+e94aa2a8935aabec2239c29439af5d16 ./inc/admin/class.dc.notices.php
+84d41344921f9022473d1a488c32ca89 ./inc/admin/prepend.php
+aabaacc9cf9963dcb258b1edef276584 ./inc/admin/class.dc.favorites.php
+05216cc14e347a3b3d2aee677864d9f6 ./inc/admin/class.dc.menu.php
+56af90ac55b4565fc3f32b75c65d4d15 ./inc/admin/actions/class.dcactioncomments.php
+5578b58897d314f38b6b069a827979aa ./inc/admin/actions/class.dcactionposts.php
+006f006bc53b8b5ecb6fdcb15b478477 ./inc/admin/actions/class.dcaction.php
+e034a8e111fac1403593c137cf902400 ./inc/admin/actions/class.dcactionblogs.php
+b91d365ba68942126b6f398d8619a76f ./inc/admin/class.dc.blog_pref.php
+0892576372c4e8e056b94e3cd2924d5c ./inc/admin/lib.moduleslist.php
+7f17f2996a7af14637011558d37f9ed7 ./inc/admin/lib.dc.adminurl.php
+56dc86b7bab1dce6ddf37d5e4e6c688d ./inc/admin/lib.themeconfig.php
+106d7d64fb5ec88ad22a3786a74cbb07 ./inc/admin/lib.dc.page.php
+b888d0cb9c1dd051f292a3e57aaa84ec ./inc/dbschema/upgrade.php
+6f4fb97efc20c9d9d4b6cc080d86db94 ./inc/dbschema/upgrade-cli.php
+1334a2dc7a74028d094c04f02c373ea8 ./inc/dbschema/db-schema.php
+fde286d7d847606509af81fd646e4052 ./inc/load_var_file.php
+9257611cc4fe1e7e3b55d021435de341 ./inc/.htaccess
+782c46d049bd8c821e84a4ca34afbd62 ./inc/prepend.php
+0ac6fdff4e285f6b02b1bc1c554962c2 ./inc/load_plugin_file.php
+533296e79a60c2ba3e88acf4d0bacdde ./inc/public/lib.tpl.context.php
+53baf8d7a0a311632e407a7885a62949 ./inc/public/prepend.php
+3b3c88c538b9459e0d1f24844adee91d ./inc/public/lib.urlhandlers.php
+f6d6aea676b329c172c33484fca48b65 ./inc/public/rs.extension.php
+d7ba68c7dae378e478bb1f344b5a2b0e ./inc/public/class.dc.template.php
+83110f66514591a257f01696fa9ff73e ./inc/public/default-templates/dotty/atom.xml
+51e84a6ae3a7e6f337b7472b82bc8300 ./inc/public/default-templates/dotty/_sidebar.html
+0ef7f104343e9585377f4417747b91fe ./inc/public/default-templates/dotty/_video_player.html
+43bcf819524ca6f7ee992ab855f2d27c ./inc/public/default-templates/dotty/_entry-content.html
+ff1701d5bf3c123522adb7240d19b018 ./inc/public/default-templates/dotty/404.html
+93ed8b91a01e430fef8646f03f424296 ./inc/public/default-templates/dotty/rss2.xml
+1c2a03ea641941bcb859f19a831908d4 ./inc/public/default-templates/dotty/rss2.xsl
+5ced40e1047712adbf97d013c80f3e02 ./inc/public/default-templates/dotty/_head.html
+6079fc07a1356e16057e52156f18d31a ./inc/public/default-templates/dotty/category.html
+b1968d4b1235e1cb598ddd8d781bfee2 ./inc/public/default-templates/dotty/_top.html
+993b62dad634783ab35db445dd06e3ca ./inc/public/default-templates/dotty/README.md
+bbd99a040d386385fea2b41865602b30 ./inc/public/default-templates/dotty/rss2-comments.xml
+0faf1d88691e946b2b49347fcc35d793 ./inc/public/default-templates/dotty/archive_month.html
+1a052df8df9b910f37ac174c51453d7f ./inc/public/default-templates/dotty/_footer.html
+878d8d60aaa5df7b0f6bd659cfa11925 ./inc/public/default-templates/dotty/archive.html
+aaeff5c3c01e23c598d8a1336af2a527 ./inc/public/default-templates/dotty/_mp3_player.html
+1b3c11f458e8754cdbdf887ca9afa8f3 ./inc/public/default-templates/dotty/atom-comments.xml
+732e8eb89794cccb4a1b7a50a8b4cc8c ./inc/public/default-templates/dotty/__layout.html
+31508ff46ae8a49cbeb31c3038fcdf4b ./inc/public/default-templates/dotty/post.html
+9c7acea80b7f0e718704c9fe115052aa ./inc/public/default-templates/dotty/_entry-title.html
+3eca08ce448034cc9b0bddb23b417760 ./inc/public/default-templates/dotty/_pagination.html
+72b37618290ab4f3b8f22269f6cc14f1 ./inc/public/default-templates/dotty/static.html
+8212c042ba01c9d2aab58d8158758471 ./inc/public/default-templates/dotty/user_head.html
+fd1b0f48d5b788b35e518a56c8755722 ./inc/public/default-templates/dotty/password-form.html
+f2dc093498c45e6c7e4c4e1f35c9d88b ./inc/public/default-templates/dotty/_entry-short.html
+d66bed8ccedcc421f8df3d5d9c5e6fc1 ./inc/public/default-templates/dotty/_entry-full.html
+80e1b343a4e00e26482df8204623229a ./inc/public/default-templates/dotty/search.html
+3388e9a16eca1452af60b6eb284f6c46 ./inc/public/default-templates/dotty/_entry-feedback.html
+c522cd528313b11a0d5f54943cbcd87c ./inc/public/default-templates/dotty/home.html
+59e62f95c8eefa8d129966dd76898584 ./inc/public/default-templates/dotty/_simple-entry.html
+4a32096520122faec0699929b55ac7a5 ./inc/public/default-templates/dotty/_flv_player.html
+16e2b3cfb2eb9715b3bed5044d7764ee ./inc/public/default-templates/dotty/_audio_player.html
+0d3d41dd3d6faf47e68514608109ea24 ./inc/public/default-templates/currywurst/atom.xml
+ac16885cbe5f6792d4c78b0aed61e279 ./inc/public/default-templates/currywurst/_sidebar.html
+a86052dabee8e95240bc973a53ad6ea6 ./inc/public/default-templates/currywurst/_video_player.html
+7f07f268601081db9cf248bf7b52de2c ./inc/public/default-templates/currywurst/_entry-content.html
+e16a0d13108dde7a6f9ccc5990cde39c ./inc/public/default-templates/currywurst/404.html
+efc7b7074e41f95211e045b6d5b722d5 ./inc/public/default-templates/currywurst/rss2.xml
+92937ce4ec166a37db798b1cfac2b640 ./inc/public/default-templates/currywurst/rss2.xsl
+8ff3975ed0a63e8ca700cc0f80b81404 ./inc/public/default-templates/currywurst/_head.html
+417050c44992fd18f8732344a4f5318b ./inc/public/default-templates/currywurst/category.html
+8297cf19ac0df87d7d2928062796e6e9 ./inc/public/default-templates/currywurst/_top.html
+dace6750c22e5b794e34142dabe3b2c8 ./inc/public/default-templates/currywurst/README.md
+13d7551f19b73cef7a7d7759cbce0ac5 ./inc/public/default-templates/currywurst/rss2-comments.xml
+5ae4a0a89b2da131570ca499ec931aef ./inc/public/default-templates/currywurst/archive_month.html
+68036c1b93a034842be5219fb9479401 ./inc/public/default-templates/currywurst/_footer.html
+cfc839f668f9654908748caef845e8c6 ./inc/public/default-templates/currywurst/archive.html
+aaeff5c3c01e23c598d8a1336af2a527 ./inc/public/default-templates/currywurst/_mp3_player.html
+27d4094c40da104154615553a530ba7c ./inc/public/default-templates/currywurst/atom-comments.xml
+cefc12809ac94a37ba3d7d40fce189b3 ./inc/public/default-templates/currywurst/__layout.html
+b7f5073ce2c3fda70e26164206eeca46 ./inc/public/default-templates/currywurst/post.html
+9e6ff3ef918e90106ae2582ef1ca6d85 ./inc/public/default-templates/currywurst/_entry-title.html
+e6e6026081ca871aea27cc12ee542d53 ./inc/public/default-templates/currywurst/_pagination.html
+72b37618290ab4f3b8f22269f6cc14f1 ./inc/public/default-templates/currywurst/static.html
+8212c042ba01c9d2aab58d8158758471 ./inc/public/default-templates/currywurst/user_head.html
+d1017a719a4e11ba264628c0e1e5f596 ./inc/public/default-templates/currywurst/password-form.html
+8f54c5578d7cd3c6381c1206712b5820 ./inc/public/default-templates/currywurst/_entry-short.html
+668562db56ecb3f050b8289b6db532b7 ./inc/public/default-templates/currywurst/_entry-full.html
+11b58d583ba3c2e1dcfdb1d27c77d24a ./inc/public/default-templates/currywurst/search.html
+fb6530fefa4b1de1ad743d5b73f49d8b ./inc/public/default-templates/currywurst/_entry-feedback.html
+c522cd528313b11a0d5f54943cbcd87c ./inc/public/default-templates/currywurst/home.html
+59e62f95c8eefa8d129966dd76898584 ./inc/public/default-templates/currywurst/_simple-entry.html
+4a32096520122faec0699929b55ac7a5 ./inc/public/default-templates/currywurst/_flv_player.html
+16e2b3cfb2eb9715b3bed5044d7764ee ./inc/public/default-templates/currywurst/_audio_player.html
+0d3d41dd3d6faf47e68514608109ea24 ./inc/public/default-templates/mustek/atom.xml
+a86052dabee8e95240bc973a53ad6ea6 ./inc/public/default-templates/mustek/_video_player.html
+6b837540ab826bf262a2a6664c070bb1 ./inc/public/default-templates/mustek/404.html
+efc7b7074e41f95211e045b6d5b722d5 ./inc/public/default-templates/mustek/rss2.xml
+92937ce4ec166a37db798b1cfac2b640 ./inc/public/default-templates/mustek/rss2.xsl
+4f24681e66e00e4ecf7a38989f18b0f1 ./inc/public/default-templates/mustek/_head.html
+3ed27b88d7d555fc9aab88b28deb9ab3 ./inc/public/default-templates/mustek/category.html
+3d6d8f7b582b90864092c01885dad0e8 ./inc/public/default-templates/mustek/_top.html
+13d7551f19b73cef7a7d7759cbce0ac5 ./inc/public/default-templates/mustek/rss2-comments.xml
+c7c5d06abfbef7c264f927bc9a812012 ./inc/public/default-templates/mustek/archive_month.html
+822f56bb473ffdd0c400c19c464feb9f ./inc/public/default-templates/mustek/_footer.html
+043926d6ac8d8349d814f091b4c98fd5 ./inc/public/default-templates/mustek/archive.html
+aaeff5c3c01e23c598d8a1336af2a527 ./inc/public/default-templates/mustek/_mp3_player.html
+27d4094c40da104154615553a530ba7c ./inc/public/default-templates/mustek/atom-comments.xml
+405b5afa13f8575b7296a7ede5e44537 ./inc/public/default-templates/mustek/__layout.html
+b567cc19052955ec1bb0b28f9e447bc2 ./inc/public/default-templates/mustek/post.html
+72b37618290ab4f3b8f22269f6cc14f1 ./inc/public/default-templates/mustek/static.html
+8212c042ba01c9d2aab58d8158758471 ./inc/public/default-templates/mustek/user_head.html
+d1017a719a4e11ba264628c0e1e5f596 ./inc/public/default-templates/mustek/password-form.html
+98619bcc25256713daae127851871f0e ./inc/public/default-templates/mustek/search.html
+c522cd528313b11a0d5f54943cbcd87c ./inc/public/default-templates/mustek/home.html
+4a32096520122faec0699929b55ac7a5 ./inc/public/default-templates/mustek/_flv_player.html
+16e2b3cfb2eb9715b3bed5044d7764ee ./inc/public/default-templates/mustek/_audio_player.html
+612d4fd4311560a430b27ed440a821df ./inc/core_error.php
+751419260aa954499f7abaabaa882bbe ./LICENSE
+a6cc3d057bfecbc47691936f80690cde ./admin/js/_auth.js
+20f314469d69c8fb1818b56d40209523 ./admin/js/_popup_posts.js
+a6ed70a1c678f1c33426ee5c85ed07c3 ./admin/js/_blog_pref.js
+25068c759243b0533f123dac5474186c ./admin/js/_comment.js
+47d4742c7f3d1902e6690343c058b8d4 ./admin/js/_blogs_actions.js
+a63cd2d4d30c234efb2012f75f5ef5df ./admin/js/_post.js
+30f5ed0c79a4b4ef3112aca056dc3f22 ./admin/js/common.js
+5f3d57797d6fbc763c2a6b1f3530618f ./admin/js/prelude.js
+7f0e0b7107b34a22604f3ee8211a8090 ./admin/js/_preferences.js
+4faeb2c6f02bb2a5674492b22e663469 ./admin/js/_index.js
+0d0985d3d1d8c17d636fdec61ff5bdb0 ./admin/js/_users.js
+0fc61def461afea16e03fd237d4a3978 ./admin/js/_langs.js
+71f68b195c86e4026e16ba0e6898bf6a ./admin/js/filter-controls.js
+33c82f033eea11578e4717a0b7835982 ./admin/js/_media.js
+04def3ae72beb5302d624d68f9316a40 ./admin/js/_blog_theme.js
+5d175e620c6632787b1655402b17b688 ./admin/js/_preferences-dragdrop.js
+353dad43fc098caa3a064c9f4fec0f05 ./admin/js/date-picker.js
+95bfecbacec5b92fea1d494f70ba2fb4 ./admin/js/_charte.js
+fc217d502b05f65616356459c0ec1d62 ./admin/js/codemirror/lib/codemirror.css
+290f44c94a4bf73873ce18156e25ed35 ./admin/js/codemirror/lib/codemirror.js
+b1e4fe031a673fc599f4eea2ebeb65f0 ./admin/js/codemirror/mode/css/css.js
+74d8f0e26c336d3cc9f4ebecfa1b1248 ./admin/js/codemirror/mode/php/php.js
+3239b21ed2a21233738bda46f6a3fce8 ./admin/js/codemirror/mode/xml/xml.js
+b78c7a47c7f69940b7518fae24ab8175 ./admin/js/codemirror/mode/htmlmixed/htmlmixed.js
+49f9bd18db2eb0235005b7389c231057 ./admin/js/codemirror/mode/javascript/javascript.js
+8554e1ee437cc3fb3cfee9ad4a11b8ab ./admin/js/codemirror/LICENSE
+8af38682c890bccd48c4e20333dbfc73 ./admin/js/codemirror/addon/edit/matchbrackets.js
+213d6219d7998e2255465bb640e62309 ./admin/js/codemirror/addon/edit/closebrackets.js
+da6e709ccc5e7a8fba9febb8133832fa ./admin/js/codemirror/addon/mode/multiplex.js
+e204383d6bcaf654666b09970668b60e ./admin/js/codemirror/addon/display/fullscreen.js
+1a278e72b51528270f8ce9ec991929a1 ./admin/js/codemirror/addon/display/fullscreen.css
+ff1cf820cb27d21dc9c5b65cab7c4a8c ./admin/js/codemirror/theme/erlang-dark.css
+a5e7682d89da46244e5464d9572e24d8 ./admin/js/codemirror/theme/railscasts.css
+fd2c53a0057c36df05f00d6cd73191cd ./admin/js/codemirror/theme/material-ocean.css
+cc414e4ec18bc89b3c79935b0e27fc20 ./admin/js/codemirror/theme/bespin.css
+3de4363e29db5a4e58e8bb3e6d23fee8 ./admin/js/codemirror/theme/xq-dark.css
+9c40338a652bd2293c35eab3c2cadab4 ./admin/js/codemirror/theme/base16-light.css
+745180be9a932f24c6c0dd4ebdf5a0ed ./admin/js/codemirror/theme/3024-night.css
+cf1d3224d0e1272d96a0395368f26430 ./admin/js/codemirror/theme/nord.css
+c7f1232b8e8d8894ce9a4b3556574742 ./admin/js/codemirror/theme/material-palenight.css
+75c94576d53c64cc92cd84f4ca2db274 ./admin/js/codemirror/theme/icecoder.css
+7bb44bff5190c427de5ae750d6369633 ./admin/js/codemirror/theme/isotope.css
+7e608c21084c30b01de49c3e4eca05fc ./admin/js/codemirror/theme/rubyblue.css
+3c24cee0dfac767713840b24e8359c99 ./admin/js/codemirror/theme/paraiso-dark.css
+84b6347918411d58d7f9b65a7ee87f65 ./admin/js/codemirror/theme/base16-dark.css
+3d6e922075daca6b0f8cace5c4a441af ./admin/js/codemirror/theme/xq-light.css
+c87f6374b9e4d49dfea1d5c8b3a4ebfc ./admin/js/codemirror/theme/darcula.css
+8996c0efee66bcc2fe2f52763b9da6e8 ./admin/js/codemirror/theme/ambiance.css
+04f429186495a99c1d69e82b361a747c ./admin/js/codemirror/theme/shadowfox.css
+81cd4e786c819d0af356740e5024e453 ./admin/js/codemirror/theme/ttcn.css
+55ff4bdd8a92c3dcbfd5421c532b3059 ./admin/js/codemirror/theme/mbo.css
+ff87f4fad8fa110765f12d5af30aca66 ./admin/js/codemirror/theme/idea.css
+ca86cdbcc6672dbc7ef69007c9a37fc1 ./admin/js/codemirror/theme/yeti.css
+3cc8ba485ddf4f8b7128debab1c664d4 ./admin/js/codemirror/theme/the-matrix.css
+0e71eed5ad381591e741745d092039f8 ./admin/js/codemirror/theme/moxer.css
+b924ed31af30b1c68e5a01fc3c9b0553 ./admin/js/codemirror/theme/hopscotch.css
+777d36e1c5bbfeb3bf2ca8dd607eee93 ./admin/js/codemirror/theme/tomorrow-night-bright.css
+6b19894b9787c6791c250a95d0d4f8d6 ./admin/js/codemirror/theme/neat.css
+4c9c1070a2d03127c217f39bcb5a9c33 ./admin/js/codemirror/theme/twilight.css
+8f5b2f0dfd4c6f38fd753b7d87954868 ./admin/js/codemirror/theme/duotone-light.css
+3e94a83580af1936a6ad6b38ccaf6621 ./admin/js/codemirror/theme/ayu-dark.css
+07c2ae05cb4ae14d5445b543aca4b789 ./admin/js/codemirror/theme/mdn-like.css
+94ad50bf3d048ed92cc513cd901dc685 ./admin/js/codemirror/theme/zenburn.css
+2886072b53043c167e6f8765606c705c ./admin/js/codemirror/theme/neo.css
+f9b47b8807f6472cb292114e0e69edf5 ./admin/js/codemirror/theme/liquibyte.css
+2a76022ff4c347a04c566df8a3ffbd96 ./admin/js/codemirror/theme/gruvbox-dark.css
+851ad806585c974aa204793b40b49f7f ./admin/js/codemirror/theme/solarized.css
+3428fab27f67b7795284457d84c3b1b9 ./admin/js/codemirror/theme/cobalt.css
+cf9366960ff65c8101793bc64fe13e88 ./admin/js/codemirror/theme/blackboard.css
+ec3bed26db530efd410358b94032d9c5 ./admin/js/codemirror/theme/material-darker.css
+01f520b8bd3533f2ca06b6dc4a0886b7 ./admin/js/codemirror/theme/monokai.css
+9ca57cb364b1c72cd8f15603425f0df1 ./admin/js/codemirror/theme/panda-syntax.css
+a40a918802530d402db8e194bab31ba7 ./admin/js/codemirror/theme/material.css
+b58c93881a92a0991d427679181aa6b2 ./admin/js/codemirror/theme/abcdef.css
+256f2dd130b80c6afaa40fddf700d12a ./admin/js/codemirror/theme/ambiance-mobile.css
+048659c7669ab4ac3c418e4aee533e32 ./admin/js/codemirror/theme/ayu-mirage.css
+73c8f41583b4b71dbe1e5eac5c96f1a9 ./admin/js/codemirror/theme/3024-day.css
+02ff81d8fbd29d5aa6f205452440da4c ./admin/js/codemirror/theme/ssms.css
+62e4abf3c908bbcdae55bc270b942801 ./admin/js/codemirror/theme/vibrant-ink.css
+225db574f347785986fe6bff476ccc49 ./admin/js/codemirror/theme/seti.css
+76a77ae36b82353c301f47c573052b66 ./admin/js/codemirror/theme/yonce.css
+a4497729649571f95f2fbd660fc3e0f7 ./admin/js/codemirror/theme/colorforth.css
+0a4227e805a9d5f73a55dd248c1b052d ./admin/js/codemirror/theme/elegant.css
+41905bdb70daf702a6089a4b094e3e21 ./admin/js/codemirror/theme/dracula.css
+4066167a1a40fdb6a2b80f56dc8060dd ./admin/js/codemirror/theme/pastel-on-dark.css
+e245bbfd22b4f61efe526ff13903f19e ./admin/js/codemirror/theme/paraiso-light.css
+fb6733bae4b418acc71e54423a8cce6f ./admin/js/codemirror/theme/night.css
+2696d4e2830c1bf32e17b49727cee186 ./admin/js/codemirror/theme/midnight.css
+5ceb5531fbe074d5190b55e8c725051e ./admin/js/codemirror/theme/tomorrow-night-eighties.css
+677084b334831e83d67ff7413a83bd2e ./admin/js/codemirror/theme/oceanic-next.css
+1e58608d9e179397a43f51d8973f647f ./admin/js/codemirror/theme/eclipse.css
+e1d25281a0300914a8f3b2ee9f2a5b58 ./admin/js/codemirror/theme/lucario.css
+8c1e7bcddd0ffe9049941a5983cca37f ./admin/js/codemirror/theme/duotone-dark.css
+d10b455f6c2790a9889755cd089d4137 ./admin/js/codemirror/theme/lesser-dark.css
+539151fcdbbdd6e2f42ec3dfe96223ee ./admin/js/_blog_pref_popup_posts.js
+867c98c71f13144eb977502d1c5bfac4 ./admin/js/_categories.js
+f8d3bb7b92ddefee917b7f1686a89970 ./admin/js/confirm-close.js
+30a52becaf07be2283bfa718c095f9e4 ./admin/js/_posts_actions.js
+9e968239e9e66552b37efd94eef4584b ./admin/js/_media_item.js
+b73e83eaef6a2ee50434f374df8393b9 ./admin/js/_trackbacks.js
+1967cf684c341c6d22bd17e34abf19a3 ./admin/js/jquery/jquery.pageTabs.js
+0a97cf1b38c1bf7e8d94d978abe1f6a0 ./admin/js/jquery/jquery.pwstrength.js
+e0b927264723339e80dd4aa46b0c9656 ./admin/js/jquery/jquery-ui.custom.js
+6ce247bbb5b1908b07bc3389bb40235c ./admin/js/jquery/jquery.mjs.nestedSortable.js
+69176657327fd02cd3ad9bbe9e06b399 ./admin/js/jquery/jquery.js
+fd180846c9d1135579adfb367a41bc47 ./admin/js/jquery/jquery-migrate.js
+4e7590756bd9433c14c2a88a9f311a24 ./admin/js/jquery/jquery.autocomplete.js
+9ccad6f9606f7d0e9b7a9b8d2b5547b1 ./admin/js/jquery/jquery.ui.touch-punch.js
+51be7f1476315ce5fc888b53d911bcae ./admin/js/jquery/jquery.biscuit.js
+636d2a9bbe2c9c6b21b2e5dd0f02f00d ./admin/js/jquery/jquery.magnific-popup.js
+aaa70fd15f2efbf710ee48b9fcf58a13 ./admin/js/prepend.js
+9b8974d3b75bb8d69fbc10dfce669ac8 ./admin/js/_posts_list.js
+530331d4a9805d0ca5063f2e338d88c4 ./admin/js/toggles.js
+96434980106de50f91fd5d1a7cdfc9c1 ./admin/js/_install.js
+7f39a615ee321c8cab14220ce42643c3 ./admin/js/meta-editor.js
+37a7a8f1b72130c91baa4ac00cf4c373 ./admin/js/_update.js
+0ce7aa7a1c588019b51e039f914263dc ./admin/js/services.js
+982fd005df6beba27284811740cbffeb ./admin/js/_comments.js
+c584d3bc2d8c453178c1ed0b29c560dd ./admin/js/_plugins.js
+6ae041856bd755725fa4923d44721e31 ./admin/js/jquery-mute.js
+8fe096f2896839fb2dd00f12fce0f831 ./admin/js/_comments_actions.js
+41814973156f5578fd0a7fe21d5b4324 ./admin/js/codemirror.js
+1af54aa63ef76855f86b053bf6074108 ./admin/js/_category.js
+bc35bbbb76605a9c1fd28c007a9edb5a ./admin/js/_blogs.js
+eb4d9992f680cdfdeb459e90218d48f9 ./admin/js/jsUpload/template-download.js
+a4e17dc8e0c91b64def859f031dd0778 ./admin/js/jsUpload/template-upload.js
+64513a699578700ffb4935816e1cc65b ./admin/js/jsUpload/jquery.iframe-transport.js
+b36b43986394bc5c091fbee46432a185 ./admin/js/jsUpload/tmpl.js
+db7c463fd0722852573565a66018c6af ./admin/js/jsUpload/jquery.fileupload-ui.js
+8018e33b051b6820912f8132f9d8aec0 ./admin/js/jsUpload/jquery.fileupload-process.js
+b9eb3154042c9cf3bc35a0a696d80174 ./admin/js/jsUpload/jquery.fileupload-resize.js
+7ea0de538ad380e9b0d731ec955516b4 ./admin/js/jsUpload/load-image.js
+2172acf4ff2ffc61fa63fbf20e4382ab ./admin/js/jsUpload/jquery.fileupload.js
+3506684bf2aa9aae4faa16bf52c64321 ./admin/js/_user.js
+c102dc64142f815f5494438ef12e4dba ./admin/js/file-upload.js
+fde8953160d766d305634627149ddb1c ./admin/js/_popup_link.js
+f2b7204b2a676998a0160bb615e2e77b ./admin/js/_users_actions.js
+09e125e1a3ab99e3ed8210cdf34b63e1 ./admin/js/page-tabs.js
+34268f21a34c3ebfc0ba20b4c4b49fc9 ./admin/blogs.php
+82e7fbd2744ddc06113091a9e99dee2f ./admin/posts_actions.php
+31ab85c0119945f0d0b34fb77172644b ./admin/blog_del.php
+3b0aa78775e47f479f55bb8139ce90d5 ./admin/update.php
+c34db19f88c74c1fb731883f142a0e0f ./admin/plugins.php
+e2345b563000e82fb9b715be3ee4bf25 ./admin/popup_posts.php
+e273a087285082cccaed8895bc669e79 ./admin/style/scss/default-dark.scss
+9993a2785bc13688525182b9038f9d81 ./admin/style/scss/init/_config.scss
+0ce8c5d98f6982f994ca533b75503baa ./admin/style/scss/init/_rebase.scss
+9e20fbbc7a8bb5161925f9e9bb222a62 ./admin/style/scss/init/_mixins-functions.scss
+b946683b7ce6829498c4aef137e4ce31 ./admin/style/scss/default.scss
+f8ffa5197dec794a8390152ca30145a3 ./admin/style/scss/partials/_filters.scss
+dca65f67a8094af193c946b5d903f339 ./admin/style/scss/partials/_content.scss
+1883f9e43208b96e46a22f13a5cdd93d ./admin/style/scss/partials/_forms.scss
+6d7b62242f21e5bcece98e6e473a1353 ./admin/style/scss/partials/_classes.scss
+2dc7872342e38142bfc0075366a7338d ./admin/style/scss/partials/_common.scss
+e8724fe21f2b80173084fa828730edd8 ./admin/style/scss/partials/_messages.scss
+b7600e28888ea4dec1759de92fe475b8 ./admin/style/scss/partials/_categories.scss
+b00190485d1a14525ac8042385441d16 ./admin/style/scss/partials/_utils.scss
+566f8a79dab62990c7b784ee629221f3 ./admin/style/scss/partials/_media_item.scss
+c4230063d279ea22bf8249d4ca0c3c66 ./admin/style/scss/partials/_preferences.scss
+66d497869b40275a7a4fa12a5d07e90b ./admin/style/scss/partials/_debug.scss
+dc13dfb0cae9fdbd492ee1e74bb521d0 ./admin/style/scss/partials/_entry.scss
+49081bf898cb84f8f34f8bd720371bfc ./admin/style/scss/partials/_charte.scss
+d8a65d50ec9c47f334f0cae05aeed5b9 ./admin/style/scss/partials/_blog_pref.scss
+d3c1e92ebdbe7f1c9f4089fa52011e3b ./admin/style/scss/partials/_layout.scss
+a106da8cc53f0a19c1c00d216aa09e40 ./admin/style/scss/partials/_footer.scss
+f93f4e46cacb8136749109dcff698f5b ./admin/style/scss/partials/_main-menu.scss
+31a6bdc16a0596ef18e1a18b36b57120 ./admin/style/scss/partials/_editors.scss
+6c336d76ad9c2a699f7b90fdac24738e ./admin/style/scss/partials/_plugins.scss
+c1ab7db2679b48c77bfe34046d28dc7c ./admin/style/scss/partials/_auth.scss
+8af6d42e5fd70a13e2f85c00f2d935f6 ./admin/style/scss/partials/_blog_theme.scss
+cb55e9b236c82799fc5ee2047996bb23 ./admin/style/scss/partials/_markup.scss
+8ea7b53f9f761ab110903a4cbc774774 ./admin/style/scss/partials/_index.scss
+aa3a3a0168e9173ae054ace9a422b276 ./admin/style/scss/partials/_buttons.scss
+2ec38d170fdcf30a79d5bc07d50f0990 ./admin/style/scss/partials/_mediaqueries.scss
+70056b9af42040b0316c8a10f17e99f5 ./admin/style/scss/partials/_header.scss
+c5f5dea05faaf423a5ea6dd64180d3b5 ./admin/style/scss/partials/_user.scss
+93c8ae9654d7f6977ebc9a7a50aef11a ./admin/style/scss/partials/_tables.scss
+a52583e131a5948f0db2496b295db08b ./admin/style/scss/partials/_media.scss
+5d837c3aee5c9b16f9f78bc68737000c ./admin/style/scss/themes/_dark.scss
+7b57a8d0b3430fbfbf246007d3c4680f ./admin/style/scss/themes/_light.scss
+d492ceae2579c5dd7ec4e522f9cc74c4 ./admin/style/scss/vendor/_magnific-popup.scss
+213741a0c87a4014b81103c83ae3cd7d ./admin/style/scss/vendor/_codemirror.scss
+97cd56a41bfd548d310f5bd9e407de57 ./admin/style/dashboard.png
+f76a8110ae6ff0aad3aa76ff08aefb3e ./admin/style/msg-error.png
+00d7898f848b82ea35f0ffb5623beb59 ./admin/style/install.css
+838f5bbe0ea69038d24164f4a3b9b6b6 ./admin/style/dc_logos/dc_logo_footer.png
+fdb483f525709e73d06a2a92dc223ede ./admin/style/dc_logos/b-dotclear120.png
+a80b737ec8c74b236e0f69bd393de3cb ./admin/style/dc_logos/sq-logo-32.png
+099aa015d55d0cf03fd47afb7450339c ./admin/style/dc_logos/w-dotclear180.png
+a03c553f94b39ae0b63a4922e29e2704 ./admin/style/dc_logos/w-dotclear90.png
+f67502d7c09726517fc1ae666fd991c9 ./admin/style/dc_logos/w-dotclear240.png
+05a97293fa04cf80ba1da3df12d0ff29 ./admin/style/default.css
+f50d645d84a96f8746d8d5d9c9458d51 ./admin/style/config.rb
+f42e37929a2bf50b1abdabc71e308b7b ./admin/style/grid.png
+150b740b4124f1bc25ca10da922fd546 ./admin/style/default-rtl.css
+28b05f45bf3a576a967eaf0196367e7b ./admin/style/msg-success.png
+f32881bbbd1d7d8c23ca561f49f67488 ./admin/style/msg-std.png
+8875406591b829b338451ee035c53ac2 ./admin/style/drag.png
+63f77fbfd76d330b33a4b279cbf237aa ./admin/style/help12.png
+93f541c0a52922859e7dda2d67ae8c10 ./admin/style/msg-info.png
+deb5c791e787615f52941227f045b37e ./admin/style/loader.gif
+ce91ceafe92e5e80711b9032b0f52de7 ./admin/style/loader.png
+dcb1696cf571a28ae0a3925e137a5f5a ./admin/style/dashboard-alt.png
+5360581d80c99726f8ef96a09e1756c6 ./admin/style/settings.png
+631d03bfd4799d22846f935a9d44be2b ./admin/style/search.png
+6a12e31fa3817956ff74c2a6f1a3b07b ./admin/style/search.svg
+fbadfebdc72fd01042411daffb89e855 ./admin/style/user.png
+06d3d4ce08be4018094658b43d62b17d ./admin/style/default-dark.css
+fb7fa8b68292e2a9b28a1747a8f98549 ./admin/style/msg-warning.png
+99eb9cff557600cded5855e9a1ca2253 ./admin/style/date-picker.css
+f32881bbbd1d7d8c23ca561f49f67488 ./admin/style/install/note.png
+f76a8110ae6ff0aad3aa76ff08aefb3e ./admin/style/install/process_warning.png
+fb7fa8b68292e2a9b28a1747a8f98549 ./admin/style/install/important.png
+cf94130151061cd8bef13c5646f175b5 ./admin/style/install/w-logo.png
+8d29fdf20ac103125d18dcf042c8eced ./admin/style/help-mini.png
+0e0edddfb9a2da5b2cea8ea22e8100b6 ./admin/style/cancel.png
+a16d7f2f32d707ea3cd1de7b8750d420 ./admin/xmlrpc.php
+67e294b08150ff9cc7bc961765be8279 ./admin/auth.php
+d7f96bac76e879559ee667354d9c016e ./admin/post.php
+7694fff2522b122aa0aa1486a4bdcf26 ./admin/blog.php
+2c4253fd589bb73335d931e11daa6deb ./admin/plugin.php
+66e03344a089df34c859f062adb87523 ./admin/users_actions.php
+4596be46ce451c259dddfe561474f38f ./admin/media.php
+e515f6058252d90278f20f01656afda3 ./admin/media_item.php
+7b4d5e2a9c5d8acf64d8df3d8ca249c4 ./admin/images/list-off.png
+8e270369b4448eb391c678d1c7aed68f ./admin/images/plus.png
+ea45a522b2345a79a2abbdb7b1fdc577 ./admin/images/page_help.png
+53b35f34a62c1c7b5af9e7b02c937d1c ./admin/images/menu/blogs-b.png
+7db5a1d740ff67681233e0e04a21ed87 ./admin/images/menu/edit.png
+e7455818bbc05f957611597b5d5d8e80 ./admin/images/menu/blogs.png
+f84d08338ea3635cd34ee4b8ecf9e9e6 ./admin/images/menu/categories-b.png
+c0c7403faede03cc5f75ee012a42b75d ./admin/images/menu/langs-b.png
+e1ae7a61f8279806b8d6a6947b422da8 ./admin/images/menu/update.png
+41c279174c832d0290e1ea995fc57136 ./admin/images/menu/plugins.png
+4d043d0996976426187838a72ef2802e ./admin/images/menu/blog-theme-b.png
+19e6b5d64a80867cbf8c4616e00c0475 ./admin/images/menu/blog-pref.png
+a9185891ddb596f947ecb8e2f8b98651 ./admin/images/menu/plugins-b-update.png
+ada57bad1fad1926a9c23d8e0edabaf2 ./admin/images/menu/themes.png
+200f059bb03ea74bfee257da3b240121 ./admin/images/menu/help-b.png
+509e044a9f15890415e833f2d48fe97c ./admin/images/menu/search-b.png
+861c08a775be94c9865d35e942cd4153 ./admin/images/menu/plugins-b.png
+17ad144e8f715ed5bba4e2c11848e004 ./admin/images/menu/media.png
+236308b1108e7696e838ba815d68413b ./admin/images/menu/user-pref.png
+3153589b3fff2b76fb87184905704046 ./admin/images/menu/entries.png
+2bb42e236d158a3376e00afe550a37f2 ./admin/images/menu/comments.png
+ff0f3f0fb29bcce863999fd70fa4d9e5 ./admin/images/menu/users-b.png
+47b5d96cefd4d1e2eea279b1c265acac ./admin/images/menu/media-b.png
+a7c3711678e184edf7efd2b33f019cec ./admin/images/menu/search.png
+4aeb5e2ccbb865324976c1858ebfd8c9 ./admin/images/menu/user-pref-b.png
+064bed55cd29833cbb3870c1174a38bd ./admin/images/menu/blog-pref-b.png
+724b99186c5ff0e930b48a51fda3fe86 ./admin/images/menu/entries-b.png
+e4fdf3673ff93e48330c50cb3dbabc4d ./admin/images/menu/users.png
+6ad669ea81acb21172b1fc80880659e3 ./admin/images/menu/blog-theme-b-update.png
+ea7543a11641bbe58ac119ec4cd5c5fe ./admin/images/menu/edit-b.png
+fd19f4e734928baf854083ffb85febc7 ./admin/images/menu/comments-b.png
+12e167c5341a34122812f605fa9ea4c6 ./admin/images/menu/categories.png
+c800ee1b814865e82cf7bef29b0e6cd0 ./admin/images/menu/help.png
+5893c5c3a5edc204630febbd6ee48a77 ./admin/images/menu/langs.png
+1f3d3f670abc8364f7ed2a8ae2f0ee15 ./admin/images/dotclear_pw.png
+6e235954c5046c66b23ca80d13149859 ./admin/images/favicon.ico
+5d45288e70e1b1ba61a9a2f01e1ef0dd ./admin/images/plus-theme.png
+f0f649e0ca8ade9744b8d63248bc280b ./admin/images/superadmin.png
+dcac72c04bf7b50cbdc5486354ec1272 ./admin/images/pagination/no-last.png
+cbe566f5830735c7479deaf6df483308 ./admin/images/pagination/first.png
+7af5a42ff161b4aaaf7a6c9fd1b6b61c ./admin/images/pagination/previous.png
+f60ee8e99e49eb7070ff4ec5f3f8dae5 ./admin/images/pagination/no-previous.png
+3191247b2322623f61fab6631e0b2f82 ./admin/images/pagination/no-first.png
+a2489f4f1f6fd54b5363bcc2fa71999c ./admin/images/pagination/next.png
+d15a02610e1a0bad288dc2f99ffc3f2c ./admin/images/pagination/no-next.png
+8cc8e911400ef342b3c1b63e8161cf4c ./admin/images/pagination/last.png
+c8a3d2e1906aa2fa8b525baef5c28fb6 ./admin/images/media/video.png
+4424423329d679fd388aa0b6453f0724 ./admin/images/media/blank.png
+6d2a2f454b8c3b52c6c7e1e355e9a583 ./admin/images/media/spreadsheet.png
+bb3479dbcdf1a7c155336568405b6aa9 ./admin/images/media/package.png
+69b0e1d5a5ba710dc05e5fcd519ee225 ./admin/images/media/html.png
+9bad33b0c3e586283839b8703d3c5a98 ./admin/images/media/folder-up.png
+73c30e679636a90f279889183570f988 ./admin/images/media/document.png
+c655c2706dc2dafbca360ed287b375b9 ./admin/images/media/audio.png
+5c00b60dfcc480419ee258c83e930067 ./admin/images/media/text.png
+b875b027a49923d5ca6b5c2979fa7ee6 ./admin/images/media/image.png
+a0f86ae468251912f39b361d8bbd377f ./admin/images/media/presentation.png
+3ef8257fe2e4ed77659bccb3ca42f8a9 ./admin/images/media/folder.png
+44640bb19af8e7216d47e93f92d1bc00 ./admin/images/media/executable.png
+1fabbc15cfa614eebb7b5b25a0704490 ./admin/images/trash.png
+eeb0be1574aa2a4e4fdfd762658c75a6 ./admin/images/module.png
+d31ba7a1d3c0bb1d9797d9ad53cfbd70 ./admin/images/grid-off.png
+c40d6dc012c5356e341722d4f9695be8 ./admin/images/down.png
+19c2eff251d254fba2ff5257407abee2 ./admin/images/selected.png
+0bafe0760f2d3f242b03c9006e387773 ./admin/images/menu_on.png
+b919c92959bf20b0baf680d8062b25fa ./admin/images/outgoing-blue.png
+7b6c48e5757b53d6594836cf609291d2 ./admin/images/check-off.png
+30f5f7c9cc529e968788015073b49721 ./admin/images/disabled_up.png
+f04d7767e2e7607cad0ba8aa027c82c4 ./admin/images/admin.png
+4258d4dabf3f865e95b61e22f91d7e1d ./admin/images/scheduled.png
+8cfde03b54a0ebb05d2be8a3b28b3cbc ./admin/images/fav-off.png
+8609f8f388e49503cb12d2f5feaeca8f ./admin/images/picker.png
+d1d3829203cb9997d85ea3f02ecf6b33 ./admin/images/hide.png
+e64569a20ce307641ddec1ba33ecec33 ./admin/images/collapser-show.png
+b0f2a4cc0cbdadccfa6e2f12c9d2f2f7 ./admin/images/junk.png
+21ee138180270e8447192ad9e511b18b ./admin/images/minus-theme.png
+6cdca52e75eb94c47c5eab364960b997 ./admin/images/list-on.png
+3ee190189b4550fa09607378a732368b ./admin/images/menu_off.png
+f4d80a70608d34eb956c71463cd2b12d ./admin/images/palette-traviata.png
+19d1a321f8df4112d9efa704c92f3356 ./admin/images/comments.png
+12f2eab9a8a04c6397bd9deab39aed8e ./admin/images/check-wrn.png
+832332e41c8a69bb6c6955f8d73834e0 ./admin/images/up.png
+93a95a0ee74983f986e14564e35ff24e ./admin/images/favicon96-login.png
+1762562d36bda02cf7c524d2f43ad8f9 ./admin/images/attach.png
+a3fd221fd10722d62cd00bca8c33ee43 ./admin/images/attach.svg
+0128207b7cbff9cee9031421b089cdd4 ./admin/images/close.png
+97f6fa49017d058ec9bd9655d6c77866 ./admin/images/dragndrop.svg
+b082a00424c3e53b898516abf0c30b09 ./admin/images/grid-on.png
+d595e882fbfb7ab2c03ba63e18be87d4 ./admin/images/expand.png
+eadb497a41197c92c554e1341bdef3ee ./admin/images/outgoing-link.svg
+45fdb1fdba3a1bf72c35465dc852bcea ./admin/images/locker.png
+a7ad816ea834d6e9e6860d9af94cd3b8 ./admin/images/collapser-hide.png
+a3f1b141a9df47f99e8318a1c56eaa05 ./admin/images/favicon96-logout.png
+d1ece81ac317796cefac3aec8d49417e ./admin/images/check-on.png
+8fd4d3a33407db440cd87278e7150e39 ./admin/images/fav-on.png
+97c334d8d8ed1ee575ee07202208b359 ./admin/images/outgoing.png
+f4dd6ff8d320364e464760ace8926736 ./admin/images/date-picker.png
+f6748962038c49f695ff2e18f0968f97 ./admin/images/edit-mini.png
+e81adcbde27934d164ec92f95fea1cc8 ./admin/images/disabled_down.png
+4a9b92472a39e640221b30c4b00f7a7a ./admin/images/hidden.png
+dc81fab7a1ca4cacd3f6e9cbb20caf7d ./admin/images/noscreenshot.png
+f7cb064174236d926d2a650fe63dc016 ./admin/images/logout.png
+5829fee6bc6c460583121ba679316e30 ./admin/images/trackbacks.png
+823de045875b9f5aeda733154fe0eb5e ./admin/index.php
+6efcbe1b0a6448ec1e0d1b54175bd7bb ./admin/csp_report.php
+42e259ae63bdea354a8af71d863ea316 ./admin/posts.php
+74c6ccccfa9cbf038eebb767d939c75c ./admin/comments.php
+f906343563a69fc9c2d7918bc9dfdf4a ./admin/popup_link.php
+5589bab97ab164cbf4db29fc30ddb10d ./admin/services.php
+37dbe1599c1c009cd75ff09baf0b8637 ./admin/search.php
+caebd342c7dabe8ad33c04ff9a240b95 ./admin/blog_theme.php
+3b1102d8bc7c77426c486f4b960f9c11 ./admin/comments_actions.php
+ee5b35b94d97af77b66177616ae5090a ./admin/comment.php
+241dac8de1ad65dab9940f1970bc2f16 ./admin/user.php
+f1b59fb977b3e1722dcab060f826d585 ./admin/preferences.php
+97d495ccdfab234bb312676a597b2b04 ./admin/users.php
+fdbcf612e0a740db2704733050816b0e ./admin/install/index.php
+d656ad088080fe08d655097bd49a1db2 ./admin/install/check.php
+752fb15385b5f75da7bf573b8433637d ./admin/install/wizard.php
+0ee4e4be5fe2269f5685c73a7e8884c9 ./admin/category.php
+495aa970d0653a30d88f2c1d6f6d882c ./admin/_charte.php
+189b43f2dde6e5533494527c63f06594 ./admin/post_media.php
+192d23c8e0f04023f52cbee9c7550698 ./admin/blog_pref.php
+1242cdbb64fe3c6fa55b8bc99c261898 ./admin/categories.php
+103ff92e52b1578f6ee218b98a173ea1 ./admin/help.php
+e2f2186f72e021a7bf42047aac05d051 ./admin/langs.php
+5df7acf61e776af5ae6063c5ed824dad ./README.md
+57dd67bded4350112bef29a8042a0b78 ./locales/en/date.po
+b51ac83ef53104b7795aece322dd0226 ./locales/en/help/core_plugins.html
+494a4b8ba0937a543d0efb1c55253d5e ./locales/en/help/core_dashboard.html
+23b61dd0b458b57353670a20eb619545 ./locales/en/help/core_media.html
+d6c2ff782d89034a9fbd1651852c707a ./locales/en/help/core_blog_new.html
+132e5da3bf3c2691177a325b5cf04364 ./locales/en/help/core_user_pref.html
+31f4de8529461d0471feb71a5420adc8 ./locales/en/help/index.html
+ae1a19c8ae1e9515ce2b51336d00fad0 ./locales/en/help/core_category.html
+fc51ec4fe28cadc83913b369fc77d65a ./locales/en/help/core_comments.html
+54c4c30196dfd7716eb82f26b48abe04 ./locales/en/help/core_users.html
+efb53390937d332b7b5a432ddc9f6f0a ./locales/en/help/core_categories.html
+36c74e1954086984e8b40f09eb7f0638 ./locales/en/help/core_post.html
+5138f3667cc0e41f84a513ab05fc5cdd ./locales/en/help/core_langs.html
+41b29e23d3038964aee7a3101b77d2cd ./locales/en/help/core_posts.html
+d0ff569870c9bb01bd3f2cdd9da05a3d ./locales/en/help/core_wiki.html
+37f9967bf336a33fd0a84a85212c66f9 ./locales/en/help/core_search.html
+1c368099d0cd9360eabc2d7bbb970986 ./locales/en/help/core_user.html
+bd05b25bc7b3003881b97b4e86058601 ./locales/en/help/core_trackbacks.html
+f1708dd3c4440fa9936aa7eee3267d88 ./locales/en/help/core_update.html
+53534ba5d3928ff1debcf0080c98e674 ./locales/en/help/core_blogs.html
+ebf8e6bf5a9b14a9b920c0a229df73e7 ./locales/en/help/core_blog_pref.html
+aa01f4d2f7ecab8a7ff9a1acd7549e57 ./locales/en/help/core_blog_theme.html
+b11a8c7e30806f289333fbd7e6703aba ./locales/en/public.lang.php
+168123878f9772d0bcc165cfb1f8ff26 ./locales/en/date.lang.php
+164215d1afa93434832596b8828f1410 ./locales/en/main.po
+58dcfe53e742d196a474761cc7896408 ./locales/en/plugins.po
+16a3ca01951623066fa57e6d6782a168 ./locales/en/plugins.lang.php
+ed6f942785e8247da2ccb347b9459891 ./locales/en/resources.php
+77b047423f6c6b51f14b30cd4d609d9d ./locales/en/public.po
+6df929a28facd9ac0d0fd3261b89ed56 ./locales/en/main.lang.php
+700d98599f6b656aa606876b6b83f74d ./locales/fr/date.po
+00aa186c1da351bf4be30cc635b87838 ./locales/fr/help/core_plugins.html
+8b07509bd4f8ac23423df251d1427319 ./locales/fr/help/core_dashboard.html
+f6b0b5bd34f0724e3c50123d8c45b6d5 ./locales/fr/help/core_media.html
+99422ea460ea636ecbd5a1c10ef31f37 ./locales/fr/help/core_blog_new.html
+13df94f717c76176618e1e22dff0bae4 ./locales/fr/help/core_user_pref.html
+5612842b521206d5b55704554272930e ./locales/fr/help/index.html
+de390162a4b697a30b667deebad00248 ./locales/fr/help/core_category.html
+b52aae46739027c160929e3ff35d348c ./locales/fr/help/core_comments.html
+808e6fe2891fd6f952ccf6793d0e4a06 ./locales/fr/help/core_users.html
+b24305d413f38c54880f64cf2d90646b ./locales/fr/help/core_categories.html
+d8f9179c5206b3341338885b4d472dde ./locales/fr/help/core_post.html
+26a64ac6abdd599b2c8a85968b9c66a6 ./locales/fr/help/core_langs.html
+5389eb085975569e44ee6ef7fd2ecf89 ./locales/fr/help/core_posts.html
+74f7340b78376cba55a94875338d95c5 ./locales/fr/help/core_wiki.html
+6b6199d6f75d6f19d7144ffe4d38eb30 ./locales/fr/help/core_search.html
+55106cee6b670fb52b833702d2125f84 ./locales/fr/help/core_user.html
+4d751a29aa29abd6b13f8521e17868c5 ./locales/fr/help/core_trackbacks.html
+aa6c495927a582702b5506ff0ab03bbd ./locales/fr/help/core_update.html
+657822b7f3a151d7e84e189892d01cbc ./locales/fr/help/core_blogs.html
+36ad18c646ead0d8f06eb06c38627c43 ./locales/fr/help/core_blog_pref.html
+2c2a800c1b78926831824ce7a9c8dc7e ./locales/fr/help/core_blog_theme.html
+6339c7b35d3cf2d5931ede8d2307c73c ./locales/fr/public.lang.php
+d443a98c6839203651400d49c360ef38 ./locales/fr/date.lang.php
+ad5592d155d94fed31c7c1057eedcb12 ./locales/fr/main.po
+c3db1533124624b422bb1f94baeb7e4b ./locales/fr/plugins.po
+d334ee510f2ae47b90de5bef3c2a273c ./locales/fr/plugins.lang.php
+0f2bcdcc6f77e5aaafbcc0e3ca5bf47f ./locales/fr/resources.php
+27e7383b00d7709a2371567fe06e73f4 ./locales/fr/public.po
+e3a34dcf77925a0a4c5193547ead6375 ./locales/fr/main.lang.php
+5cb41fd34fd50a2e4071674e350f219f ./locales/README
+a10b0e0489fade96ca82b1a1d3c67095 ./index.php
+a71bc3c3af01c8ea2202fe15a9dd2de6 ./themes/default/img/trackback.png
+b95cd33cda5f506fd7e83ceb47c40efa ./themes/default/img/commentmy-b.png
+b5f5ba0ce0dce98121253b67a3b11d18 ./themes/default/img/commentmy-t.png
+e2377dbbbd8bedf737c8549ea9acb2e3 ./themes/default/img/page-bg.png
+1161f08ecf0276f36b003f4f8cc98673 ./themes/default/img/comment-b.png
+ef513e7c9fcd5632ba47caed1358699e ./themes/default/img/tag.png
+62d60a3cc35d7d1f6f4712682ea7f169 ./themes/default/img/comment-t.png
+02db797fd998e4b560893294b1198105 ./themes/default/img/page-b.png
+d2291d5d5f11b778ce3cc286d28aae8d ./themes/default/img/page-t.png
+ea90518f318c6c9ffe0a1c10f74df13f ./themes/default/img/feed.png
+88193b955356bf80e96c9ec4149cac87 ./themes/default/img/attach.png
+5f69e4b5cb11f750389973cd42b14cbd ./themes/default/img/comment.png
+9ec3d6953e6f5633c637564dd41686bd ./themes/default/img/body-bg.png
+2a27b9adb499beda3661dde8352119ff ./themes/default/_define.php
+021f8dc3e922c1d5a0f6af6df137648b ./themes/default/style.css
+916dbb52a27aed8fb315f8e6ce8396c6 ./themes/default/smilies/laugh.png
+db67a415b99002ef2e0dccf82684f4cb ./themes/default/smilies/cry.png
+ede319bcf37eb7fb3ceda9a4ba15e805 ./themes/default/smilies/confused.png
+9066339d2c5b735aad88ad52e7aa20a0 ./themes/default/smilies/sad.png
+c642230c499e956b5bedc0f39a77c02b ./themes/default/smilies/redface.png
+cbaf88260eba807bda24e7e6b3eb52e9 ./themes/default/smilies/eek.png
+b3d832f47e39f1e7be211e1b5d284e75 ./themes/default/smilies/dizzy.png
+103ae5555ed762ccd40d545b30c68175 ./themes/default/smilies/idea.png
+3de2bed45015c22bb9a9912b28ea7e79 ./themes/default/smilies/exclam.png
+b40650a1e907eb2f40a0273ceb6d6c12 ./themes/default/smilies/lol.png
+08b39e2fb006e47cbdf73fd240cc3422 ./themes/default/smilies/rolleyes.png
+ca45018c65b49b37f207acd375b277ff ./themes/default/smilies/cool.png
+7ba52d8580d8803ce151ea3d99aced3d ./themes/default/smilies/mrgreen.png
+d3671aa8ccb54bbc9b89a83ba202b626 ./themes/default/smilies/smilies.txt
+3a9b3348561ec6e3801fe6d61795a0c0 ./themes/default/smilies/alien.png
+c3532867f015912592ec8d921637de01 ./themes/default/smilies/smile.png
+2effb85fca1321137cd66388030be2a9 ./themes/default/smilies/normal.png
+18c136d628415a3fab6ee98401e1238a ./themes/default/smilies/evil.png
+7c676e33a76e8b0eecf0c6d360e30e35 ./themes/default/smilies/angry.png
+6e65820fe60294bc74a020842425c0f0 ./themes/default/smilies/wink.png
+8a6892e8420cbfd919145cb4aab4a3af ./themes/default/smilies/razz.png
+6a515ed2eeb57f8ee9b4c75431c11f2a ./themes/default/smilies/arrow.png
+ccd0b6ad80ad381ce2e4d6886b144ac7 ./themes/default/smilies/question.png
+ea84250becea90a9883582412be0d551 ./themes/default/smilies/surprised.png
+59a37691a0ae546a67369bba244aa738 ./themes/default/screenshot.jpg
+92b23cb12187ed316a73b342ade6626a ./themes/default/print.css
+801230e2084b4de01e5f0574d2414036 ./themes/ductile/img/menu.png
+eb1cf538fb97afb3bfb7b899f6052a78 ./themes/ductile/img/sticker-contact.png
+5b6e25b399b91b54d3ad658bfdd5923b ./themes/ductile/img/filet.png
+d25435a42b49549b3297b32cba7ab5ae ./themes/ductile/img/last-hline.png
+8897b5179bc8f21fa56273dd5eeb596f ./themes/ductile/img/post-info.png
+fb7d2ede6ec9d2295b20eb35e3db8468 ./themes/ductile/img/sticker-about.png
+2df82e3ead0cdcfa449c98c4d4e02614 ./themes/ductile/img/squares.png
+e275ee0651f0541f9bae9228d110ac56 ./themes/ductile/img/404.png
+f9bc3bb4c9ef94ad984a6eae3f44d492 ./themes/ductile/img/minus-plus.png
+e0cdf03fc691469d720377f27201cbbc ./themes/ductile/img/hline.png
+6e36f0a77362ae19b54e6c7b3cc34434 ./themes/ductile/img/feed.png
+3eac6b64e480d076b467ea7530eca009 ./themes/ductile/img/vline.png
+dcd527bdfc04fd8346a8c91221903d7b ./themes/ductile/img/sticker-feed.png
+0d5947a32b875cc554ebabe401f0631b ./themes/ductile/img/logo.png
+48f986b72e59336787434da7c783324a ./themes/ductile/img/menumobile.png
+c02c7680f51eb90ca97088d3e6d1853c ./themes/ductile/img/info-co.png
+8076edc1d6041f0468ef92d3ccdc6bf8 ./themes/ductile/img/download.png
+9771748371441d110b83d08f72401b5d ./themes/ductile/tpl/_sidebar.html
+6b3d12ce1abfe5dd9e2a596337f4a6ad ./themes/ductile/tpl/404.html
+e9185e8904a19cba3222da541515767e ./themes/ductile/tpl/_head.html
+6465f19463b7f1292bdd493a3b57c251 ./themes/ductile/tpl/category.html
+505d80483c1fb86aa066b9692b2633a7 ./themes/ductile/tpl/_top.html
+7918913a62fd1c49187b266035229998 ./themes/ductile/tpl/page.html
+76781038168349e702f584fb80ef495c ./themes/ductile/tpl/archive_month.html
+3a5193e2f05ecb90b7463054a9f27b1e ./themes/ductile/tpl/_footer.html
+d342c481fa500387d45a88671bd16ba3 ./themes/ductile/tpl/archive.html
+dd5480da6919c7c34cc98d25765f145f ./themes/ductile/tpl/tag.html
+285128f55e9c3e43ce5104fcf53da65e ./themes/ductile/tpl/post.html
+e87b87728be6ba92f9a7a90034fac890 ./themes/ductile/tpl/_entry-title.html
+e6e6026081ca871aea27cc12ee542d53 ./themes/ductile/tpl/_pagination.html
+ee249ef4483cc19ea8b8ba36680c6fec ./themes/ductile/tpl/tags.html
+46dc353bd0c51772de4a22c0859615a1 ./themes/ductile/tpl/_entry-short.html
+721ad5c59fbf12a1e8902a166d9039eb ./themes/ductile/tpl/_entry-full.html
+6636b2814716e8bdd568f75c124515a6 ./themes/ductile/tpl/search.html
+03f6cd1eb25342203e31791b69f65736 ./themes/ductile/tpl/home.html
+2e0395e61ce9747a2cb8a9bde92b9e17 ./themes/ductile/tpl/_simple-entry.html
+d5ce0c610a13e6d6a89747476d718d75 ./themes/ductile/_define.php
+a4666e3029f6a0589d52d755f3b05e79 ./themes/ductile/style.css
+461331e1dc1d0266d15be4c84ecc9506 ./themes/ductile/_public.php
+917671c8a29b46c280ecadc16b0c3aac ./themes/ductile/locales/en/help/help.html
+464d1bec47c94bd1318a1d0b7591d69d ./themes/ductile/locales/en/main.po
+932f40619361ed53099cb8bc7f16017f ./themes/ductile/locales/en/resources.php
+cf4945b4acb046cd2919e1c28ac18bc3 ./themes/ductile/locales/en/main.lang.php
+ad841e7d0c5259b806f7cc9fa1ae7ebe ./themes/ductile/locales/es/main.po
+bb9148fa5e79261d00a19ee5669fa98a ./themes/ductile/locales/es/main.lang.php
+6c51801104f237b6412ab46e5ee40df2 ./themes/ductile/locales/fr/help/help.html
+1c5fe19b27776dcca3dbe49eb9ecc106 ./themes/ductile/locales/fr/main.po
+932f40619361ed53099cb8bc7f16017f ./themes/ductile/locales/fr/resources.php
+a61e72a1b148dbe5e9fb24b89bc34fbd ./themes/ductile/locales/fr/admin.lang.php
+4adf562fae0359bd4aa36e1aadb89c22 ./themes/ductile/locales/fr/admin.po
+c04ace3e8f6a0b017320b4cebf051107 ./themes/ductile/locales/fr/main.lang.php
+44e3870b32f18ce3c178a92e20711911 ./themes/ductile/_prepend.php
+41268b2c9f4e7a588033689d7ff9f89c ./themes/ductile/screenshot.jpg
+0a8d41b740380ca0277feaf8a09a9a3d ./themes/ductile/ductile.js
+dcc8ca471cc91b5e86c298ff8b7bcbac ./themes/ductile/_config.php
+e537b29c47f97bef68959078af07fd54 ./themes/ductile/rebase.css
+a104dd619741e81138ba8b6d62e4e6f0 ./themes/ductile/mediaqueries.css
+0bf067395ed65e38c74618135fd924cf ./themes/berlin/js/berlin.js
+fec4a7b01e6bdba42907f81cf6b14ee4 ./themes/berlin/img/icon_close.png
+180efe7f58c61fb49178f5fd8903c804 ./themes/berlin/img/icon_close.svg
+fdbd91a7abdc10687d0ec0a933b27641 ./themes/berlin/img/bg_dark.png
+d25435a42b49549b3297b32cba7ab5ae ./themes/berlin/img/last-hline.png
+aa177eac1bb4df2d0b7931f33ea351d2 ./themes/berlin/img/icon_attachments.png
+88f69ee4c0f70a122fc8d7b8945a5987 ./themes/berlin/img/icon_attachments.svg
+0f31f9a05bf09b9dacd1d2c263d16322 ./themes/berlin/img/icon_tags.png
+30c9e8a7e9eb9e031bbf1fb5eaef86ca ./themes/berlin/img/icon_trackbacks.png
+3e8e52bc5662f1738a8589e9c8e064a4 ./themes/berlin/img/icon_open-sidebar.png
+f36183f76db42fbb42c5ed5bd7aa3405 ./themes/berlin/img/icon_open-sidebar.svg
+56781fdc3246cfd7d101a72782b2d89e ./themes/berlin/img/icon_permalink.png
+3c1279d40437d45d0ca8915882d2894f ./themes/berlin/img/icon_prev.png
+b5886b11eb39437f016e5f546e7df268 ./themes/berlin/img/icon_feed.png
+688c58dfdda2fd1626a94d856b8df18d ./themes/berlin/img/icon_feed.svg
+733cc6c9ade9b80cad1fd2a767756082 ./themes/berlin/img/icon_next.png
+927c2d5c3d0e04ae3bba328b66215730 ./themes/berlin/img/icon_hamburger.png
+b4f62ffaea1938a32ff8105512fd42cf ./themes/berlin/img/icon_hamburger.svg
+122e5e0426f2b1e916de3aad4094c2b3 ./themes/berlin/img/icon_comments.png
+e0cdf03fc691469d720377f27201cbbc ./themes/berlin/img/hline.png
+3eac6b64e480d076b467ea7530eca009 ./themes/berlin/img/vline.png
+a95e3b1d31dbf41961cd1442d0773603 ./themes/berlin/img/bg_light.png
+12b0a53381f48e0f258e5c1174045c85 ./themes/berlin/img/icon_search.png
+f7e251d7292384975b718abc1dcf5e3c ./themes/berlin/img/icon_attach.png
+4a70080819600aee72f0dc7980e2dd2b ./themes/berlin/img/icon_date.png
+62851820f532101a0b6338d09cb1b48d ./themes/berlin/img/icon_category.png
+f28132634103b56d54fce11f2ee5ca33 ./themes/berlin/tpl/user_footer.html
+4cfeef997382a1f4c2cd669b81aed056 ./themes/berlin/scss/init/_config.scss
+447690bbcac2e1680676c03fedc41b42 ./themes/berlin/scss/init/_rebase.scss
+63b96f29cabbcb6a5152215dba498c69 ./themes/berlin/scss/init/_mixins-functions.scss
+e435f31b1aa003ce4d74e84fbb3e27fb ./themes/berlin/scss/libs/_scut.scss
+206e32443d69650419ff31c50e850c93 ./themes/berlin/scss/libs/sass-mq-master/README.md
+b35a7dadd96a8a6cf698c1f6e4777304 ./themes/berlin/scss/libs/sass-mq-master/_mq.scss
+0ff657fd99b19958424f8b425840d411 ./themes/berlin/scss/libs/sass-mq-master/LICENSE.md
+693e02ae1646d41393baee4ee36abaa0 ./themes/berlin/scss/libs/zen-grids/_grids.scss
+3205a52da0c229c05713ae30e78877e1 ./themes/berlin/scss/libs/zen-grids/_flow.scss
+f945228c482d540395a8ce82895e5771 ./themes/berlin/scss/libs/zen-grids/_background.scss
+793baf79388c99d5a1ba92ba6e1d026f ./themes/berlin/scss/libs/bourbon/css3/_perspective.scss
+eec59426ccbe6ed999a5d383b7ad7ea6 ./themes/berlin/scss/libs/bourbon/css3/_linear-gradient.scss
+1d693a6a2827e8f5cd149031832e5599 ./themes/berlin/scss/libs/bourbon/css3/_box-sizing.scss
+2c06708aab3af4dd6fb43af587a8d05c ./themes/berlin/scss/libs/bourbon/css3/_transition.scss
+89c56fb044c24c0d8ab2d87b36c16146 ./themes/berlin/scss/libs/bourbon/css3/_radial-gradient.scss
+1a6fa43d67dc55e7224271f621177c16 ./themes/berlin/scss/libs/bourbon/css3/_border-image.scss
+5fbe0b8110e24dbf2b7d4720e5ad01aa ./themes/berlin/scss/libs/bourbon/css3/_hidpi-media-query.scss
+4c2019fdf700c7a0f1f64e754620a330 ./themes/berlin/scss/libs/bourbon/css3/_font-face.scss
+fcf794c47fa8279503bf60b68715bb47 ./themes/berlin/scss/libs/bourbon/css3/_user-select.scss
+fe69484230ca57ed02178aa83bfe4acb ./themes/berlin/scss/libs/bourbon/css3/_keyframes.scss
+a6cc4789a350eedea26c1cf849a917c7 ./themes/berlin/scss/libs/bourbon/css3/_placeholder.scss
+2859ed66a439a1280cb4150a760caae3 ./themes/berlin/scss/libs/bourbon/css3/_inline-block.scss
+7759cec69b20500438e6afbbb8c81a02 ./themes/berlin/scss/libs/bourbon/css3/_backface-visibility.scss
+4d3d5364521d737d0998d92ce5178df8 ./themes/berlin/scss/libs/bourbon/css3/_animation.scss
+373a4d781651d8f70d3a881c2a73e847 ./themes/berlin/scss/libs/bourbon/css3/_border-radius.scss
+7e88bcfcf572bdecc5fd9544e65b5a07 ./themes/berlin/scss/libs/bourbon/css3/_image-rendering.scss
+73147d542b1a29b694e222a14429795d ./themes/berlin/scss/libs/bourbon/css3/_columns.scss
+70afecfd28f823fa6bc13fb43eb35a4b ./themes/berlin/scss/libs/bourbon/css3/_transform.scss
+3314b2de62ffeb20e0075a8912a32ba1 ./themes/berlin/scss/libs/bourbon/css3/_flex-box.scss
+31b11ba871dd5c21aa901588b08bfe95 ./themes/berlin/scss/libs/bourbon/css3/_background-image.scss
+6c436dc81b892aecec061649ad74d71e ./themes/berlin/scss/libs/bourbon/css3/_appearance.scss
+a7ecc005764e546bab26c74e44903fc0 ./themes/berlin/scss/libs/bourbon/css3/_background.scss
+3420764386296fa42429e8ea4f7f25f8 ./themes/berlin/scss/libs/bourbon/_bourbon-deprecated-upcoming.scss
+7d768710d2b2bd1e7fe11551b9e2f1ba ./themes/berlin/scss/libs/bourbon/addons/_hide-text.scss
+8808d73cdf632135bca44c0759606d7c ./themes/berlin/scss/libs/bourbon/addons/_prefixer.scss
+6b26830422e5cdd9d00eae835e4272b5 ./themes/berlin/scss/libs/bourbon/addons/_timing-functions.scss
+7092d245a820481a90ba03aaaa491613 ./themes/berlin/scss/libs/bourbon/addons/_html5-input-types.scss
+e2db1b06cfbd07303f7a07fffc67096a ./themes/berlin/scss/libs/bourbon/addons/_triangle.scss
+06a51eacdc8c39e192d4499d9321a191 ./themes/berlin/scss/libs/bourbon/addons/_button.scss
+d08d2939b7ff5026b3ea988976c1abe1 ./themes/berlin/scss/libs/bourbon/addons/_font-family.scss
+a8cce220fc82a570b22abd377cc6839b ./themes/berlin/scss/libs/bourbon/addons/_size.scss
+4af52cb857281ece004aa5da204d0bc2 ./themes/berlin/scss/libs/bourbon/addons/_retina-image.scss
+772848d19d4dd35c477a29f7768e0af4 ./themes/berlin/scss/libs/bourbon/addons/_clearfix.scss
+4cbfec40971e6dd49c646660cabb7d11 ./themes/berlin/scss/libs/bourbon/addons/_position.scss
+96abfdaf083f2924fb2f13125ee59f44 ./themes/berlin/scss/libs/bourbon/functions/_linear-gradient.scss
+f22bf5eee2d939854da12ece0ac5bbaf ./themes/berlin/scss/libs/bourbon/functions/_grid-width.scss
+2803056bf58562ee5fded423dcb9f527 ./themes/berlin/scss/libs/bourbon/functions/_flex-grid.scss
+f2669018699b0009a3f83314fa640237 ./themes/berlin/scss/libs/bourbon/functions/_radial-gradient.scss
+8a88f39d99dc5747e4ae991d3f4e69e4 ./themes/berlin/scss/libs/bourbon/functions/_transition-property-name.scss
+b5c422224be3d36942b3a92d8c2ec89e ./themes/berlin/scss/libs/bourbon/functions/_tint-shade.scss
+344bc6de267e16d0d88c523121d3a677 ./themes/berlin/scss/libs/bourbon/functions/_modular-scale.scss
+4c00c6f4b40a193a3f3ae521d5975567 ./themes/berlin/scss/libs/bourbon/functions/_compact.scss
+a963a80a68da838bc2f910dc2aeb2855 ./themes/berlin/scss/libs/bourbon/functions/_px-to-em.scss
+7d685d8ea4890313fcda530cbc91c90f ./themes/berlin/scss/libs/bourbon/helpers/_deprecated-webkit-gradient.scss
+8b2a16ec9d8156a06c0e6ca6b9824bcc ./themes/berlin/scss/libs/bourbon/helpers/_linear-positions-parser.scss
+fd4157198cf4848a2ec892dd0a4b6343 ./themes/berlin/scss/libs/bourbon/helpers/_gradient-positions-parser.scss
+b4be890c84e8a6363fc2a62dc919b98d ./themes/berlin/scss/libs/bourbon/helpers/_radial-arg-parser.scss
+d2982aa29f132f4e4ee76153c6011479 ./themes/berlin/scss/libs/bourbon/helpers/_render-gradients.scss
+8fe35e46c19a0e3f05851e77635aa385 ./themes/berlin/scss/libs/bourbon/helpers/_radial-positions-parser.scss
+a0e10c7137e2127c985293a3802fafb6 ./themes/berlin/scss/libs/bourbon/helpers/_shape-size-stripper.scss
+6403d0ed7aa2cb32f3ee12df8af89fda ./themes/berlin/scss/libs/bourbon/_bourbon.scss
+1b6b11cd7a259a7dda23652dd343a356 ./themes/berlin/scss/partials/_global_layout.scss
+db371620e49470d88381c8d2307925a5 ./themes/berlin/scss/partials/_content.scss
+4280d3aebe05502d3946e684cc401343 ./themes/berlin/scss/partials/_sidebar.scss
+1cdb8ab4b6591841f9a4a401eba02223 ./themes/berlin/scss/partials/_forms.scss
+a06f33d3c1b4dfea7b0daaad79c85890 ./themes/berlin/scss/partials/_common.scss
+aaad7e24d1472f612927c6e1ed24e90f ./themes/berlin/scss/partials/_icons.scss
+bc55edba63d7a9ef082ab81784bfe70a ./themes/berlin/scss/partials/_footer.scss
+da183e106cfbf449bc170037950e35cf ./themes/berlin/scss/partials/_archive.scss
+ee719d550977d426e92f2aa0ef4c78a6 ./themes/berlin/scss/partials/_post.scss
+cbf671fb4c62b9d0f5501135d66c23ad ./themes/berlin/scss/partials/_header.scss
+96c5ba4ff33de42a071d617d307bdc02 ./themes/berlin/scss/modules/_off-canvas.scss
+60955aabdb8246eb9b5ca4e4e05298e4 ./themes/berlin/scss/style.scss
+74a1616ca67498bbc9c23ab3a2028ca8 ./themes/berlin/_define.php
+3b24e9d0f6d33d420b6e5b8af8ab3bf2 ./themes/berlin/style.css
+eaa44e7935815507ee8c8154931bbc9e ./themes/berlin/config.rb
+8c9b760bb5acf8d2a208c502a323cda3 ./themes/berlin/scripts/boxsizing.htc
+970e961dea8d5dbfdc5529e61efb0e8e ./themes/berlin/_public.php
+45b9e548bda08e71b9308e004247794b ./themes/berlin/locales/de/main.po
+388aba770734b4262e2de40361ef3d7f ./themes/berlin/locales/de/main.lang.php
+7ab64bb08b392c6d5636d2ca90e6e531 ./themes/berlin/locales/es/main.po
+ad7c75f22a26867d087355ff69e19ae2 ./themes/berlin/locales/es/main.lang.php
+f8b6da0ead467a4819f0de6408693508 ./themes/berlin/locales/fr/main.po
+2c34f89c3dfeec7dfd8226407e10374c ./themes/berlin/locales/fr/main.lang.php
+4ad1f15cec2e032792404f4670098d3f ./themes/berlin/screenshot.jpg
+7821d4ca583c1add2dcc46dbdba6f077 ./themes/blueSilence/img/background.png
+e974b267079d9ad2968aebbcf9bc63ae ./themes/blueSilence/img/report.png
+6faf0bf9883017d3388aee7a10ba535e ./themes/blueSilence/img/rss.png
+fb9ac6f3f5127ae3fff657d151fb2230 ./themes/blueSilence/img/tag.png
+f7a34b6d7c3641b7afa0132a9723ece3 ./themes/blueSilence/img/top.jpg
+c4ed3a4c043f9e451ff1f7a7e24f2b9c ./themes/blueSilence/img/li.png
+6583ded1d313f1a83b71ae2ea454ff39 ./themes/blueSilence/img/commentaire.png
+6208c3172e5b98b8fffb532ae15e1223 ./themes/blueSilence/img/tags.png
+51b0038bbedbe890899352e95fdbc597 ./themes/blueSilence/img/commentaire_bulle.png
+28f685caa7369183c0b0e7e92daa4bd7 ./themes/blueSilence/img/attach.png
+e5a4853dd364599723e8d1651611be92 ./themes/blueSilence/img/footer.png
+9ef6b66149bda6749d6ae51bb141c1ba ./themes/blueSilence/img/retrolien.png
+b0627263c71be1ffd27480066dd9d3a7 ./themes/blueSilence/img/sidebar_li.png
+42e720614bc757197ab8afaa0ad2d845 ./themes/blueSilence/_define.php
+55d240cdb5d393b845f88ffeccd46f7d ./themes/blueSilence/style.css
+6c0c644258713357329a5bcac1f44768 ./themes/blueSilence/screenshot.jpg
+2500eaaef04b59025940ef2b59b141c9 ./themes/customCSS/_define.php
+d41d8cd98f00b204e9800998ecf8427e ./themes/customCSS/style.css
+48dd240e64dc8b1aa6b5d9ba8024b43e ./themes/customCSS/_public.php
+57ea8e90dca31625ff3f2d2ac773d1c7 ./themes/customCSS/locales/fr/main.po
+a17b07ddc12a18c6f06f120e0479e0f8 ./themes/customCSS/locales/fr/main.lang.php
+20ac284bcbdad05788fc4a8a02c850e0 ./themes/customCSS/_config.php
+eccd34aaee3f016bc01e7bca5ad30400 ./plugins/tags/js/legacy-post.js
+64fa749c59d0c64a8507f0f134a47b68 ./plugins/tags/js/posts_actions.js
+561046f89487fc607c4e2b470ec774f2 ./plugins/tags/js/tag.png
+333fb1da61d179fd508650b65cbc8b1d ./plugins/tags/js/posts.js
+14cdbd24db913b04c3dcca30a66741c9 ./plugins/tags/js/ckeditor-tags-plugin.js
+c47a781e4a4c65579eb3125e304ad48c ./plugins/tags/js/post.js
+deb5c791e787615f52941227f045b37e ./plugins/tags/img/loader.gif
+0c3e9d546a5fafa0e519d708b25197c5 ./plugins/tags/img/tag-add.png
+b1b7f7b549e7983814753a9d75252c5c ./plugins/tags/inc/tags.behaviors.php
+6365c49706fbccd0070d782332713b90 ./plugins/tags/_define.php
+fd0a19b302b44a8d99e606a94304d7c8 ./plugins/tags/style.css
+2cd10176d0d5523f9e4cb431b838fafe ./plugins/tags/_widgets.php
+18ff4d1b71e9250ba0cecda1ea9843cd ./plugins/tags/_public.php
+4e51ee07ec8a6fa754d1c4fdf34f765f ./plugins/tags/icon.png
+d54ed64baf27d07a13208cdd5049327d ./plugins/tags/locales/en/help/tag_post.html
+f5561c6fb7c3ff1e5d2b21d7f76236a0 ./plugins/tags/locales/en/help/tags.html
+adc980843279027fb43e365e7ef237b0 ./plugins/tags/locales/en/help/tag_posts.html
+ac8cce1eb8a470d3cee659bb108b4f1e ./plugins/tags/locales/en/resources.php
+a043f36cb393104ed7b9966e171adc27 ./plugins/tags/locales/fr/help/tag_post.html
+26e4c7e4a92ec10abe0838d9dba5d717 ./plugins/tags/locales/fr/help/tags.html
+3878c7a10a4acb2ec7e13e05f06291fc ./plugins/tags/locales/fr/help/tag_posts.html
+ac8cce1eb8a470d3cee659bb108b4f1e ./plugins/tags/locales/fr/resources.php
+b302aaf1fa8ffa35c68951de0aa9e79b ./plugins/tags/_prepend.php
+83aba808e4bb7916686d43d5172b3e35 ./plugins/tags/_admin.php
+eb2623744e40099b4addec0a6c51998d ./plugins/tags/index.php
+da6df81eb9cde7a7cd62abf766bbbcba ./plugins/tags/_xmlrpc.php
+72d3a3b9d76d599fdd00ca1a439b1b68 ./plugins/tags/tags.php
+5a1bc2a0aecbd12eccf932a7953aa8f7 ./plugins/tags/tag_posts.php
+f5063d7b7e85b9f90821f852740dbe6e ./plugins/tags/icon-big.png
+8e93e3bf9504bd9dd61cbf32bf4562f0 ./plugins/tags/default-templates/dotty/tag.html
+a0cee45e2b4530b1a150c90b577d9337 ./plugins/tags/default-templates/dotty/tags.html
+cbc6361f0608db28335a75dbaeeb9610 ./plugins/tags/default-templates/currywurst/tag.html
+06c8558adf5d32eef0f97a1d1ba31258 ./plugins/tags/default-templates/currywurst/tags.html
+28f287d13bf2016b0cc11ecfdb10c1c9 ./plugins/tags/default-templates/mustek/tag.html
+a7cc0985501a15820356f6fe23517d22 ./plugins/tags/default-templates/mustek/tags.html
+653fbd425901902b0e1be632caa3d3cf ./plugins/maintenance/js/settings.js
+b6d9f4f7cd7d1d2a614b0f0332542fe1 ./plugins/maintenance/js/dc.maintenance.js
+aeafbef9a12acf4512ddce6ae10acef1 ./plugins/maintenance/inc/class.dc.maintenance.task.php
+2b79b54cf8a17e5912365b95fb10527f ./plugins/maintenance/inc/tasks/class.dc.maintenance.logs.php
+b4b663cec547b33ce007300e48aeec0c ./plugins/maintenance/inc/tasks/class.dc.maintenance.synchpostsmeta.php
+00a74e8aa32bc50d5b170c706bf67d86 ./plugins/maintenance/inc/tasks/class.dc.maintenance.vacuum.php
+f94525451135e6bd05a2843e04962709 ./plugins/maintenance/inc/tasks/class.dc.maintenance.cache.php
+3bd53f52c3d36956569f4799d81e6479 ./plugins/maintenance/inc/tasks/class.dc.maintenance.csp.php
+1a8e1abd0692df77c91180be325595a0 ./plugins/maintenance/inc/tasks/class.dc.maintenance.zipmedia.php
+555b4a6f8ef47b0954cd6ed2f5c61de4 ./plugins/maintenance/inc/tasks/class.dc.maintenance.ziptheme.php
+6e9d0fd89fa18bd5ba6a75bb3614255d ./plugins/maintenance/inc/tasks/class.dc.maintenance.indexposts.php
+dcb8f053535d8b686ea5946770516dc6 ./plugins/maintenance/inc/tasks/class.dc.maintenance.indexcomments.php
+fe11207c2d6a0095c60416d24d96adfa ./plugins/maintenance/inc/tasks/class.dc.maintenance.countcomments.php
+139a1104df59e7540e422b2f3309cd93 ./plugins/maintenance/inc/class.dc.maintenance.php
+ca02b633cb17a882baa7a8e057ff2439 ./plugins/maintenance/inc/class.dc.maintenance.descriptor.php
+ef02f10eb23d83548dd8cee3a0e477ba ./plugins/maintenance/icon-small.png
+cf31c7ab38bbabe07911c0948c6fe49a ./plugins/maintenance/_define.php
+2ca4f893061ee8ff20667d2448d4175c ./plugins/maintenance/icon.png
+cc949f181cb15274b1f6c3f626975496 ./plugins/maintenance/locales/en/help/maintenance.html
+f1dcf9cb47f5076442abf56ae13ebdd0 ./plugins/maintenance/locales/en/resources.php
+08572b2a1fe3c26c83dedf784ef33a90 ./plugins/maintenance/locales/fr/help/maintenance.html
+f1dcf9cb47f5076442abf56ae13ebdd0 ./plugins/maintenance/locales/fr/resources.php
+2d622d0ad75e4b81581a165ba10fe175 ./plugins/maintenance/_prepend.php
+ca32b91e964a41d1a4ad54d46c793e7d ./plugins/maintenance/_admin.php
+bb9f43af3b40abd803a84ae42e24c009 ./plugins/maintenance/icon-big-update.png
+8b862fc5d7d7c182820b1a2c175dc6ef ./plugins/maintenance/index.php
+66b672dcb9268dfa8360a9fafe0fef73 ./plugins/maintenance/_services.php
+e83ccdd57add55af6a0391f235b05de3 ./plugins/maintenance/icon-big.png
+03be53cdc4938dcce1ba8b8d0eb01841 ./plugins/pages/js/list.js
+196feff4beee9daf7934a996f298871b ./plugins/pages/js/page.js
+2a2fb5f0c8d081377f3452b14dd533c5 ./plugins/pages/js/_users_actions.js
+31d59a0eea39f942e5a929dcaf7d3e4e ./plugins/pages/class.actionpage.php
+3312ef48b826a272bcbe044902f55fd4 ./plugins/pages/list.php
+0150f9a058be3a81bbc2988b46c2aace ./plugins/pages/_define.php
+ae57d552abe25be81b09fa1ab226763f ./plugins/pages/_install.php
+3819b024d5465840290c7cdd2892a008 ./plugins/pages/page.php
+910d0638d4e1540949373014994018e5 ./plugins/pages/class.listpage.php
+8cc94b45b8dedd0336dfc32fc8dee0c0 ./plugins/pages/_widgets.php
+92e9ba5aa63a3cdc1a11a9dc2125e7e8 ./plugins/pages/_public.php
+1dd7ee56fe0fe9aa616c8f7fc527c6ad ./plugins/pages/icon.png
+dc8040aece6d2e57d434434dce627232 ./plugins/pages/locales/en/help/page.html
+1c0b11c9b602707408f67be6a1c72a5d ./plugins/pages/locales/en/help/pages.html
+ccef5bab6231eb67b48d8a4cec32f44c ./plugins/pages/locales/en/resources.php
+225a82d6ac873c521688d02ab686d96d ./plugins/pages/locales/fr/help/page.html
+ff627d15a03e1aa2193943cf7844d2d1 ./plugins/pages/locales/fr/help/pages.html
+ccef5bab6231eb67b48d8a4cec32f44c ./plugins/pages/locales/fr/resources.php
+15812f32ab00e21ab8b6bbf33a4045b6 ./plugins/pages/_prepend.php
+60205c99782927b91aa4fcd24c575c89 ./plugins/pages/_admin.php
+200f79e1b12acab32c6651046148bf8f ./plugins/pages/index.php
+7c2a3ce8f1422ccd682dfcc709f8999b ./plugins/pages/icon-np-big.png
+1d0fc90dfdae8c5d40ec413f03f92eff ./plugins/pages/icon-np.png
+4c96d8240dd994340e9165fb190fe196 ./plugins/pages/icon-big.png
+2e70d543ad8392efe776de04a1104cdb ./plugins/pages/default-templates/dotty/page.html
+2e70d543ad8392efe776de04a1104cdb ./plugins/pages/default-templates/currywurst/page.html
+b8bd86f7beed1339069cc9b580cbfad6 ./plugins/pages/default-templates/mustek/page.html
+55cdaa0f1faf103f2e9d047e175305d8 ./plugins/pings/js/post.js
+8f4cab396d97c51df16079862af3e151 ./plugins/pings/lib.pings.php
+9779b73c232ecc5b598a73631474d3b4 ./plugins/pings/_define.php
+270c5bc60a88a5069c36857a0586b1bb ./plugins/pings/_install.php
+f9b48285bca224c9d23fdf30c028a5f7 ./plugins/pings/icon.png
+9f1890538a94ac20c2959cc8ce5ac291 ./plugins/pings/locales/en/help/pings_post.html
+187f31ad1aeaf7dd61eebedd12d9c59a ./plugins/pings/locales/en/help/pings.html
+500013a9f9d3a1826ee742efc2c6f0b7 ./plugins/pings/locales/en/resources.php
+11fdcfa2f232a4fe3c084e95d84fc201 ./plugins/pings/locales/fr/help/pings_post.html
+96925a11786ad1eec466c2d70382f492 ./plugins/pings/locales/fr/help/pings.html
+500013a9f9d3a1826ee742efc2c6f0b7 ./plugins/pings/locales/fr/resources.php
+e3d62547872c9e2645338b1ace5cd4ba ./plugins/pings/_prepend.php
+3b8adc72cfc6b2525054ec587a2a767d ./plugins/pings/_admin.php
+1a40eda5d2ed6cc07809580bdcbbf4f0 ./plugins/pings/index.php
+89befe291ac75f166d050bcf108ce4a8 ./plugins/pings/icon-big.png
+4245aca902b793c3e178060041cea6b9 ./plugins/attachments/js/post.js
+ec93f9580710eeff2607cdb42a853ac0 ./plugins/attachments/_define.php
+a1da1771c44c0baae9d4cbf4def0dce3 ./plugins/attachments/_public.php
+8c0baf47932efbda5693f30720b5a4de ./plugins/attachments/locales/en/help/help.html
+0e44dfe7e8427b2c69d5b8098dca07cf ./plugins/attachments/locales/en/resources.php
+dd0b8d6721462658ec8b03e9bbd2b8a3 ./plugins/attachments/locales/fr/help/help.html
+0e44dfe7e8427b2c69d5b8098dca07cf ./plugins/attachments/locales/fr/resources.php
+207fe772ed01a3a00d93ea1bc17107a1 ./plugins/attachments/_admin.php
+29164d32bfca39c6f3fae1ae8ca6910d ./plugins/dcLegacyEditor/js/jsToolBar/jsToolBar.js
+183bdbef60949b54b73cee27343613c6 ./plugins/dcLegacyEditor/js/jsToolBar/popup_posts.js
+afc2f21879017e10f95dcc4be1e43e35 ./plugins/dcLegacyEditor/js/jsToolBar/jsToolBar.config.js
+ab28e792e460e60425a1fe439bf2ba06 ./plugins/dcLegacyEditor/js/jsToolBar/popup_link.js
+0f163beea66d7bcb3348d09b6417fb56 ./plugins/dcLegacyEditor/js/jsToolBar/popup_media.js
+bfa666733f08fd8966340d36f2b4ddde ./plugins/dcLegacyEditor/js/jsToolBar/jsToolBar.dotclear.js
+9c16d798ed68f5b110d7cbb4fb645791 ./plugins/dcLegacyEditor/js/jsToolBar/jsToolBar.wysiwyg.js
+d9fa07c9b7d6beaa1e2e0c51982b8b4f ./plugins/dcLegacyEditor/js/_post_editor.js
+78d232b81f990e4849c1767a168effcd ./plugins/dcLegacyEditor/css/jsToolBar/jsToolBar.css
+f612a6261246f9d30e70eb7d37e596e0 ./plugins/dcLegacyEditor/css/jsToolBar/bt_link.png
+eade2e870ac130c37b3a17f9630b0831 ./plugins/dcLegacyEditor/css/jsToolBar/resize.png
+5fda825d63568990c710da1bf8755ce9 ./plugins/dcLegacyEditor/css/jsToolBar/bt_clean.png
+24d5891771894886ad21244cbd34c391 ./plugins/dcLegacyEditor/css/jsToolBar/bt_img_select.png
+f8d1344fbb4a7b2141ce6665a79c0df3 ./plugins/dcLegacyEditor/css/jsToolBar/bt_br.png
+61da2dfc9f1e424b17d3314a447fc60b ./plugins/dcLegacyEditor/css/jsToolBar/bt_em.png
+d05bee8d6ca09f2e4d1bf603b02eed9a ./plugins/dcLegacyEditor/css/jsToolBar/bt_img.png
+191116dfb1717292048a67a7540b76ce ./plugins/dcLegacyEditor/css/jsToolBar/bt_bquote.png
+520a391dbb39aa5bff7d0e2bae715204 ./plugins/dcLegacyEditor/css/jsToolBar/bt_ins.png
+3ea4743c27d78aeef527d6fa8463806b ./plugins/dcLegacyEditor/css/jsToolBar/bt_pre.png
+9cebf86331264b5fb2588464aae38971 ./plugins/dcLegacyEditor/css/jsToolBar/bt_paragraph.png
+cdcaa8a0819adb19b9268d211757cf4b ./plugins/dcLegacyEditor/css/jsToolBar/bt_code.png
+574e762b46ce52daefa4378486a8bc0b ./plugins/dcLegacyEditor/css/jsToolBar/bt_ol.png
+a4dd972b5aca75fa0bbf5f9690dd9e98 ./plugins/dcLegacyEditor/css/jsToolBar/bt_quote.png
+750c41a2324dee91e0d71579948a5333 ./plugins/dcLegacyEditor/css/jsToolBar/bt_strong.png
+630d054ff06700358c0b8c85cf5492c0 ./plugins/dcLegacyEditor/css/jsToolBar/bt_del.png
+9a8372756aa6cadf62999d85f5f13f05 ./plugins/dcLegacyEditor/css/jsToolBar/bt_post.png
+313e10c6bb12c4970f8313801ffd552e ./plugins/dcLegacyEditor/css/jsToolBar/bt_ul.png
+0c6894534c3d7d5ce582bcaa172ef9ec ./plugins/dcLegacyEditor/css/jsToolBar/bt_mark.png
+7b70f5d1b5873bd0a98df3a339b8ca3b ./plugins/dcLegacyEditor/inc/dc.legacy.editor.behaviors.php
+acfc3bcaf942a1c179c7d1822601505f ./plugins/dcLegacyEditor/tpl/index.php
+e49b28a505c51d3561fe859863aad72e ./plugins/dcLegacyEditor/_define.php
+57e4de4f120a2ab908f0d513615f49a3 ./plugins/dcLegacyEditor/_install.php
+41a9ebdb7edc07ef351093a48e880ce6 ./plugins/dcLegacyEditor/icon.png
+bdd8db47fa639fa50ad1893d75e8f5d4 ./plugins/dcLegacyEditor/locales/en/help/legacy_editor.html
+4b412c3cb7599488303dfbd296b01d05 ./plugins/dcLegacyEditor/locales/en/resources.php
+0f515b91596c87dc3d4eac798848bbcf ./plugins/dcLegacyEditor/locales/fi/main.po
+2efeeae9a66f0a832828feebc442d7ed ./plugins/dcLegacyEditor/locales/fi/main.lang.php
+7f5b89b295b0fd595b3ce4de749cbb29 ./plugins/dcLegacyEditor/locales/fr/help/legacy_editor.html
+a63145c2d7e725c979859f96518bb2e0 ./plugins/dcLegacyEditor/locales/fr/main.po
+4b412c3cb7599488303dfbd296b01d05 ./plugins/dcLegacyEditor/locales/fr/resources.php
+628adc5e291e76c354c0412241a358fd ./plugins/dcLegacyEditor/locales/fr/main.lang.php
+7d3f4feaef3dd12af71ca3332dfb45d3 ./plugins/dcLegacyEditor/_prepend.php
+277f92036e874b2fb1a6e1b15bd4db1a ./plugins/dcLegacyEditor/_admin.php
+68e5a2cb57dac3c4a0cc4c1c4e3e520d ./plugins/dcLegacyEditor/index.php
+b8df3145b50f177c94ae5e6e801ff6cd ./plugins/antispam/js/dashboard.js
+2588c9536b516b9dc1814cec54f724c8 ./plugins/antispam/js/antispam.js
+b41e09c0acc2f62e5eaaac5629ef9f97 ./plugins/antispam/inc/class.dc.spamfilters.php
+ec893221e5b9dea3bdabfe956a169573 ./plugins/antispam/inc/lib.dc.antispam.php
+da2e85e1316263cc6769cd1c829a08e3 ./plugins/antispam/inc/class.dc.spamfilter.php
+5359d2c418b4b98635f17205e43d065b ./plugins/antispam/inc/lib.dc.antispam.url.php
+bab4689ca4d5bee58bcf62db6b2e54a9 ./plugins/antispam/_define.php
+fa89dcd5802279d26c37851c3547e32c ./plugins/antispam/_install.php
+848f11ed6026d4eda8555d48a5ed6bf5 ./plugins/antispam/style.css
+6c6be35096c4f3fee1fe73aa162f7d60 ./plugins/antispam/_public.php
+00ae2051272e9e7f6953d22dc97c8142 ./plugins/antispam/icon.png
+84f8a64dc45214672c824b6c4ba07c6e ./plugins/antispam/locales/en/help/comments.html
+95167a14842fd0086f982aa04923e4ad ./plugins/antispam/locales/en/help/words.html
+c0f4632077fa578a614ed95f37b592e4 ./plugins/antispam/locales/en/help/ip.html
+076deb7b241e9a2478fe02a091f315fb ./plugins/antispam/locales/en/help/help.html
+1c3076d9a02cf85c2f3ba070d76fe239 ./plugins/antispam/locales/en/help/filters.html
+16239e397d633e70524fa8548e7d6a4e ./plugins/antispam/locales/en/help/iplookup.html
+acf11d462192ef391d5ca97fb36fb2c8 ./plugins/antispam/locales/en/resources.php
+fcfaa58281ed152fd0eed604456a65b5 ./plugins/antispam/locales/fr/help/comments.html
+defdc6c59036215ed1a325f383ac8593 ./plugins/antispam/locales/fr/help/words.html
+bbcf0b467a95e410e0e5ef55dae3edd7 ./plugins/antispam/locales/fr/help/ip.html
+665c098a8f78a44c8ca49c8fd6bb8123 ./plugins/antispam/locales/fr/help/help.html
+8bc9b78511e8c27b5154d5f254fbff54 ./plugins/antispam/locales/fr/help/filters.html
+aa11a39057c066570d9c76c8c70c0e19 ./plugins/antispam/locales/fr/help/iplookup.html
+acf11d462192ef391d5ca97fb36fb2c8 ./plugins/antispam/locales/fr/resources.php
+e365c56f2022a51aac9fa28f5ac58c70 ./plugins/antispam/_prepend.php
+63cc93bd3225fdf66a65110366f8492d ./plugins/antispam/filters/class.dc.filter.linkslookup.php
+475291d78b42560779dfffcc35abf249 ./plugins/antispam/filters/class.dc.filter.words.php
+fb498f639ea67f52d09df67f6d86aa09 ./plugins/antispam/filters/class.dc.filter.ip.php
+74b0c3825841498bc4b8276ebb8689ae ./plugins/antispam/filters/class.dc.filter.iplookup.php
+e4d536f10fda10cb4e4c3d83633cdbc8 ./plugins/antispam/_admin.php
+5136ae3bb9f884361cb5dc07843043d9 ./plugins/antispam/index.php
+58a1f912d0f3699e21fb516801b21d35 ./plugins/antispam/feed.png
+31ad5030b3b43888c502bc4b63a1e069 ./plugins/antispam/_services.php
+cc94097d12274e9e278bf47cb804e226 ./plugins/antispam/icon-big.png
+aa3ced2c972ca460416d38ffe0fc29f7 ./plugins/dclegacy/_define.php
+331d8b4a31c01db77d1f12a95b8874f8 ./plugins/dclegacy/_admin.php
+d72fbc33d3c78972026405d5b20baaa9 ./plugins/themeEditor/js/mode.js
+b7b5c5cc36ac3a92895af6e9470aad2c ./plugins/themeEditor/js/theme.js
+2873b3d392434f40cd36d38f858e0078 ./plugins/themeEditor/js/script.js
+2547c68dcd95cd9603a706ba179e9282 ./plugins/themeEditor/_define.php
+1de738f3c2a2e4c511103137f2050966 ./plugins/themeEditor/style.css
+e4bbb7d741b74ac201271a839e9fcde0 ./plugins/themeEditor/locales/en/help/help.html
+0271ff4caa1ee5c0d27999b2e3ee2ed2 ./plugins/themeEditor/locales/en/resources.php
+e526e7d21d457acca66157745ea1746a ./plugins/themeEditor/locales/fr/help/help.html
+0271ff4caa1ee5c0d27999b2e3ee2ed2 ./plugins/themeEditor/locales/fr/resources.php
+c6f20488e5b358c7d508c36a1c698074 ./plugins/themeEditor/_admin.php
+ccbbc178e56bce7d4df7d941fd73bf3f ./plugins/themeEditor/index.php
+2f1f4c81511e71f7bfa8f8d6c30645ac ./plugins/themeEditor/class.themeEditor.php
+9257611cc4fe1e7e3b55d021435de341 ./plugins/.htaccess
+26be1bb202fcd96d1c61fe044c7f3d94 ./plugins/blogroll/js/blogroll.js
+9a33dba7b4365dc29756378b6149b7a3 ./plugins/blogroll/js/_users_actions.js
+e0f92b41f75e2a31535d494320e5e722 ./plugins/blogroll/icon-small.png
+6ce4d59515f888a06be12d42f82d3de0 ./plugins/blogroll/edit.php
+adeb034f99f208b9f600bd340af70d87 ./plugins/blogroll/_define.php
+91298e12de3d89fa42dd0a75b41994c4 ./plugins/blogroll/class.dc.importblogroll.php
+9bf10e6a9ff78c9a9467691e62a76bc1 ./plugins/blogroll/_install.php
+a60b8b7ad113618d4b86b914b5090e6c ./plugins/blogroll/_widgets.php
+734e4502ebbfa5e57a006b3024c6c006 ./plugins/blogroll/_public.php
+b6c236cac6b9dc3869f9801aea49c6cd ./plugins/blogroll/icon.png
+05a240d5af2cc639ce85a7af9eb282f6 ./plugins/blogroll/locales/en/help/blogroll.html
+1629bd07aa71898b4760c77388464257 ./plugins/blogroll/locales/en/resources.php
+5bf2b9507d89ce3ef30584aeea1ecb48 ./plugins/blogroll/locales/fr/help/blogroll.html
+1629bd07aa71898b4760c77388464257 ./plugins/blogroll/locales/fr/resources.php
+f38cea2c76589161098365ee87534438 ./plugins/blogroll/_prepend.php
+63e65c4bbcb010226282349fc774cad4 ./plugins/blogroll/_admin.php
+c9bb6227839711408a03c2ac3154cb63 ./plugins/blogroll/class.dc.blogroll.php
+d03c0edd54672e11eeba98b52b44bb98 ./plugins/blogroll/index.php
+0ad694353337db54b7302b5f9a979494 ./plugins/dcCKEditor/js/popup_posts.js
+d1d87f6028e03f39af8ab1813da56c9b ./plugins/dcCKEditor/js/ckeditor-plugins/img/dialogs/img.js
+c3e17ee13b772fd7b679cab16ebf38dd ./plugins/dcCKEditor/js/ckeditor-plugins/img/icons/img.png
+f86e8d1fb49de87e87b1781ede2d20ae ./plugins/dcCKEditor/js/ckeditor-plugins/img/plugin.js
+45f8b4121b4c54f370de17c503572d32 ./plugins/dcCKEditor/js/ckeditor-plugins/media/icons/media.png
+a106ccf848e27b279d5a549ad5768ac6 ./plugins/dcCKEditor/js/ckeditor-plugins/media/plugin.js
+f96ec6b377358d3f8cd8c10a326dc8d9 ./plugins/dcCKEditor/js/ckeditor-plugins/dclink/icons/dclink.png
+a1bb50da28fbcc8a80396578c8c4a6df ./plugins/dcCKEditor/js/ckeditor-plugins/dclink/plugin.js
+16f4bc5f08caa83c9ae4e9254bcfeba0 ./plugins/dcCKEditor/js/ckeditor-plugins/entrylink/icons/entrylink.png
+c3b0407c8f8bb729547278c5493128ea ./plugins/dcCKEditor/js/ckeditor-plugins/entrylink/plugin.js
+c48de074f121ce1754e4acc5ef373bff ./plugins/dcCKEditor/js/popup_link.js
+1be305c2aed9f3e03100a5530de7b7e8 ./plugins/dcCKEditor/js/popup_media.js
+321da5d8770ad680461afa4d1db17b24 ./plugins/dcCKEditor/js/_post_editor.js
+01c34bb4d2f1d6b15c80357a10a8f95e ./plugins/dcCKEditor/js/ckeditor-skins/dotclear/skin.js
+5186e161229a161a796b81483b5217fa ./plugins/dcCKEditor/js/ckeditor-skins/dotclear/dialog.css
+398796538d22fd0fe1c102f5f6567d71 ./plugins/dcCKEditor/js/ckeditor-skins/dotclear/images/hidpi/anchor.png
+61b09126774ba1c8f97d1b268a36303a ./plugins/dcCKEditor/js/ckeditor-skins/dotclear/images/hidpi/lock-open.png
+7951e77da753e455649be7c725496d2f ./plugins/dcCKEditor/js/ckeditor-skins/dotclear/images/hidpi/refresh.png
+6b37e84dd4f8deefcb79c6c211803ffd ./plugins/dcCKEditor/js/ckeditor-skins/dotclear/images/hidpi/lock.png
+d925282972b93c0f8f85deac5800d2e4 ./plugins/dcCKEditor/js/ckeditor-skins/dotclear/images/hidpi/close.png
+c5e1cf53ce8db85674167fb425824e19 ./plugins/dcCKEditor/js/ckeditor-skins/dotclear/images/anchor.png
+1007c83887ae2446d305b8dc0a6c08bc ./plugins/dcCKEditor/js/ckeditor-skins/dotclear/images/lock-open.png
+7f32b6e67f42a0ef3e1ddb0b9401f6c5 ./plugins/dcCKEditor/js/ckeditor-skins/dotclear/images/spinner.gif
+a97a516fb1c061fdd73b393d1a81263b ./plugins/dcCKEditor/js/ckeditor-skins/dotclear/images/refresh.png
+5b9854a7f865788fff62fe32b0324ca0 ./plugins/dcCKEditor/js/ckeditor-skins/dotclear/images/arrow.png
+44aeed002636c661231fad2b3e3d5bc7 ./plugins/dcCKEditor/js/ckeditor-skins/dotclear/images/lock.png
+738269db9d41f199e104f9960885a08c ./plugins/dcCKEditor/js/ckeditor-skins/dotclear/images/close.png
+fc45bc33ab02a1aa3ea7a316d8ca40fb ./plugins/dcCKEditor/js/ckeditor-skins/dotclear/icons.png
+1aad9b356e6018e61215d8cc6c8da13f ./plugins/dcCKEditor/js/ckeditor-skins/dotclear/icons_hidpi.png
+62ce2cee645230193776c034d7ebafc1 ./plugins/dcCKEditor/js/ckeditor-skins/dotclear/editor.css
+808a9160a2f8a437545f75f7eba14065 ./plugins/dcCKEditor/js/ckeditor/lang/af.js
+0c38179cdcc14f1ae284a06abd7dbb7a ./plugins/dcCKEditor/js/ckeditor/lang/bg.js
+5a1ba003697cda272c7c05ffbbd526c0 ./plugins/dcCKEditor/js/ckeditor/lang/ar.js
+0dddfd257338ad9b83e6f9587586c14f ./plugins/dcCKEditor/js/ckeditor/lang/ca.js
+8a343cc1e6fdd151ef82bd3324657dbc ./plugins/dcCKEditor/js/ckeditor/lang/bn.js
+c93faad92ceee3e1fe6bcb8397f20442 ./plugins/dcCKEditor/js/ckeditor/lang/az.js
+ff6c2a378d69f916a632abb7256f0b67 ./plugins/dcCKEditor/js/ckeditor/lang/bs.js
+b29a0aaf182f63d531785de810e7df5b ./plugins/dcCKEditor/js/ckeditor/lang/da.js
+8729cedd7ff4d1d1f5940ccd85e11998 ./plugins/dcCKEditor/js/ckeditor/lang/de.js
+f42388c0beee0b10ed490a0678dfe483 ./plugins/dcCKEditor/js/ckeditor/lang/cs.js
+f7d3b0f72d63918a3e99e77058e34b33 ./plugins/dcCKEditor/js/ckeditor/lang/cy.js
+a2e89bd5a7019becae14fc965da4c8da ./plugins/dcCKEditor/js/ckeditor/lang/el.js
+b99db350471545e09bc1fe57a543598d ./plugins/dcCKEditor/js/ckeditor/lang/fa.js
+5aa3776b740d4b35deca8e744780009c ./plugins/dcCKEditor/js/ckeditor/lang/en.js
+0f32eda2e7d77191f24795ff53f94d89 ./plugins/dcCKEditor/js/ckeditor/lang/eo.js
+f33fc80c35f1267390228789a5fba409 ./plugins/dcCKEditor/js/ckeditor/lang/es.js
+d762828b2a6ca91abdc69f74025703c2 ./plugins/dcCKEditor/js/ckeditor/lang/fi.js
+0590df272a8823d2da033cbd8eff7efe ./plugins/dcCKEditor/js/ckeditor/lang/et.js
+4870ed8da6b35ff48dcb24e182b3bb17 ./plugins/dcCKEditor/js/ckeditor/lang/eu.js
+a87f163c2021bad2c908a592a850a626 ./plugins/dcCKEditor/js/ckeditor/lang/fo.js
+ce09faf2333da1f43653ce575cf037e0 ./plugins/dcCKEditor/js/ckeditor/lang/fr.js
+64b98ee83062905700b427ffff93adfb ./plugins/dcCKEditor/js/ckeditor/lang/gl.js
+95fe247be7ff237e8f213298f81b2090 ./plugins/dcCKEditor/js/ckeditor/lang/he.js
+c62dc270471c78c9ef5dfb56feb08774 ./plugins/dcCKEditor/js/ckeditor/lang/hi.js
+9f4d4a255d950768b41277c1675d5838 ./plugins/dcCKEditor/js/ckeditor/lang/gu.js
+9913c898fd539689a0d5c36ccc71df6f ./plugins/dcCKEditor/js/ckeditor/lang/id.js
+af505e5aededa9b1b717d07c86ef5b2f ./plugins/dcCKEditor/js/ckeditor/lang/hr.js
+6e7c39d63027c15ca95e8f992a779b94 ./plugins/dcCKEditor/js/ckeditor/lang/hu.js
+bdf1d2e3611f435ad7789b2a82e6b8a2 ./plugins/dcCKEditor/js/ckeditor/lang/ja.js
+707324bec419953e1e5d4bec0fdb6d8e ./plugins/dcCKEditor/js/ckeditor/lang/is.js
+a1ac79c11635a3bdba94190d8de58c27 ./plugins/dcCKEditor/js/ckeditor/lang/it.js
+be14c6992fae80b80c03bd3bac727630 ./plugins/dcCKEditor/js/ckeditor/lang/ka.js
+723b9d31d8f980c0b71f17adf6e8979d ./plugins/dcCKEditor/js/ckeditor/lang/km.js
+f0a86b462a50441d36e938a0a819bb78 ./plugins/dcCKEditor/js/ckeditor/lang/ko.js
+97cacc4722e15d449557e0c6d668ce06 ./plugins/dcCKEditor/js/ckeditor/lang/ku.js
+8ad5f155b073f7bcea8b2b1f501fd709 ./plugins/dcCKEditor/js/ckeditor/lang/lt.js
+85f0f4c06291fb3f2f28b19ad2208997 ./plugins/dcCKEditor/js/ckeditor/lang/mk.js
+7d2c1df990f522e1b0ecc702c653657e ./plugins/dcCKEditor/js/ckeditor/lang/lv.js
+139dd2ea930eeb640b40e88ffe554820 ./plugins/dcCKEditor/js/ckeditor/lang/nb.js
+cc81a47cd5603c68828d6f4bdebe33a4 ./plugins/dcCKEditor/js/ckeditor/lang/mn.js
+4d81d3bcb6c61cca30ce760bf2378632 ./plugins/dcCKEditor/js/ckeditor/lang/ms.js
+1224b6cb9bbbb5a101c5e535d7967e60 ./plugins/dcCKEditor/js/ckeditor/lang/nl.js
+4891886efb9c0b582085b3991845f798 ./plugins/dcCKEditor/js/ckeditor/lang/oc.js
+aefb7a30a82b6e87473c1294a528fc54 ./plugins/dcCKEditor/js/ckeditor/lang/no.js
+00615111a7676858a01bce421bcd3209 ./plugins/dcCKEditor/js/ckeditor/lang/pl.js
+ca628057b52e3e156ca88a36db3cde76 ./plugins/dcCKEditor/js/ckeditor/lang/pt.js
+854f74907d7fc9d6edeb0b1dda9371e3 ./plugins/dcCKEditor/js/ckeditor/lang/ro.js
+3c6aa8a8d628a0a379fb95570bcebde5 ./plugins/dcCKEditor/js/ckeditor/lang/si.js
+773231f54e21a62a5e058ff1ad6ca2de ./plugins/dcCKEditor/js/ckeditor/lang/ru.js
+8b9e5384dca7e9da3f928b22d0725da0 ./plugins/dcCKEditor/js/ckeditor/lang/sk.js
+00942bcc13999ce9988d9fc66edb45e2 ./plugins/dcCKEditor/js/ckeditor/lang/sl.js
+6fc2ecf4a4d2bae160978649f599fdfc ./plugins/dcCKEditor/js/ckeditor/lang/sq.js
+63fd8e2423da83f621a1ff05a203702c ./plugins/dcCKEditor/js/ckeditor/lang/sr.js
+2e8fcce14fc21bc89fdf2fc54918be89 ./plugins/dcCKEditor/js/ckeditor/lang/th.js
+6f3546d40b3571e4889f126fc1740d82 ./plugins/dcCKEditor/js/ckeditor/lang/sv.js
+bca56e623c765776723db394042ea1da ./plugins/dcCKEditor/js/ckeditor/lang/ug.js
+13fbd50193a4fe0b4ea2d7ce926347c7 ./plugins/dcCKEditor/js/ckeditor/lang/tr.js
+ba985553fd7b1958401b9b99d32a1b49 ./plugins/dcCKEditor/js/ckeditor/lang/tt.js
+a8776f2b412d8c38b8773082382415fb ./plugins/dcCKEditor/js/ckeditor/lang/uk.js
+873bd91a86839801e428edbaf1d0e974 ./plugins/dcCKEditor/js/ckeditor/lang/vi.js
+a8b11582464bd9e12c8d0cc4260fd440 ./plugins/dcCKEditor/js/ckeditor/lang/zh.js
+d3ab8678cfa34fe6d2710c432d54eed8 ./plugins/dcCKEditor/js/ckeditor/lang/_translationstatus.txt
+ec2311f15b29b1e742277f567628dd41 ./plugins/dcCKEditor/js/ckeditor/lang/pt-br.js
+1e29e8c836793b073a39b4ab0b99b78f ./plugins/dcCKEditor/js/ckeditor/lang/es-mx.js
+c7184e951020abd85e303091d90aaf9e ./plugins/dcCKEditor/js/ckeditor/lang/zh-cn.js
+1b8870a3598d77156f4582cc181571d2 ./plugins/dcCKEditor/js/ckeditor/lang/en-au.js
+61c26337b772023eac6c45c7ca6fdda1 ./plugins/dcCKEditor/js/ckeditor/lang/en-ca.js
+8f768ebbfcff69471de64f2a0f50ee0c ./plugins/dcCKEditor/js/ckeditor/lang/en-gb.js
+ccde4dff8ccae9482fc7905d8f890cca ./plugins/dcCKEditor/js/ckeditor/lang/de-ch.js
+9e3c68956d422bf2c401eb871eb5d309 ./plugins/dcCKEditor/js/ckeditor/lang/fr-ca.js
+2ab490537f94b02733acce7ecb485b8f ./plugins/dcCKEditor/js/ckeditor/lang/sr-latn.js
+67d462a16ce3869517431c149185f4ef ./plugins/dcCKEditor/js/ckeditor/skins/moono/dialog_ie.css
+f72cfe2d4239847afacc5e13dd2c5042 ./plugins/dcCKEditor/js/ckeditor/skins/moono/editor_iequirks.css
+94dc15249abcb2ab557f90c1cc453809 ./plugins/dcCKEditor/js/ckeditor/skins/moono/dialog_ie7.css
+279cbab9b0c40128578223867bdbbc7d ./plugins/dcCKEditor/js/ckeditor/skins/moono/dialog_ie8.css
+dd45871dc11b0d69ed1eddee08a75497 ./plugins/dcCKEditor/js/ckeditor/skins/moono/editor_gecko.css
+2b4def4a4ca4d9d91640fa16419b4995 ./plugins/dcCKEditor/js/ckeditor/skins/moono/editor_ie.css
+cd65b34cf6940d98de5b203857f316f1 ./plugins/dcCKEditor/js/ckeditor/skins/moono/dialog.css
+398796538d22fd0fe1c102f5f6567d71 ./plugins/dcCKEditor/js/ckeditor/skins/moono/images/hidpi/anchor.png
+61b09126774ba1c8f97d1b268a36303a ./plugins/dcCKEditor/js/ckeditor/skins/moono/images/hidpi/lock-open.png
+7951e77da753e455649be7c725496d2f ./plugins/dcCKEditor/js/ckeditor/skins/moono/images/hidpi/refresh.png
+6b37e84dd4f8deefcb79c6c211803ffd ./plugins/dcCKEditor/js/ckeditor/skins/moono/images/hidpi/lock.png
+d925282972b93c0f8f85deac5800d2e4 ./plugins/dcCKEditor/js/ckeditor/skins/moono/images/hidpi/close.png
+c5e1cf53ce8db85674167fb425824e19 ./plugins/dcCKEditor/js/ckeditor/skins/moono/images/anchor.png
+1007c83887ae2446d305b8dc0a6c08bc ./plugins/dcCKEditor/js/ckeditor/skins/moono/images/lock-open.png
+7f32b6e67f42a0ef3e1ddb0b9401f6c5 ./plugins/dcCKEditor/js/ckeditor/skins/moono/images/spinner.gif
+a97a516fb1c061fdd73b393d1a81263b ./plugins/dcCKEditor/js/ckeditor/skins/moono/images/refresh.png
+5b9854a7f865788fff62fe32b0324ca0 ./plugins/dcCKEditor/js/ckeditor/skins/moono/images/arrow.png
+44aeed002636c661231fad2b3e3d5bc7 ./plugins/dcCKEditor/js/ckeditor/skins/moono/images/lock.png
+738269db9d41f199e104f9960885a08c ./plugins/dcCKEditor/js/ckeditor/skins/moono/images/close.png
+3fed53e3a77a38eace2595036e62e272 ./plugins/dcCKEditor/js/ckeditor/skins/moono/editor_ie7.css
+43335d6b68cc25c758f8382fd5f3871f ./plugins/dcCKEditor/js/ckeditor/skins/moono/editor_ie8.css
+b99429627032576d5b691f7e87106ff7 ./plugins/dcCKEditor/js/ckeditor/skins/moono/icons.png
+7613c68f1d58bbdcecb980e41869de35 ./plugins/dcCKEditor/js/ckeditor/skins/moono/icons_hidpi.png
+5e2a02d8e0c34a4a1d5653cf0c0ce265 ./plugins/dcCKEditor/js/ckeditor/skins/moono/dialog_iequirks.css
+512aef65e07987ef05dcbd2d32016e0e ./plugins/dcCKEditor/js/ckeditor/skins/moono/editor.css
+ed20a78b0699361fe3997b8bf1d88280 ./plugins/dcCKEditor/js/ckeditor/skins/moono/readme.md
+91bac1daac2fb1b3e95363791a95882a ./plugins/dcCKEditor/js/ckeditor/styles.js
+79ffbdbf7841fa6c6c61fac20d9c8ad8 ./plugins/dcCKEditor/js/ckeditor/config.js
+20ff1d092e8612dd9cad95693174f478 ./plugins/dcCKEditor/js/ckeditor/adapters/jquery.js
+f9cc22f62c2d440a70e0a73b9497d347 ./plugins/dcCKEditor/js/ckeditor/ckeditor.js
+bdd7b1a5211ef8687a2b772e6ab44de1 ./plugins/dcCKEditor/js/ckeditor/LICENSE.md
+f6aecaa34a4f047b698bece80fdec833 ./plugins/dcCKEditor/js/ckeditor/CHANGES.md
+b4c4ee1322a4b6d0ae34c2a0557791da ./plugins/dcCKEditor/js/ckeditor/plugins/div/dialogs/div.js
+b5c64e2d4bb3e7a8a2ec7b5d10f0a56e ./plugins/dcCKEditor/js/ckeditor/plugins/link/dialogs/link.js
+306334b0ff17ca0a602c50a31136b5a0 ./plugins/dcCKEditor/js/ckeditor/plugins/link/dialogs/anchor.js
+dabcdf82e4c23bc02b0b7d1418821f2c ./plugins/dcCKEditor/js/ckeditor/plugins/link/images/hidpi/anchor.png
+aeb83dd838829f002cac946e6e1960eb ./plugins/dcCKEditor/js/ckeditor/plugins/link/images/anchor.png
+703e5cf58855e26c290233a1feba6554 ./plugins/dcCKEditor/js/ckeditor/plugins/about/dialogs/hidpi/logo_ckeditor.png
+ba7165a0e2cc8cb55cb81e22f8c0de73 ./plugins/dcCKEditor/js/ckeditor/plugins/about/dialogs/logo_ckeditor.png
+f2389a414bfc27ae4086b6d2a1cfac07 ./plugins/dcCKEditor/js/ckeditor/plugins/about/dialogs/about.js
+aede4ea6a64fe68fe2aaec534fb81a96 ./plugins/dcCKEditor/js/ckeditor/plugins/image/dialogs/image.js
+3eed23f5021065a8351126936bbe1e95 ./plugins/dcCKEditor/js/ckeditor/plugins/image/images/noimage.png
+bc0b976396d5b7a360d11df16afe6954 ./plugins/dcCKEditor/js/ckeditor/plugins/table/dialogs/table.js
+70da7b4dcb610b0dd46ed33f6541bebe ./plugins/dcCKEditor/js/ckeditor/plugins/tabletools/dialogs/tableCell.js
+ecaa88f7fa0bf610a5a26cf545dcd3aa ./plugins/dcCKEditor/js/ckeditor/plugins/dialog/dialogDefinition.js
+02a9a3768cfd69b759cb5b0c2b835cb7 ./plugins/dcCKEditor/js/ckeditor/plugins/dialog/styles/dialog.css
+110cea7804a105032c3430ab34487fab ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/af.js
+9d3f1906c94782d81103f83c9fff3a16 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/bg.js
+86de615973c00ae179844ac9495d5b58 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/ar.js
+03a1e16b85cf951ba0b9bc724ebe49f8 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/ca.js
+85e5a6a3fff31629dd38859854727b2d ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/az.js
+eb197f944f38781ddcd951840ff3eb2b ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/da.js
+939d64f1190a661fbc5bd3d37b0b003a ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/de.js
+c586c2f7d3aa5d7d7028752984aff921 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/cs.js
+139ccfbfed91e92cb15435d08287730c ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/cy.js
+a39869ab1bd7ca6deb83bf957c44b356 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/el.js
+3ea36d068255f8694fbd463218d045e0 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/fa.js
+48c84bd300a2d555cd4af3505a391717 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/en.js
+740e396b6cd397c987955dd5e24e755e ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/eo.js
+a4636947ff07e3357f86e9d364b05966 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/es.js
+b7f413dad4b6a63478d33b11e4f71da7 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/fi.js
+09b5754a58d9ee494d6febb4b511799b ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/et.js
+18b1e3692784bd6e4be134b5cb58a29b ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/eu.js
+b42e89d7f1702a1c3d1cd3395bddfc06 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/fo.js
+5aef09cd29a3e995cbeda9fe9b51bf33 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/fr.js
+a791fed5d97ceddeda22076027e52796 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/gl.js
+806569625eff3094640d9d68c9a1eae1 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/he.js
+e7dad2fd60a43f2ce69ec08638bc2ba4 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/hi.js
+e2d84081d703f8d6818951bab24e2ec2 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/gu.js
+99dcfcc44b92c809120133be5f21ec35 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/id.js
+1132b8c2c532f115c67c7173b5c78413 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/hr.js
+4cd508baac61065ce8f718a7734ed608 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/hu.js
+17642995b683f2108dd0cf834cbc0be5 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/ja.js
+784aad5a339c2ce216938b60cd536bd0 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/it.js
+12f9784d5bfbb732c4fb6c71f9729703 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/km.js
+d2125823474cc37a99f896f6c3934bfc ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/ko.js
+4286fdc5c860fc89fb4b4183b8057c21 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/ku.js
+0cb6eedf2bb73873f1b0ba8d4ebd5eba ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/lt.js
+3bd13c4d0255a7af073c8a97f86e5105 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/mk.js
+8d3be40c4a512322fe9bc0fb3c30bbc8 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/lv.js
+8f9c5f403711efcff05b8133c52bfcb3 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/nb.js
+5923a9b901bdca837fa2c6dda8f836b9 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/mn.js
+9327b46f621c65065ab6359da943f191 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/nl.js
+97d84797ad3f69d01440de333f3a868f ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/oc.js
+7acc85fa6a2a49572aef0bf84180791c ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/no.js
+3d095f4156612cfa294d0d97c122eeb6 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/pl.js
+01c2e62359eec54aea220955743fb836 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/pt.js
+3dc2cebc8fdbab812eeb9aacada4c7b4 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/ro.js
+c66cf250c18f51b7290eb0163ddc4cc4 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/si.js
+1fc5a5eabf92264f870fb3b2aed03965 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/ru.js
+273eac8856e967229dee3e11004cd861 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/sk.js
+70b22ea745df44d05ddc55a6cdfac3a1 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/sl.js
+ffd7f289877a9f778e8933c017a8fb8f ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/sq.js
+2135cda07df7e8bca644b166987bdf32 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/sr.js
+81754530db2d5387a2345c9063d53068 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/th.js
+3c674cf174fe49927b822e06d96b5f6f ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/sv.js
+da02069fd6c941a88aaf5a7b2f33ebb4 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/ug.js
+0d33d5fcd161ed3f3f4ce298889627a9 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/tr.js
+6426deb4eae361c1549a0001b0d8ed53 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/tt.js
+6cf4d1557bc80f461a37613d187a25a8 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/uk.js
+044997341b2965f3b67547e001172959 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/vi.js
+c26404a0ffb9ec5b682beada3ffb80de ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/zh.js
+c6f18c3cd42115c3dab09ae2525a744c ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/_translationstatus.txt
+a1d2a8b2b725fd51757f8ed8e4427340 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/pt-br.js
+a9bba6ecc0d6085631eec484d2ff7db7 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/es-mx.js
+2fffe21fbcd48e050717d722ab7ab5f6 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/zh-cn.js
+a7dabeaaaaee47975530ba516b10cf6e ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/en-au.js
+48e6b2973ff7ab9ae829ece2f2fbf703 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/en-gb.js
+f91257e05d7e8be4b7bf68000bd0fdd0 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/de-ch.js
+b356ccd7f5fb2b3ec3f4648f6d8afabd ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/fr-ca.js
+d4e5693f8424a645f732058dab8f9aec ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/lang/sr-latn.js
+7757b32dc5426701811a2896beba06f0 ./plugins/dcCKEditor/js/ckeditor/plugins/a11yhelp/dialogs/a11yhelp.js
+b37d0404583c0ac273a27873451c3234 ./plugins/dcCKEditor/js/ckeditor/plugins/magicline/images/hidpi/icon-rtl.png
+5ba2e7b6aa50c7843ae9ca01ce08b606 ./plugins/dcCKEditor/js/ckeditor/plugins/magicline/images/hidpi/icon.png
+a29eda8cd2b1ebcbd3379654acebfb85 ./plugins/dcCKEditor/js/ckeditor/plugins/magicline/images/icon-rtl.png
+baf6974c98b636142c7b0b5ba19bd96c ./plugins/dcCKEditor/js/ckeditor/plugins/magicline/images/icon.png
+29760b48741dff38aa0b9b026c7d96b4 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/af.js
+476bbb15aaf9ecdc18d403dd821ffce8 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/bg.js
+4532a4bf572a7edbf8e62ea33a131fa2 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/ar.js
+1db8ed47b8953b6c0e7edeca87e4d5bf ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/ca.js
+bb4f490ecb824bf204d2edc47d790251 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/az.js
+8e4fc35f5ae5b52d0635d6b4fca2e5da ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/da.js
+7653327278316dc38c9a1298a71dd403 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/de.js
+f30098969a80a5dc9a64c57f844c1479 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/cs.js
+61ca24a8f9082e0e750fb44f5a15eaa6 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/cy.js
+e6487cba72491a773b55a2afb9668359 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/el.js
+0950693974ea87134ccb65bc90a2c550 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/fa.js
+01af53808479a2304324ede2e5d63053 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/en.js
+90a467b3a77d8823d83d6e3436b08763 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/eo.js
+a254237de463864becf1fdb0dccf4193 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/es.js
+6739ccadcdf3b4416704ac4121cd9024 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/fi.js
+53e792ef226ba2213fc4ccb683872575 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/et.js
+d96da2dec68ad6477c1db3a1cb986188 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/eu.js
+a8dea66c2a622f92450eb7fe2a0253e1 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/fr.js
+2915a62326d07c774c4434be356b6654 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/gl.js
+b787c4d4708132ec3a3c7253296f082e ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/he.js
+a107dc08153e3c7fe50969bdcdd76bd4 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/id.js
+a55a45821d53305afcf358cc77a74238 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/hr.js
+00863ebbb3245cfbcbd0553443c3a590 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/hu.js
+747d985417f8d16b4c1921ac4c861edf ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/ja.js
+1df4b6173333c16e7aa8a9f356151151 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/it.js
+b5d786bdc923e51347f5df318bf6ab57 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/km.js
+b6e8ff77fd6bc304d379a307e2eb4ac3 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/ko.js
+5acf1217c449ea452bc15f2336eedd5d ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/ku.js
+440c3a2d8e6b0b2c487d5fdfed7176c9 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/lt.js
+a2056cd7119af883959adc44fccef666 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/lv.js
+65f64648a29b93be475a30e8aa92ed55 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/nb.js
+24ef7e345b3d641451087f30c7569de6 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/nl.js
+1c3944a6a0a9e9d49f00be3d6bd633e0 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/oc.js
+1341b993ed9deebd114853ecddae13e3 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/no.js
+66fd33414c34477c054ffe67e222ee23 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/pl.js
+d73d9064357fdaadffab2a9674481ae6 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/pt.js
+54116ac903505a81742d047b17970f15 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/ro.js
+992bc2d1a103124f10d29616dc927b02 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/si.js
+cca61e7db4a3546b0f9beaac0ce98e2a ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/ru.js
+6f9e4d3d8f045ec5d4fe91213cc40b3d ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/sk.js
+933de2c53077f690a8dd6069197bc556 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/sl.js
+92e865bea1de2b5220e610c2c9c5a0c7 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/sq.js
+fba4fd684503b6e7d6ec5fb74ab80284 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/sr.js
+8e04702ac9b716e72cc3e77f68402524 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/th.js
+4808951d9c87b32be0dc08bf348515db ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/sv.js
+19a5707c63a3e1e16a245e89527bba4a ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/ug.js
+bce6da24a89d096f4056d9f9f7e91fa5 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/tr.js
+df50552d5efd6fab0f35bc6e99950dd7 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/tt.js
+39fbe8501b2c4c712810da47a8b1072d ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/uk.js
+0f85c575bb6ab5a03fd4771dc006da21 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/vi.js
+7f92ab066a75bbb9aafa48da86cd0c48 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/zh.js
+18d4f33bdf0d790fad4e88b957d8a417 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/_translationstatus.txt
+b7da49f0842661f8375c546dfc229ccb ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/pt-br.js
+61a068ca4b26e009c369a0a6b729818e ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/es-mx.js
+57c1c99b05e66504f4bdd47416e72ed0 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/zh-cn.js
+2318ee76e1c5c76c56b0145318aeb249 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/en-au.js
+30d94bb19735d71c6723dd542c769560 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/en-ca.js
+bc6f4c496919f88afddbf08beaf1938e ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/en-gb.js
+5b07a325bc7a3002d3a0261edb9cf166 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/de-ch.js
+88205cb7eeb01313f893e1da53194f6e ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/fr-ca.js
+371244932698e781353ef957b8adefc5 ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/lang/sr-latn.js
+a35ddd6d933709c6d9d8a7666efe35df ./plugins/dcCKEditor/js/ckeditor/plugins/specialchar/dialogs/specialchar.js
+830250655b7a436c0fda7d334bfccda5 ./plugins/dcCKEditor/js/ckeditor/plugins/liststyle/dialogs/liststyle.js
+431c58e17fd114e1c9dc0c79846fa17f ./plugins/dcCKEditor/js/ckeditor/plugins/icons.png
+64e36995ae40c9e560eb94e755779cbe ./plugins/dcCKEditor/js/ckeditor/plugins/icons_hidpi.png
+b45c15332f5da516df795f09dbe03656 ./plugins/dcCKEditor/js/ckeditor/plugins/pastefromword/filter/default.js
+de28c0f8f3e090eac88f402feab80246 ./plugins/dcCKEditor/js/ckeditor/plugins/clipboard/dialogs/paste.js
+1c3739650ae5db1cdb388b18f9a00a22 ./plugins/dcCKEditor/js/ckeditor/plugins/pastetools/filter/common.js
+d79d21fb7dc0213f27074bc5b86c58e5 ./plugins/dcCKEditor/js/ckeditor/plugins/pastetools/filter/image.js
+05dcfa6e3332b3ab7ac9218bf420cb58 ./plugins/dcCKEditor/js/ckeditor/plugins/preview/images/pagebreak.gif
+3045d7681e4f47aac7841a34d6ba4cb9 ./plugins/dcCKEditor/js/ckeditor/plugins/preview/styles/screen.css
+257d9ed4c5bcbf8f60f1a908966c3b32 ./plugins/dcCKEditor/js/ckeditor/plugins/preview/preview.html
+fec084ca973998b8149c49af957530c9 ./plugins/dcCKEditor/js/ckeditor/plugins/templates/dialogs/templates.css
+dff17d043dae52407a57ce782f2310c2 ./plugins/dcCKEditor/js/ckeditor/plugins/templates/dialogs/templates.js
+3136e5de71846b8c953b00f10eb397f0 ./plugins/dcCKEditor/js/ckeditor/plugins/templates/templates/images/template1.gif
+7d06d52475862c2eb024be874db07ba1 ./plugins/dcCKEditor/js/ckeditor/plugins/templates/templates/images/template2.gif
+ba8efaef2def8132bac190720d34b4c9 ./plugins/dcCKEditor/js/ckeditor/plugins/templates/templates/images/template3.gif
+e1d78655ea98addcf7b99d421c99b360 ./plugins/dcCKEditor/js/ckeditor/plugins/templates/templates/default.js
+471be63a33e8ce099801e38f84391d42 ./plugins/dcCKEditor/js/ckeditor/contents.css
+64ccbc114d188e8c080a91f4887bc57d ./plugins/dcCKEditor/inc/dc.ckeditor.behaviors.php
+d152a494aba57fbe44f7722913afacf8 ./plugins/dcCKEditor/inc/_config.php
+c3a5f505ae4d43bb137f70f0fb9b5466 ./plugins/dcCKEditor/tpl/index.php
+e3f612b360f3cdddcaf31865002c88d5 ./plugins/dcCKEditor/imgs/icon.png
+3dc03edc1dc86c1d0c76c94f8250f1a3 ./plugins/dcCKEditor/_define.php
+8caa85b3c1d473f11dd1edd89d501591 ./plugins/dcCKEditor/_install.php
+0adb994ffdf7fc85b132fef4c4da3d1e ./plugins/dcCKEditor/locales/en/help/config_help.html
+d34f513a759ef43762e8770d60dc5211 ./plugins/dcCKEditor/locales/en/main.po
+8ac931d2a911ed4364240786a7654b3a ./plugins/dcCKEditor/locales/en/resources.php
+21c5e3d3fbd14ab4749fa0ea80e85088 ./plugins/dcCKEditor/locales/en/main.lang.php
+6a63c66cb3e44309dd1c2be77f532d5f ./plugins/dcCKEditor/locales/fi/main.po
+da1719d9e5e3a0e15394c1bfc290dbad ./plugins/dcCKEditor/locales/fi/main.lang.php
+2267505c5bfa536ec3eb99e05b4a1e72 ./plugins/dcCKEditor/locales/fr/help/config_help.html
+a939017f0a6a48ae78731938d137de2a ./plugins/dcCKEditor/locales/fr/main.po
+8ac931d2a911ed4364240786a7654b3a ./plugins/dcCKEditor/locales/fr/resources.php
+344210ce5d58ee018b136f6ff9aad34d ./plugins/dcCKEditor/locales/fr/main.lang.php
+7a143e9678ce1a5d2ccb00073390dff4 ./plugins/dcCKEditor/locales/templates/messages.pot
+a7b93436ede847297904b5e6efac4a5e ./plugins/dcCKEditor/_prepend.php
+a9a77e2acb8bbeacd0ba2a0d9ce8f7da ./plugins/dcCKEditor/_admin.php
+8a6b026cb45a5c965309c610d7f554f3 ./plugins/dcCKEditor/index.php
+25538877bfe2f47bb0d266b68c1c4aae ./plugins/dcCKEditor/_post_config.php
+41c1a96892e4d3e97394a2447c251b99 ./plugins/akismet/_define.php
+62b02c9f8b654727b69ee2bd170e7eb2 ./plugins/akismet/locales/en/help/help.html
+0ba870a8fb9402ab0f9ad4f1eabe7776 ./plugins/akismet/locales/en/resources.php
+627328879b59f8725e13122ba2fdc196 ./plugins/akismet/locales/fr/help/help.html
+0ba870a8fb9402ab0f9ad4f1eabe7776 ./plugins/akismet/locales/fr/resources.php
+9c98ed1a98dcb7565d9657364a44368b ./plugins/akismet/_prepend.php
+f5e082d99dcc3ecfb1028593ce813326 ./plugins/akismet/class.dc.filter.akismet.php
+91d8933157a3bd7637cc14b0310f2501 ./plugins/aboutConfig/js/index.js
+32f9c54d4f1517ea3fba1aab918363ed ./plugins/aboutConfig/_define.php
+609e5802050c019361f17dd85900ab10 ./plugins/aboutConfig/icon.png
+f6068c4c4cef79527896d30687c81fe9 ./plugins/aboutConfig/locales/en/help/help.html
+d1b9f9dd8f2c0e406c2536e7204bce0b ./plugins/aboutConfig/locales/en/resources.php
+6fc99dc80cac7cea2f3c8c73b159507d ./plugins/aboutConfig/locales/fr/help/help.html
+d1b9f9dd8f2c0e406c2536e7204bce0b ./plugins/aboutConfig/locales/fr/resources.php
+5214d7313aa2ace89d3a115e66b34fec ./plugins/aboutConfig/_admin.php
+2a31da61dc0e1c9990313342f7c90514 ./plugins/aboutConfig/index.php
+6a5dd74094de1f917836cfed3d93c087 ./plugins/aboutConfig/icon-big.png
+adc17e4e97fa9747f37fc2048857b072 ./plugins/userPref/js/index.js
+c818967dc6d3ff41c0824ef0efc24ce8 ./plugins/userPref/_define.php
+4b213557895344f9ff141acfbe8b8f94 ./plugins/userPref/icon.png
+edd3d4e0680edf78a7789a3f4ed56ab5 ./plugins/userPref/locales/en/help/help.html
+54a9122da5653d88f54f5e7b9204f040 ./plugins/userPref/locales/en/resources.php
+4e37914203ea91b0934743d417326df2 ./plugins/userPref/locales/fr/help/help.html
+54a9122da5653d88f54f5e7b9204f040 ./plugins/userPref/locales/fr/resources.php
+0b3e31b40061458669e07ff8d58ecf7f ./plugins/userPref/_admin.php
+1e057d19b9f570deba513daac23f418b ./plugins/userPref/index.php
+2f1237e2f4dca084d4789ab0d663fb78 ./plugins/userPref/icon-big.png
+f187d217b1840bf3f3dce0655a61cfac ./plugins/breadcrumb/_define.php
+e1fc5b150c677201744acc06cff8dffb ./plugins/breadcrumb/_public.php
+26b045669cbb4735344ebe3a1c56d3a3 ./plugins/breadcrumb/_admin.php
+c2552d7c23d3ee7624d2fd596f20eeca ./plugins/widgets/js/widgets.js
+2ba088b56290fdbaa034698d5d1428fd ./plugins/widgets/js/dragdrop.js
+6c116755ffae6384e2872734bf57a580 ./plugins/widgets/_define.php
+07ef2bede45d9d74eb3b011a4d406e42 ./plugins/widgets/_install.php
+cf80db48a3afdf08200bf0a13a1cb1cb ./plugins/widgets/style.css
+e9c8ad6eb98f0a019f03d57422c6a2c0 ./plugins/widgets/_widgets_functions.php
+d969f1e54b1f7039279e424c906b7588 ./plugins/widgets/_public.php
+2bad02372cb986143ee7a051fdbf84c6 ./plugins/widgets/_default_widgets.php
+6708981312cfdb95da31a0b3c2100876 ./plugins/widgets/icon.png
+20287ff3a185d28154cda2ebb2000706 ./plugins/widgets/locales/en/help/help.html
+e24cfcbb43a9bcada9e012f3c9b5ecab ./plugins/widgets/locales/en/resources.php
+ff8440f95760146770b42d81321b4b3d ./plugins/widgets/locales/fr/help/help.html
+e24cfcbb43a9bcada9e012f3c9b5ecab ./plugins/widgets/locales/fr/resources.php
+c115477f18dd595ebadf5ce4b8467b70 ./plugins/widgets/class.widgets.php
+f44a4c832d972bfbeaaa0e8130c968e4 ./plugins/widgets/_admin.php
+e4481eb74001b51e0356fbe00d5fbc73 ./plugins/widgets/index.php
+122a22b51d5429d4cbf5147c468f81ea ./plugins/widgets/icon-big.png
+beb240cb5774ff64e486bdf28dde34d2 ./plugins/fairTrackbacks/_define.php
+da7937860a349427ef24fa879e3154df ./plugins/fairTrackbacks/_public.php
+ac2fdd4d2a94dc2e008bdadd921c4fb1 ./plugins/fairTrackbacks/_prepend.php
+ada122af6a6219634dad5c5592584213 ./plugins/fairTrackbacks/class.dc.filter.fairtrackbacks.php
+bef5d538c1e38f68f0805980e4609736 ./plugins/blowupConfig/js/config.js
+7859ee3234a5e0bc02f6f86191e7d332 ./plugins/blowupConfig/lib/class.blowup.config.php
+d6bc40b6eb22cc39221b496697d3e6df ./plugins/blowupConfig/_define.php
+5e3417cc026fec75ba2d6e802b83cab9 ./plugins/blowupConfig/_install.php
+47aeacd492dd7d9aebc664ab70c662ab ./plugins/blowupConfig/_public.php
+f8adb98ead8d028c20b3aff6faf6b562 ./plugins/blowupConfig/locales/en/help/help.html
+513c6f088958e74e34397363e418793b ./plugins/blowupConfig/locales/en/resources.php
+8c59e58368075905716f40b6b04d164f ./plugins/blowupConfig/locales/fr/help/help.html
+513c6f088958e74e34397363e418793b ./plugins/blowupConfig/locales/fr/resources.php
+f4feac1aa87ed11af6b055b799566274 ./plugins/blowupConfig/_admin.php
+f586caf9107b209c2b6e80a3dc815b20 ./plugins/blowupConfig/index.php
+e2377dbbbd8bedf737c8549ea9acb2e3 ./plugins/blowupConfig/alpha-img/page-bg.png
+2512ef1dc330ca0c6d645ed3c1aebdb8 ./plugins/blowupConfig/alpha-img/comment-b.png
+fe54e82001a853cdbf4b47f3142ded53 ./plugins/blowupConfig/alpha-img/comment-t.png
+5b5139631d9ad49ba5c0190a5b7cbfc4 ./plugins/blowupConfig/alpha-img/page-t/flamingo.png
+e0be7057c8ee8a30741fe4afa1b64e37 ./plugins/blowupConfig/alpha-img/page-t/blank.png
+23748cc1c877e6ea5117abb56ad1d959 ./plugins/blowupConfig/alpha-img/page-t/plumetis.png
+54c74e7b8bd178a946b80833e67fefb2 ./plugins/blowupConfig/alpha-img/page-t/animals.png
+30b69e1873395cf6f343d5e92ddfafb4 ./plugins/blowupConfig/alpha-img/page-t/default.png
+4457fff4792a22bda1e375d3b2b1fed5 ./plugins/blowupConfig/alpha-img/page-t/typo.png
+e830326325652f1079a72c1b9eaedccd ./plugins/blowupConfig/alpha-img/page-t/roadrunner-1.png
+f83ae2f48e3518415d8e9e6a76b4e81d ./plugins/blowupConfig/alpha-img/page-t/roadrunner-2.png
+f9195ec5bbbb1f0cabcd8e856b2bce6c ./plugins/blowupConfig/alpha-img/page-t/rabbit.png
+7cc48059ec65b032e236e749706aee4b ./plugins/blowupConfig/alpha-img/page-t/flourish-1.png
+3eb92f8e5328789e8dd22b7ba19eaadc ./plugins/blowupConfig/alpha-img/page-t/flourish-2.png
+63ac4fa439aeb2b19acbe602315e98b3 ./plugins/blowupConfig/alpha-img/page-t/light-trails-1.png
+83d427cccda5fe90acc283b16b1c1800 ./plugins/blowupConfig/alpha-img/page-t/light-trails-2.png
+6f5cac4ce9e927f0d26756dea2bff69b ./plugins/blowupConfig/alpha-img/page-t/light-trails-3.png
+4f98c2f02c458d862ad75b329d8c10e5 ./plugins/blowupConfig/alpha-img/page-t/light-trails-4.png
+de2c3e1ff75de307be28437c359f6be4 ./plugins/blowupConfig/alpha-img/page-t/image-mask.png
+1a962e92ac6e0b93f5a42ebd09d7674c ./plugins/blowupConfig/alpha-img/page-t/butterflies.png
+1a3c6f6a30dc7d1a350cd5914b51f464 ./plugins/blowupConfig/alpha-img/page-b.png
+b6c22a740270a6d977db1b957f25e90c ./plugins/blowupConfig/alpha-img/gradient-d.png
+d9828722fabe49baccf674a141ad9c26 ./plugins/blowupConfig/alpha-img/gradient-l.png
+ecc8a24415fea061079e3d5327c715f0 ./plugins/blowupConfig/alpha-img/gradient-m.png
+a1ee9185c19f8004a7643b3f5b2b35c2 ./plugins/importExport/js/import_flat.js
+6951c3db3172b3961f3b2565c418243d ./plugins/importExport/js/script.js
+caa11e523e5a3ed38d90480bed5d5a73 ./plugins/importExport/inc/img/progress.png
+3caad8a2a62340057b19df0f762adf4c ./plugins/importExport/inc/flat/class.flat.export.php
+17deb621e052cd09a4d22ba9ddf10449 ./plugins/importExport/inc/flat/class.flat.import.php
+4c77d9baa66bcc31e399d5bbfd24874b ./plugins/importExport/inc/flat/class.flat.backup.php
+491aa9f51b52a718df0fb2a07c784cef ./plugins/importExport/inc/lib.ie.maintenance.php
+4f624291b7a375853a8b7d9d7a73c9ab ./plugins/importExport/inc/class.dc.export.flat.php
+6f3ad52f26a0a9b69618ec376d5b29ce ./plugins/importExport/inc/class.dc.ieModule.php
+544da51e6603ceccf7943462151207da ./plugins/importExport/inc/class.dc.import.dc1.php
+555bc7aff473d2ad76a290d12086b4dd ./plugins/importExport/inc/class.dc.import.wp.php
+cfef425eb0d66357328ff486f274236d ./plugins/importExport/inc/class.dc.import.flat.php
+27141863708a856ef2742b70c0f304d2 ./plugins/importExport/inc/class.dc.import.feed.php
+250c01ba41ac6abd4face9d9ef04218e ./plugins/importExport/_define.php
+7f2191f2e8bb7f40d7fbefcb40c97cb0 ./plugins/importExport/style.css
+7a0fa52684685448a0598515e0c262cb ./plugins/importExport/icon.png
+0ce2e0c3a5f31517b37c67a24a4e218e ./plugins/importExport/locales/en/help/import.html
+c5b24e10938130d7cfdda89b95e19af4 ./plugins/importExport/locales/en/resources.php
+61ec2772f6613c2e019da4f15f9bb7ec ./plugins/importExport/locales/fr/help/import.html
+c5b24e10938130d7cfdda89b95e19af4 ./plugins/importExport/locales/fr/resources.php
+d981940178f73b419a2d668b707c03e5 ./plugins/importExport/_prepend.php
+2cf0c0c41df01dddb0b5331d02109797 ./plugins/importExport/_admin.php
+3e2a266b41d077a32ed04cd5ec3e3d63 ./plugins/importExport/index.php
+0f05bcf2a0bddc324175272ee7c1eb58 ./plugins/importExport/icon-big.png
+7e0ddba4a8cd71087cd55f3a5c5256af ./plugins/simpleMenu/js/simplemenu.js
+95dc007a449485b1251c572006b5f1f7 ./plugins/simpleMenu/icon-small.png
+0fa08b8a65a379db3d52bcdab6de3f02 ./plugins/simpleMenu/_define.php
+147793c3ae7f1647bb80f18c5c4baea0 ./plugins/simpleMenu/_install.php
+4d6140cd62abcf3c3bfbd2455e1f9d1e ./plugins/simpleMenu/_widgets.php
+cb0367887670f8b0456fa4b616641762 ./plugins/simpleMenu/_public.php
+c46fbf665992d49c6518dc76bd591376 ./plugins/simpleMenu/icon.png
+9b6be70e79d0546ab0fc65ccbb0d0ea5 ./plugins/simpleMenu/locales/en/help/help.html
+1315c3fa982954dd2e4a7e7862b4e617 ./plugins/simpleMenu/locales/en/resources.php
+36b46cf558e996fb1ec9ac44a71fb691 ./plugins/simpleMenu/locales/fr/help/help.html
+1315c3fa982954dd2e4a7e7862b4e617 ./plugins/simpleMenu/locales/fr/resources.php
+6afce113024a98f49fdbb99b3477365d ./plugins/simpleMenu/_admin.php
+d57e5b3237b4616ee541ec231f9680c9 ./plugins/simpleMenu/index.php
+c91f0ff6f43d135f5d8f84732da54a1c ./CREDITS
+aaf6bfe3962647cc450728e1eeb87b2b ./CONTRIBUTING.md
diff --git a/dotclear._no/inc/js/jquery/1.11.1/jquery.cookie.js b/dotclear._no/inc/js/jquery/1.11.1/jquery.cookie.js
new file mode 100755
index 0000000..741936b
--- /dev/null
+++ b/dotclear._no/inc/js/jquery/1.11.1/jquery.cookie.js
@@ -0,0 +1,13 @@
+
+(function(factory){if(typeof define==='function'&&define.amd){define(['jquery'],factory);}else if(typeof exports==='object'){factory(require('jquery'));}else{factory(jQuery);}}(function($){var pluses=/\+/g;function encode(s){return config.raw?s:encodeURIComponent(s);}
+function decode(s){return config.raw?s:decodeURIComponent(s);}
+function stringifyCookieValue(value){return encode(config.json?JSON.stringify(value):String(value));}
+function parseCookieValue(s){if(s.indexOf('"')===0){s=s.slice(1,-1).replace(/\\"/g,'"').replace(/\\\\/g,'\\');}
+try{s=decodeURIComponent(s.replace(pluses,' '));return config.json?JSON.parse(s):s;}catch(e){}}
+function read(s,converter){var value=config.raw?s:parseCookieValue(s);return $.isFunction(converter)?converter(value):value;}
+var config=$.cookie=function(key,value,options){if(arguments.length>1&&!$.isFunction(value)){options=$.extend({},config.defaults,options);if(typeof options.expires==='number'){var days=options.expires,t=options.expires=new Date();t.setTime(+t+days*864e+5);}
+return(document.cookie=[encode(key),'=',stringifyCookieValue(value),options.expires?'; expires='+options.expires.toUTCString():'',options.path?'; path='+options.path:'',options.domain?'; domain='+options.domain:'',options.secure?'; secure':''].join(''));}
+var result=key?undefined:{};var cookies=document.cookie?document.cookie.split('; '):[];for(var i=0,l=cookies.length;i=0&&j=0;},isEmptyObject:function(obj){var name;for(name in obj){return false;}
+return true;},isPlainObject:function(obj){var key;if(!obj||jQuery.type(obj)!=="object"||obj.nodeType||jQuery.isWindow(obj)){return false;}
+try{if(obj.constructor&&!hasOwn.call(obj,"constructor")&&!hasOwn.call(obj.constructor.prototype,"isPrototypeOf")){return false;}}catch(e){return false;}
+if(support.ownLast){for(key in obj){return hasOwn.call(obj,key);}}
+for(key in obj){}
+return key===undefined||hasOwn.call(obj,key);},type:function(obj){if(obj==null){return obj+"";}
+return typeof obj==="object"||typeof obj==="function"?class2type[toString.call(obj)]||"object":typeof obj;},globalEval:function(data){if(data&&jQuery.trim(data)){(window.execScript||function(data){window["eval"].call(window,data);})(data);}},camelCase:function(string){return string.replace(rmsPrefix,"ms-").replace(rdashAlpha,fcamelCase);},nodeName:function(elem,name){return elem.nodeName&&elem.nodeName.toLowerCase()===name.toLowerCase();},each:function(obj,callback,args){var value,i=0,length=obj.length,isArray=isArraylike(obj);if(args){if(isArray){for(;i0&&(length-1)in obj;}
+var Sizzle=(function(window){var i,support,Expr,getText,isXML,tokenize,compile,select,outermostContext,sortInput,hasDuplicate,setDocument,document,docElem,documentIsHTML,rbuggyQSA,rbuggyMatches,matches,contains,expando="sizzle"+-(new Date()),preferredDoc=window.document,dirruns=0,done=0,classCache=createCache(),tokenCache=createCache(),compilerCache=createCache(),sortOrder=function(a,b){if(a===b){hasDuplicate=true;}
+return 0;},strundefined=typeof undefined,MAX_NEGATIVE=1<<31,hasOwn=({}).hasOwnProperty,arr=[],pop=arr.pop,push_native=arr.push,push=arr.push,slice=arr.slice,indexOf=arr.indexOf||function(elem){var i=0,len=this.length;for(;i+~]|"+whitespace+")"+whitespace+"*"),rattributeQuotes=new RegExp("="+whitespace+"*([^\\]'\"]*?)"+whitespace+"*\\]","g"),rpseudo=new RegExp(pseudos),ridentifier=new RegExp("^"+identifier+"$"),matchExpr={"ID":new RegExp("^#("+characterEncoding+")"),"CLASS":new RegExp("^\\.("+characterEncoding+")"),"TAG":new RegExp("^("+characterEncoding.replace("w","w*")+")"),"ATTR":new RegExp("^"+attributes),"PSEUDO":new RegExp("^"+pseudos),"CHILD":new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+whitespace+"*(even|odd|(([+-]|)(\\d*)n|)"+whitespace+"*(?:([+-]|)"+whitespace+"*(\\d+)|))"+whitespace+"*\\)|)","i"),"bool":new RegExp("^(?:"+booleans+")$","i"),"needsContext":new RegExp("^"+whitespace+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+
+whitespace+"*((?:-\\d)?\\d*)"+whitespace+"*\\)|)(?=[^-]|$)","i")},rinputs=/^(?:input|select|textarea|button)$/i,rheader=/^h\d$/i,rnative=/^[^{]+\{\s*\[native \w/,rquickExpr=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,rsibling=/[+~]/,rescape=/'|\\/g,runescape=new RegExp("\\\\([\\da-f]{1,6}"+whitespace+"?|("+whitespace+")|.)","ig"),funescape=function(_,escaped,escapedWhitespace){var high="0x"+escaped-0x10000;return high!==high||escapedWhitespace?escaped:high<0?String.fromCharCode(high+0x10000):String.fromCharCode(high>>10|0xD800,high&0x3FF|0xDC00);};try{push.apply((arr=slice.call(preferredDoc.childNodes)),preferredDoc.childNodes);arr[preferredDoc.childNodes.length].nodeType;}catch(e){push={apply:arr.length?function(target,els){push_native.apply(target,slice.call(els));}:function(target,els){var j=target.length,i=0;while((target[j++]=els[i++])){}
+target.length=j-1;}};}
+function Sizzle(selector,context,results,seed){var match,elem,m,nodeType,i,groups,old,nid,newContext,newSelector;if((context?context.ownerDocument||context:preferredDoc)!==document){setDocument(context);}
+context=context||document;results=results||[];if(!selector||typeof selector!=="string"){return results;}
+if((nodeType=context.nodeType)!==1&&nodeType!==9){return[];}
+if(documentIsHTML&&!seed){if((match=rquickExpr.exec(selector))){if((m=match[1])){if(nodeType===9){elem=context.getElementById(m);if(elem&&elem.parentNode){if(elem.id===m){results.push(elem);return results;}}else{return results;}}else{if(context.ownerDocument&&(elem=context.ownerDocument.getElementById(m))&&contains(context,elem)&&elem.id===m){results.push(elem);return results;}}}else if(match[2]){push.apply(results,context.getElementsByTagName(selector));return results;}else if((m=match[3])&&support.getElementsByClassName&&context.getElementsByClassName){push.apply(results,context.getElementsByClassName(m));return results;}}
+if(support.qsa&&(!rbuggyQSA||!rbuggyQSA.test(selector))){nid=old=expando;newContext=context;newSelector=nodeType===9&&selector;if(nodeType===1&&context.nodeName.toLowerCase()!=="object"){groups=tokenize(selector);if((old=context.getAttribute("id"))){nid=old.replace(rescape,"\\$&");}else{context.setAttribute("id",nid);}
+nid="[id='"+nid+"'] ";i=groups.length;while(i--){groups[i]=nid+toSelector(groups[i]);}
+newContext=rsibling.test(selector)&&testContext(context.parentNode)||context;newSelector=groups.join(",");}
+if(newSelector){try{push.apply(results,newContext.querySelectorAll(newSelector));return results;}catch(qsaError){}finally{if(!old){context.removeAttribute("id");}}}}}
+return select(selector.replace(rtrim,"$1"),context,results,seed);}
+function createCache(){var keys=[];function cache(key,value){if(keys.push(key+" ")>Expr.cacheLength){delete cache[keys.shift()];}
+return(cache[key+" "]=value);}
+return cache;}
+function markFunction(fn){fn[expando]=true;return fn;}
+function assert(fn){var div=document.createElement("div");try{return!!fn(div);}catch(e){return false;}finally{if(div.parentNode){div.parentNode.removeChild(div);}
+div=null;}}
+function addHandle(attrs,handler){var arr=attrs.split("|"),i=attrs.length;while(i--){Expr.attrHandle[arr[i]]=handler;}}
+function siblingCheck(a,b){var cur=b&&a,diff=cur&&a.nodeType===1&&b.nodeType===1&&(~b.sourceIndex||MAX_NEGATIVE)-
+(~a.sourceIndex||MAX_NEGATIVE);if(diff){return diff;}
+if(cur){while((cur=cur.nextSibling)){if(cur===b){return-1;}}}
+return a?1:-1;}
+function createInputPseudo(type){return function(elem){var name=elem.nodeName.toLowerCase();return name==="input"&&elem.type===type;};}
+function createButtonPseudo(type){return function(elem){var name=elem.nodeName.toLowerCase();return(name==="input"||name==="button")&&elem.type===type;};}
+function createPositionalPseudo(fn){return markFunction(function(argument){argument=+argument;return markFunction(function(seed,matches){var j,matchIndexes=fn([],seed.length,argument),i=matchIndexes.length;while(i--){if(seed[(j=matchIndexes[i])]){seed[j]=!(matches[j]=seed[j]);}}});});}
+function testContext(context){return context&&typeof context.getElementsByTagName!==strundefined&&context;}
+support=Sizzle.support={};isXML=Sizzle.isXML=function(elem){var documentElement=elem&&(elem.ownerDocument||elem).documentElement;return documentElement?documentElement.nodeName!=="HTML":false;};setDocument=Sizzle.setDocument=function(node){var hasCompare,doc=node?node.ownerDocument||node:preferredDoc,parent=doc.defaultView;if(doc===document||doc.nodeType!==9||!doc.documentElement){return document;}
+document=doc;docElem=doc.documentElement;documentIsHTML=!isXML(doc);if(parent&&parent!==parent.top){if(parent.addEventListener){parent.addEventListener("unload",function(){setDocument();},false);}else if(parent.attachEvent){parent.attachEvent("onunload",function(){setDocument();});}}
+support.attributes=assert(function(div){div.className="i";return!div.getAttribute("className");});support.getElementsByTagName=assert(function(div){div.appendChild(doc.createComment(""));return!div.getElementsByTagName("*").length;});support.getElementsByClassName=rnative.test(doc.getElementsByClassName)&&assert(function(div){div.innerHTML="
";div.firstChild.className="i";return div.getElementsByClassName("i").length===2;});support.getById=assert(function(div){docElem.appendChild(div).id=expando;return!doc.getElementsByName||!doc.getElementsByName(expando).length;});if(support.getById){Expr.find["ID"]=function(id,context){if(typeof context.getElementById!==strundefined&&documentIsHTML){var m=context.getElementById(id);return m&&m.parentNode?[m]:[];}};Expr.filter["ID"]=function(id){var attrId=id.replace(runescape,funescape);return function(elem){return elem.getAttribute("id")===attrId;};};}else{delete Expr.find["ID"];Expr.filter["ID"]=function(id){var attrId=id.replace(runescape,funescape);return function(elem){var node=typeof elem.getAttributeNode!==strundefined&&elem.getAttributeNode("id");return node&&node.value===attrId;};};}
+Expr.find["TAG"]=support.getElementsByTagName?function(tag,context){if(typeof context.getElementsByTagName!==strundefined){return context.getElementsByTagName(tag);}}:function(tag,context){var elem,tmp=[],i=0,results=context.getElementsByTagName(tag);if(tag==="*"){while((elem=results[i++])){if(elem.nodeType===1){tmp.push(elem);}}
+return tmp;}
+return results;};Expr.find["CLASS"]=support.getElementsByClassName&&function(className,context){if(typeof context.getElementsByClassName!==strundefined&&documentIsHTML){return context.getElementsByClassName(className);}};rbuggyMatches=[];rbuggyQSA=[];if((support.qsa=rnative.test(doc.querySelectorAll))){assert(function(div){div.innerHTML=" ";if(div.querySelectorAll("[msallowclip^='']").length){rbuggyQSA.push("[*^$]="+whitespace+"*(?:''|\"\")");}
+if(!div.querySelectorAll("[selected]").length){rbuggyQSA.push("\\["+whitespace+"*(?:value|"+booleans+")");}
+if(!div.querySelectorAll(":checked").length){rbuggyQSA.push(":checked");}});assert(function(div){var input=doc.createElement("input");input.setAttribute("type","hidden");div.appendChild(input).setAttribute("name","D");if(div.querySelectorAll("[name=d]").length){rbuggyQSA.push("name"+whitespace+"*[*^$|!~]?=");}
+if(!div.querySelectorAll(":enabled").length){rbuggyQSA.push(":enabled",":disabled");}
+div.querySelectorAll("*,:x");rbuggyQSA.push(",.*:");});}
+if((support.matchesSelector=rnative.test((matches=docElem.matches||docElem.webkitMatchesSelector||docElem.mozMatchesSelector||docElem.oMatchesSelector||docElem.msMatchesSelector)))){assert(function(div){support.disconnectedMatch=matches.call(div,"div");matches.call(div,"[s!='']:x");rbuggyMatches.push("!=",pseudos);});}
+rbuggyQSA=rbuggyQSA.length&&new RegExp(rbuggyQSA.join("|"));rbuggyMatches=rbuggyMatches.length&&new RegExp(rbuggyMatches.join("|"));hasCompare=rnative.test(docElem.compareDocumentPosition);contains=hasCompare||rnative.test(docElem.contains)?function(a,b){var adown=a.nodeType===9?a.documentElement:a,bup=b&&b.parentNode;return a===bup||!!(bup&&bup.nodeType===1&&(adown.contains?adown.contains(bup):a.compareDocumentPosition&&a.compareDocumentPosition(bup)&16));}:function(a,b){if(b){while((b=b.parentNode)){if(b===a){return true;}}}
+return false;};sortOrder=hasCompare?function(a,b){if(a===b){hasDuplicate=true;return 0;}
+var compare=!a.compareDocumentPosition-!b.compareDocumentPosition;if(compare){return compare;}
+compare=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1;if(compare&1||(!support.sortDetached&&b.compareDocumentPosition(a)===compare)){if(a===doc||a.ownerDocument===preferredDoc&&contains(preferredDoc,a)){return-1;}
+if(b===doc||b.ownerDocument===preferredDoc&&contains(preferredDoc,b)){return 1;}
+return sortInput?(indexOf.call(sortInput,a)-indexOf.call(sortInput,b)):0;}
+return compare&4?-1:1;}:function(a,b){if(a===b){hasDuplicate=true;return 0;}
+var cur,i=0,aup=a.parentNode,bup=b.parentNode,ap=[a],bp=[b];if(!aup||!bup){return a===doc?-1:b===doc?1:aup?-1:bup?1:sortInput?(indexOf.call(sortInput,a)-indexOf.call(sortInput,b)):0;}else if(aup===bup){return siblingCheck(a,b);}
+cur=a;while((cur=cur.parentNode)){ap.unshift(cur);}
+cur=b;while((cur=cur.parentNode)){bp.unshift(cur);}
+while(ap[i]===bp[i]){i++;}
+return i?siblingCheck(ap[i],bp[i]):ap[i]===preferredDoc?-1:bp[i]===preferredDoc?1:0;};return doc;};Sizzle.matches=function(expr,elements){return Sizzle(expr,null,null,elements);};Sizzle.matchesSelector=function(elem,expr){if((elem.ownerDocument||elem)!==document){setDocument(elem);}
+expr=expr.replace(rattributeQuotes,"='$1']");if(support.matchesSelector&&documentIsHTML&&(!rbuggyMatches||!rbuggyMatches.test(expr))&&(!rbuggyQSA||!rbuggyQSA.test(expr))){try{var ret=matches.call(elem,expr);if(ret||support.disconnectedMatch||elem.document&&elem.document.nodeType!==11){return ret;}}catch(e){}}
+return Sizzle(expr,document,null,[elem]).length>0;};Sizzle.contains=function(context,elem){if((context.ownerDocument||context)!==document){setDocument(context);}
+return contains(context,elem);};Sizzle.attr=function(elem,name){if((elem.ownerDocument||elem)!==document){setDocument(elem);}
+var fn=Expr.attrHandle[name.toLowerCase()],val=fn&&hasOwn.call(Expr.attrHandle,name.toLowerCase())?fn(elem,name,!documentIsHTML):undefined;return val!==undefined?val:support.attributes||!documentIsHTML?elem.getAttribute(name):(val=elem.getAttributeNode(name))&&val.specified?val.value:null;};Sizzle.error=function(msg){throw new Error("Syntax error, unrecognized expression: "+msg);};Sizzle.uniqueSort=function(results){var elem,duplicates=[],j=0,i=0;hasDuplicate=!support.detectDuplicates;sortInput=!support.sortStable&&results.slice(0);results.sort(sortOrder);if(hasDuplicate){while((elem=results[i++])){if(elem===results[i]){j=duplicates.push(i);}}
+while(j--){results.splice(duplicates[j],1);}}
+sortInput=null;return results;};getText=Sizzle.getText=function(elem){var node,ret="",i=0,nodeType=elem.nodeType;if(!nodeType){while((node=elem[i++])){ret+=getText(node);}}else if(nodeType===1||nodeType===9||nodeType===11){if(typeof elem.textContent==="string"){return elem.textContent;}else{for(elem=elem.firstChild;elem;elem=elem.nextSibling){ret+=getText(elem);}}}else if(nodeType===3||nodeType===4){return elem.nodeValue;}
+return ret;};Expr=Sizzle.selectors={cacheLength:50,createPseudo:markFunction,match:matchExpr,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:true}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:true},"~":{dir:"previousSibling"}},preFilter:{"ATTR":function(match){match[1]=match[1].replace(runescape,funescape);match[3]=(match[3]||match[4]||match[5]||"").replace(runescape,funescape);if(match[2]==="~="){match[3]=" "+match[3]+" ";}
+return match.slice(0,4);},"CHILD":function(match){match[1]=match[1].toLowerCase();if(match[1].slice(0,3)==="nth"){if(!match[3]){Sizzle.error(match[0]);}
+match[4]=+(match[4]?match[5]+(match[6]||1):2*(match[3]==="even"||match[3]==="odd"));match[5]=+((match[7]+match[8])||match[3]==="odd");}else if(match[3]){Sizzle.error(match[0]);}
+return match;},"PSEUDO":function(match){var excess,unquoted=!match[6]&&match[2];if(matchExpr["CHILD"].test(match[0])){return null;}
+if(match[3]){match[2]=match[4]||match[5]||"";}else if(unquoted&&rpseudo.test(unquoted)&&(excess=tokenize(unquoted,true))&&(excess=unquoted.indexOf(")",unquoted.length-excess)-unquoted.length)){match[0]=match[0].slice(0,excess);match[2]=unquoted.slice(0,excess);}
+return match.slice(0,3);}},filter:{"TAG":function(nodeNameSelector){var nodeName=nodeNameSelector.replace(runescape,funescape).toLowerCase();return nodeNameSelector==="*"?function(){return true;}:function(elem){return elem.nodeName&&elem.nodeName.toLowerCase()===nodeName;};},"CLASS":function(className){var pattern=classCache[className+" "];return pattern||(pattern=new RegExp("(^|"+whitespace+")"+className+"("+whitespace+"|$)"))&&classCache(className,function(elem){return pattern.test(typeof elem.className==="string"&&elem.className||typeof elem.getAttribute!==strundefined&&elem.getAttribute("class")||"");});},"ATTR":function(name,operator,check){return function(elem){var result=Sizzle.attr(elem,name);if(result==null){return operator==="!=";}
+if(!operator){return true;}
+result+="";return operator==="="?result===check:operator==="!="?result!==check:operator==="^="?check&&result.indexOf(check)===0:operator==="*="?check&&result.indexOf(check)>-1:operator==="$="?check&&result.slice(-check.length)===check:operator==="~="?(" "+result+" ").indexOf(check)>-1:operator==="|="?result===check||result.slice(0,check.length+1)===check+"-":false;};},"CHILD":function(type,what,argument,first,last){var simple=type.slice(0,3)!=="nth",forward=type.slice(-4)!=="last",ofType=what==="of-type";return first===1&&last===0?function(elem){return!!elem.parentNode;}:function(elem,context,xml){var cache,outerCache,node,diff,nodeIndex,start,dir=simple!==forward?"nextSibling":"previousSibling",parent=elem.parentNode,name=ofType&&elem.nodeName.toLowerCase(),useCache=!xml&&!ofType;if(parent){if(simple){while(dir){node=elem;while((node=node[dir])){if(ofType?node.nodeName.toLowerCase()===name:node.nodeType===1){return false;}}
+start=dir=type==="only"&&!start&&"nextSibling";}
+return true;}
+start=[forward?parent.firstChild:parent.lastChild];if(forward&&useCache){outerCache=parent[expando]||(parent[expando]={});cache=outerCache[type]||[];nodeIndex=cache[0]===dirruns&&cache[1];diff=cache[0]===dirruns&&cache[2];node=nodeIndex&&parent.childNodes[nodeIndex];while((node=++nodeIndex&&node&&node[dir]||(diff=nodeIndex=0)||start.pop())){if(node.nodeType===1&&++diff&&node===elem){outerCache[type]=[dirruns,nodeIndex,diff];break;}}}else if(useCache&&(cache=(elem[expando]||(elem[expando]={}))[type])&&cache[0]===dirruns){diff=cache[1];}else{while((node=++nodeIndex&&node&&node[dir]||(diff=nodeIndex=0)||start.pop())){if((ofType?node.nodeName.toLowerCase()===name:node.nodeType===1)&&++diff){if(useCache){(node[expando]||(node[expando]={}))[type]=[dirruns,diff];}
+if(node===elem){break;}}}}
+diff-=last;return diff===first||(diff%first===0&&diff/first>=0);}};},"PSEUDO":function(pseudo,argument){var args,fn=Expr.pseudos[pseudo]||Expr.setFilters[pseudo.toLowerCase()]||Sizzle.error("unsupported pseudo: "+pseudo);if(fn[expando]){return fn(argument);}
+if(fn.length>1){args=[pseudo,pseudo,"",argument];return Expr.setFilters.hasOwnProperty(pseudo.toLowerCase())?markFunction(function(seed,matches){var idx,matched=fn(seed,argument),i=matched.length;while(i--){idx=indexOf.call(seed,matched[i]);seed[idx]=!(matches[idx]=matched[i]);}}):function(elem){return fn(elem,0,args);};}
+return fn;}},pseudos:{"not":markFunction(function(selector){var input=[],results=[],matcher=compile(selector.replace(rtrim,"$1"));return matcher[expando]?markFunction(function(seed,matches,context,xml){var elem,unmatched=matcher(seed,null,xml,[]),i=seed.length;while(i--){if((elem=unmatched[i])){seed[i]=!(matches[i]=elem);}}}):function(elem,context,xml){input[0]=elem;matcher(input,null,xml,results);return!results.pop();};}),"has":markFunction(function(selector){return function(elem){return Sizzle(selector,elem).length>0;};}),"contains":markFunction(function(text){return function(elem){return(elem.textContent||elem.innerText||getText(elem)).indexOf(text)>-1;};}),"lang":markFunction(function(lang){if(!ridentifier.test(lang||"")){Sizzle.error("unsupported lang: "+lang);}
+lang=lang.replace(runescape,funescape).toLowerCase();return function(elem){var elemLang;do{if((elemLang=documentIsHTML?elem.lang:elem.getAttribute("xml:lang")||elem.getAttribute("lang"))){elemLang=elemLang.toLowerCase();return elemLang===lang||elemLang.indexOf(lang+"-")===0;}}while((elem=elem.parentNode)&&elem.nodeType===1);return false;};}),"target":function(elem){var hash=window.location&&window.location.hash;return hash&&hash.slice(1)===elem.id;},"root":function(elem){return elem===docElem;},"focus":function(elem){return elem===document.activeElement&&(!document.hasFocus||document.hasFocus())&&!!(elem.type||elem.href||~elem.tabIndex);},"enabled":function(elem){return elem.disabled===false;},"disabled":function(elem){return elem.disabled===true;},"checked":function(elem){var nodeName=elem.nodeName.toLowerCase();return(nodeName==="input"&&!!elem.checked)||(nodeName==="option"&&!!elem.selected);},"selected":function(elem){if(elem.parentNode){elem.parentNode.selectedIndex;}
+return elem.selected===true;},"empty":function(elem){for(elem=elem.firstChild;elem;elem=elem.nextSibling){if(elem.nodeType<6){return false;}}
+return true;},"parent":function(elem){return!Expr.pseudos["empty"](elem);},"header":function(elem){return rheader.test(elem.nodeName);},"input":function(elem){return rinputs.test(elem.nodeName);},"button":function(elem){var name=elem.nodeName.toLowerCase();return name==="input"&&elem.type==="button"||name==="button";},"text":function(elem){var attr;return elem.nodeName.toLowerCase()==="input"&&elem.type==="text"&&((attr=elem.getAttribute("type"))==null||attr.toLowerCase()==="text");},"first":createPositionalPseudo(function(){return[0];}),"last":createPositionalPseudo(function(matchIndexes,length){return[length-1];}),"eq":createPositionalPseudo(function(matchIndexes,length,argument){return[argument<0?argument+length:argument];}),"even":createPositionalPseudo(function(matchIndexes,length){var i=0;for(;i=0;){matchIndexes.push(i);}
+return matchIndexes;}),"gt":createPositionalPseudo(function(matchIndexes,length,argument){var i=argument<0?argument+length:argument;for(;++i1?function(elem,context,xml){var i=matchers.length;while(i--){if(!matchers[i](elem,context,xml)){return false;}}
+return true;}:matchers[0];}
+function multipleContexts(selector,contexts,results){var i=0,len=contexts.length;for(;i-1){seed[temp]=!(results[temp]=elem);}}}}else{matcherOut=condense(matcherOut===results?matcherOut.splice(preexisting,matcherOut.length):matcherOut);if(postFinder){postFinder(null,results,matcherOut,xml);}else{push.apply(results,matcherOut);}}});}
+function matcherFromTokens(tokens){var checkContext,matcher,j,len=tokens.length,leadingRelative=Expr.relative[tokens[0].type],implicitRelative=leadingRelative||Expr.relative[" "],i=leadingRelative?1:0,matchContext=addCombinator(function(elem){return elem===checkContext;},implicitRelative,true),matchAnyContext=addCombinator(function(elem){return indexOf.call(checkContext,elem)>-1;},implicitRelative,true),matchers=[function(elem,context,xml){return(!leadingRelative&&(xml||context!==outermostContext))||((checkContext=context).nodeType?matchContext(elem,context,xml):matchAnyContext(elem,context,xml));}];for(;i1&&elementMatcher(matchers),i>1&&toSelector(tokens.slice(0,i-1).concat({value:tokens[i-2].type===" "?"*":""})).replace(rtrim,"$1"),matcher,i0,byElement=elementMatchers.length>0,superMatcher=function(seed,context,xml,results,outermost){var elem,j,matcher,matchedCount=0,i="0",unmatched=seed&&[],setMatched=[],contextBackup=outermostContext,elems=seed||byElement&&Expr.find["TAG"]("*",outermost),dirrunsUnique=(dirruns+=contextBackup==null?1:Math.random()||0.1),len=elems.length;if(outermost){outermostContext=context!==document&&context;}
+for(;i!==len&&(elem=elems[i])!=null;i++){if(byElement&&elem){j=0;while((matcher=elementMatchers[j++])){if(matcher(elem,context,xml)){results.push(elem);break;}}
+if(outermost){dirruns=dirrunsUnique;}}
+if(bySet){if((elem=!matcher&&elem)){matchedCount--;}
+if(seed){unmatched.push(elem);}}}
+matchedCount+=i;if(bySet&&i!==matchedCount){j=0;while((matcher=setMatchers[j++])){matcher(unmatched,setMatched,context,xml);}
+if(seed){if(matchedCount>0){while(i--){if(!(unmatched[i]||setMatched[i])){setMatched[i]=pop.call(results);}}}
+setMatched=condense(setMatched);}
+push.apply(results,setMatched);if(outermost&&!seed&&setMatched.length>0&&(matchedCount+setMatchers.length)>1){Sizzle.uniqueSort(results);}}
+if(outermost){dirruns=dirrunsUnique;outermostContext=contextBackup;}
+return unmatched;};return bySet?markFunction(superMatcher):superMatcher;}
+compile=Sizzle.compile=function(selector,match){var i,setMatchers=[],elementMatchers=[],cached=compilerCache[selector+" "];if(!cached){if(!match){match=tokenize(selector);}
+i=match.length;while(i--){cached=matcherFromTokens(match[i]);if(cached[expando]){setMatchers.push(cached);}else{elementMatchers.push(cached);}}
+cached=compilerCache(selector,matcherFromGroupMatchers(elementMatchers,setMatchers));cached.selector=selector;}
+return cached;};select=Sizzle.select=function(selector,context,results,seed){var i,tokens,token,type,find,compiled=typeof selector==="function"&&selector,match=!seed&&tokenize((selector=compiled.selector||selector));results=results||[];if(match.length===1){tokens=match[0]=match[0].slice(0);if(tokens.length>2&&(token=tokens[0]).type==="ID"&&support.getById&&context.nodeType===9&&documentIsHTML&&Expr.relative[tokens[1].type]){context=(Expr.find["ID"](token.matches[0].replace(runescape,funescape),context)||[])[0];if(!context){return results;}else if(compiled){context=context.parentNode;}
+selector=selector.slice(tokens.shift().value.length);}
+i=matchExpr["needsContext"].test(selector)?0:tokens.length;while(i--){token=tokens[i];if(Expr.relative[(type=token.type)]){break;}
+if((find=Expr.find[type])){if((seed=find(token.matches[0].replace(runescape,funescape),rsibling.test(tokens[0].type)&&testContext(context.parentNode)||context))){tokens.splice(i,1);selector=seed.length&&toSelector(tokens);if(!selector){push.apply(results,seed);return results;}
+break;}}}}
+(compiled||compile(selector,match))(seed,context,!documentIsHTML,results,rsibling.test(selector)&&testContext(context.parentNode)||context);return results;};support.sortStable=expando.split("").sort(sortOrder).join("")===expando;support.detectDuplicates=!!hasDuplicate;setDocument();support.sortDetached=assert(function(div1){return div1.compareDocumentPosition(document.createElement("div"))&1;});if(!assert(function(div){div.innerHTML=" ";return div.firstChild.getAttribute("href")==="#";})){addHandle("type|href|height|width",function(elem,name,isXML){if(!isXML){return elem.getAttribute(name,name.toLowerCase()==="type"?1:2);}});}
+if(!support.attributes||!assert(function(div){div.innerHTML=" ";div.firstChild.setAttribute("value","");return div.firstChild.getAttribute("value")==="";})){addHandle("value",function(elem,name,isXML){if(!isXML&&elem.nodeName.toLowerCase()==="input"){return elem.defaultValue;}});}
+if(!assert(function(div){return div.getAttribute("disabled")==null;})){addHandle(booleans,function(elem,name,isXML){var val;if(!isXML){return elem[name]===true?name.toLowerCase():(val=elem.getAttributeNode(name))&&val.specified?val.value:null;}});}
+return Sizzle;})(window);jQuery.find=Sizzle;jQuery.expr=Sizzle.selectors;jQuery.expr[":"]=jQuery.expr.pseudos;jQuery.unique=Sizzle.uniqueSort;jQuery.text=Sizzle.getText;jQuery.isXMLDoc=Sizzle.isXML;jQuery.contains=Sizzle.contains;var rneedsContext=jQuery.expr.match.needsContext;var rsingleTag=(/^<(\w+)\s*\/?>(?:<\/\1>|)$/);var risSimple=/^.[^:#\[\.,]*$/;function winnow(elements,qualifier,not){if(jQuery.isFunction(qualifier)){return jQuery.grep(elements,function(elem,i){return!!qualifier.call(elem,i,elem)!==not;});}
+if(qualifier.nodeType){return jQuery.grep(elements,function(elem){return(elem===qualifier)!==not;});}
+if(typeof qualifier==="string"){if(risSimple.test(qualifier)){return jQuery.filter(qualifier,elements,not);}
+qualifier=jQuery.filter(qualifier,elements);}
+return jQuery.grep(elements,function(elem){return(jQuery.inArray(elem,qualifier)>=0)!==not;});}
+jQuery.filter=function(expr,elems,not){var elem=elems[0];if(not){expr=":not("+expr+")";}
+return elems.length===1&&elem.nodeType===1?jQuery.find.matchesSelector(elem,expr)?[elem]:[]:jQuery.find.matches(expr,jQuery.grep(elems,function(elem){return elem.nodeType===1;}));};jQuery.fn.extend({find:function(selector){var i,ret=[],self=this,len=self.length;if(typeof selector!=="string"){return this.pushStack(jQuery(selector).filter(function(){for(i=0;i1?jQuery.unique(ret):ret);ret.selector=this.selector?this.selector+" "+selector:selector;return ret;},filter:function(selector){return this.pushStack(winnow(this,selector||[],false));},not:function(selector){return this.pushStack(winnow(this,selector||[],true));},is:function(selector){return!!winnow(this,typeof selector==="string"&&rneedsContext.test(selector)?jQuery(selector):selector||[],false).length;}});var rootjQuery,document=window.document,rquickExpr=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,init=jQuery.fn.init=function(selector,context){var match,elem;if(!selector){return this;}
+if(typeof selector==="string"){if(selector.charAt(0)==="<"&&selector.charAt(selector.length-1)===">"&&selector.length>=3){match=[null,selector,null];}else{match=rquickExpr.exec(selector);}
+if(match&&(match[1]||!context)){if(match[1]){context=context instanceof jQuery?context[0]:context;jQuery.merge(this,jQuery.parseHTML(match[1],context&&context.nodeType?context.ownerDocument||context:document,true));if(rsingleTag.test(match[1])&&jQuery.isPlainObject(context)){for(match in context){if(jQuery.isFunction(this[match])){this[match](context[match]);}else{this.attr(match,context[match]);}}}
+return this;}else{elem=document.getElementById(match[2]);if(elem&&elem.parentNode){if(elem.id!==match[2]){return rootjQuery.find(selector);}
+this.length=1;this[0]=elem;}
+this.context=document;this.selector=selector;return this;}}else if(!context||context.jquery){return(context||rootjQuery).find(selector);}else{return this.constructor(context).find(selector);}}else if(selector.nodeType){this.context=this[0]=selector;this.length=1;return this;}else if(jQuery.isFunction(selector)){return typeof rootjQuery.ready!=="undefined"?rootjQuery.ready(selector):selector(jQuery);}
+if(selector.selector!==undefined){this.selector=selector.selector;this.context=selector.context;}
+return jQuery.makeArray(selector,this);};init.prototype=jQuery.fn;rootjQuery=jQuery(document);var rparentsprev=/^(?:parents|prev(?:Until|All))/,guaranteedUnique={children:true,contents:true,next:true,prev:true};jQuery.extend({dir:function(elem,dir,until){var matched=[],cur=elem[dir];while(cur&&cur.nodeType!==9&&(until===undefined||cur.nodeType!==1||!jQuery(cur).is(until))){if(cur.nodeType===1){matched.push(cur);}
+cur=cur[dir];}
+return matched;},sibling:function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType===1&&n!==elem){r.push(n);}}
+return r;}});jQuery.fn.extend({has:function(target){var i,targets=jQuery(target,this),len=targets.length;return this.filter(function(){for(i=0;i-1:cur.nodeType===1&&jQuery.find.matchesSelector(cur,selectors))){matched.push(cur);break;}}}
+return this.pushStack(matched.length>1?jQuery.unique(matched):matched);},index:function(elem){if(!elem){return(this[0]&&this[0].parentNode)?this.first().prevAll().length:-1;}
+if(typeof elem==="string"){return jQuery.inArray(this[0],jQuery(elem));}
+return jQuery.inArray(elem.jquery?elem[0]:elem,this);},add:function(selector,context){return this.pushStack(jQuery.unique(jQuery.merge(this.get(),jQuery(selector,context))));},addBack:function(selector){return this.add(selector==null?this.prevObject:this.prevObject.filter(selector));}});function sibling(cur,dir){do{cur=cur[dir];}while(cur&&cur.nodeType!==1);return cur;}
+jQuery.each({parent:function(elem){var parent=elem.parentNode;return parent&&parent.nodeType!==11?parent:null;},parents:function(elem){return jQuery.dir(elem,"parentNode");},parentsUntil:function(elem,i,until){return jQuery.dir(elem,"parentNode",until);},next:function(elem){return sibling(elem,"nextSibling");},prev:function(elem){return sibling(elem,"previousSibling");},nextAll:function(elem){return jQuery.dir(elem,"nextSibling");},prevAll:function(elem){return jQuery.dir(elem,"previousSibling");},nextUntil:function(elem,i,until){return jQuery.dir(elem,"nextSibling",until);},prevUntil:function(elem,i,until){return jQuery.dir(elem,"previousSibling",until);},siblings:function(elem){return jQuery.sibling((elem.parentNode||{}).firstChild,elem);},children:function(elem){return jQuery.sibling(elem.firstChild);},contents:function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.merge([],elem.childNodes);}},function(name,fn){jQuery.fn[name]=function(until,selector){var ret=jQuery.map(this,fn,until);if(name.slice(-5)!=="Until"){selector=until;}
+if(selector&&typeof selector==="string"){ret=jQuery.filter(selector,ret);}
+if(this.length>1){if(!guaranteedUnique[name]){ret=jQuery.unique(ret);}
+if(rparentsprev.test(name)){ret=ret.reverse();}}
+return this.pushStack(ret);};});var rnotwhite=(/\S+/g);var optionsCache={};function createOptions(options){var object=optionsCache[options]={};jQuery.each(options.match(rnotwhite)||[],function(_,flag){object[flag]=true;});return object;}
+jQuery.Callbacks=function(options){options=typeof options==="string"?(optionsCache[options]||createOptions(options)):jQuery.extend({},options);var
+firing,memory,fired,firingLength,firingIndex,firingStart,list=[],stack=!options.once&&[],fire=function(data){memory=options.memory&&data;fired=true;firingIndex=firingStart||0;firingStart=0;firingLength=list.length;firing=true;for(;list&&firingIndex-1){list.splice(index,1);if(firing){if(index<=firingLength){firingLength--;}
+if(index<=firingIndex){firingIndex--;}}}});}
+return this;},has:function(fn){return fn?jQuery.inArray(fn,list)>-1:!!(list&&list.length);},empty:function(){list=[];firingLength=0;return this;},disable:function(){list=stack=memory=undefined;return this;},disabled:function(){return!list;},lock:function(){stack=undefined;if(!memory){self.disable();}
+return this;},locked:function(){return!stack;},fireWith:function(context,args){if(list&&(!fired||stack)){args=args||[];args=[context,args.slice?args.slice():args];if(firing){stack.push(args);}else{fire(args);}}
+return this;},fire:function(){self.fireWith(this,arguments);return this;},fired:function(){return!!fired;}};return self;};jQuery.extend({Deferred:function(func){var tuples=[["resolve","done",jQuery.Callbacks("once memory"),"resolved"],["reject","fail",jQuery.Callbacks("once memory"),"rejected"],["notify","progress",jQuery.Callbacks("memory")]],state="pending",promise={state:function(){return state;},always:function(){deferred.done(arguments).fail(arguments);return this;},then:function(){var fns=arguments;return jQuery.Deferred(function(newDefer){jQuery.each(tuples,function(i,tuple){var fn=jQuery.isFunction(fns[i])&&fns[i];deferred[tuple[1]](function(){var returned=fn&&fn.apply(this,arguments);if(returned&&jQuery.isFunction(returned.promise)){returned.promise().done(newDefer.resolve).fail(newDefer.reject).progress(newDefer.notify);}else{newDefer[tuple[0]+"With"](this===promise?newDefer.promise():this,fn?[returned]:arguments);}});});fns=null;}).promise();},promise:function(obj){return obj!=null?jQuery.extend(obj,promise):promise;}},deferred={};promise.pipe=promise.then;jQuery.each(tuples,function(i,tuple){var list=tuple[2],stateString=tuple[3];promise[tuple[1]]=list.add;if(stateString){list.add(function(){state=stateString;},tuples[i^1][2].disable,tuples[2][2].lock);}
+deferred[tuple[0]]=function(){deferred[tuple[0]+"With"](this===deferred?promise:this,arguments);return this;};deferred[tuple[0]+"With"]=list.fireWith;});promise.promise(deferred);if(func){func.call(deferred,deferred);}
+return deferred;},when:function(subordinate){var i=0,resolveValues=slice.call(arguments),length=resolveValues.length,remaining=length!==1||(subordinate&&jQuery.isFunction(subordinate.promise))?length:0,deferred=remaining===1?subordinate:jQuery.Deferred(),updateFunc=function(i,contexts,values){return function(value){contexts[i]=this;values[i]=arguments.length>1?slice.call(arguments):value;if(values===progressValues){deferred.notifyWith(contexts,values);}else if(!(--remaining)){deferred.resolveWith(contexts,values);}};},progressValues,progressContexts,resolveContexts;if(length>1){progressValues=new Array(length);progressContexts=new Array(length);resolveContexts=new Array(length);for(;i0){return;}
+readyList.resolveWith(document,[jQuery]);if(jQuery.fn.triggerHandler){jQuery(document).triggerHandler("ready");jQuery(document).off("ready");}}});function detach(){if(document.addEventListener){document.removeEventListener("DOMContentLoaded",completed,false);window.removeEventListener("load",completed,false);}else{document.detachEvent("onreadystatechange",completed);window.detachEvent("onload",completed);}}
+function completed(){if(document.addEventListener||event.type==="load"||document.readyState==="complete"){detach();jQuery.ready();}}
+jQuery.ready.promise=function(obj){if(!readyList){readyList=jQuery.Deferred();if(document.readyState==="complete"){setTimeout(jQuery.ready);}else if(document.addEventListener){document.addEventListener("DOMContentLoaded",completed,false);window.addEventListener("load",completed,false);}else{document.attachEvent("onreadystatechange",completed);window.attachEvent("onload",completed);var top=false;try{top=window.frameElement==null&&document.documentElement;}catch(e){}
+if(top&&top.doScroll){(function doScrollCheck(){if(!jQuery.isReady){try{top.doScroll("left");}catch(e){return setTimeout(doScrollCheck,50);}
+detach();jQuery.ready();}})();}}}
+return readyList.promise(obj);};var strundefined=typeof undefined;var i;for(i in jQuery(support)){break;}
+support.ownLast=i!=="0";support.inlineBlockNeedsLayout=false;jQuery(function(){var val,div,body,container;body=document.getElementsByTagName("body")[0];if(!body||!body.style){return;}
+div=document.createElement("div");container=document.createElement("div");container.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px";body.appendChild(container).appendChild(div);if(typeof div.style.zoom!==strundefined){div.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1";support.inlineBlockNeedsLayout=val=div.offsetWidth===3;if(val){body.style.zoom=1;}}
+body.removeChild(container);});(function(){var div=document.createElement("div");if(support.deleteExpando==null){support.deleteExpando=true;try{delete div.test;}catch(e){support.deleteExpando=false;}}
+div=null;})();jQuery.acceptData=function(elem){var noData=jQuery.noData[(elem.nodeName+" ").toLowerCase()],nodeType=+elem.nodeType||1;return nodeType!==1&&nodeType!==9?false:!noData||noData!==true&&elem.getAttribute("classid")===noData;};var rbrace=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,rmultiDash=/([A-Z])/g;function dataAttr(elem,key,data){if(data===undefined&&elem.nodeType===1){var name="data-"+key.replace(rmultiDash,"-$1").toLowerCase();data=elem.getAttribute(name);if(typeof data==="string"){try{data=data==="true"?true:data==="false"?false:data==="null"?null:+data+""===data?+data:rbrace.test(data)?jQuery.parseJSON(data):data;}catch(e){}
+jQuery.data(elem,key,data);}else{data=undefined;}}
+return data;}
+function isEmptyDataObject(obj){var name;for(name in obj){if(name==="data"&&jQuery.isEmptyObject(obj[name])){continue;}
+if(name!=="toJSON"){return false;}}
+return true;}
+function internalData(elem,name,data,pvt){if(!jQuery.acceptData(elem)){return;}
+var ret,thisCache,internalKey=jQuery.expando,isNode=elem.nodeType,cache=isNode?jQuery.cache:elem,id=isNode?elem[internalKey]:elem[internalKey]&&internalKey;if((!id||!cache[id]||(!pvt&&!cache[id].data))&&data===undefined&&typeof name==="string"){return;}
+if(!id){if(isNode){id=elem[internalKey]=deletedIds.pop()||jQuery.guid++;}else{id=internalKey;}}
+if(!cache[id]){cache[id]=isNode?{}:{toJSON:jQuery.noop};}
+if(typeof name==="object"||typeof name==="function"){if(pvt){cache[id]=jQuery.extend(cache[id],name);}else{cache[id].data=jQuery.extend(cache[id].data,name);}}
+thisCache=cache[id];if(!pvt){if(!thisCache.data){thisCache.data={};}
+thisCache=thisCache.data;}
+if(data!==undefined){thisCache[jQuery.camelCase(name)]=data;}
+if(typeof name==="string"){ret=thisCache[name];if(ret==null){ret=thisCache[jQuery.camelCase(name)];}}else{ret=thisCache;}
+return ret;}
+function internalRemoveData(elem,name,pvt){if(!jQuery.acceptData(elem)){return;}
+var thisCache,i,isNode=elem.nodeType,cache=isNode?jQuery.cache:elem,id=isNode?elem[jQuery.expando]:jQuery.expando;if(!cache[id]){return;}
+if(name){thisCache=pvt?cache[id]:cache[id].data;if(thisCache){if(!jQuery.isArray(name)){if(name in thisCache){name=[name];}else{name=jQuery.camelCase(name);if(name in thisCache){name=[name];}else{name=name.split(" ");}}}else{name=name.concat(jQuery.map(name,jQuery.camelCase));}
+i=name.length;while(i--){delete thisCache[name[i]];}
+if(pvt?!isEmptyDataObject(thisCache):!jQuery.isEmptyObject(thisCache)){return;}}}
+if(!pvt){delete cache[id].data;if(!isEmptyDataObject(cache[id])){return;}}
+if(isNode){jQuery.cleanData([elem],true);}else if(support.deleteExpando||cache!=cache.window){delete cache[id];}else{cache[id]=null;}}
+jQuery.extend({cache:{},noData:{"applet ":true,"embed ":true,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(elem){elem=elem.nodeType?jQuery.cache[elem[jQuery.expando]]:elem[jQuery.expando];return!!elem&&!isEmptyDataObject(elem);},data:function(elem,name,data){return internalData(elem,name,data);},removeData:function(elem,name){return internalRemoveData(elem,name);},_data:function(elem,name,data){return internalData(elem,name,data,true);},_removeData:function(elem,name){return internalRemoveData(elem,name,true);}});jQuery.fn.extend({data:function(key,value){var i,name,data,elem=this[0],attrs=elem&&elem.attributes;if(key===undefined){if(this.length){data=jQuery.data(elem);if(elem.nodeType===1&&!jQuery._data(elem,"parsedAttrs")){i=attrs.length;while(i--){if(attrs[i]){name=attrs[i].name;if(name.indexOf("data-")===0){name=jQuery.camelCase(name.slice(5));dataAttr(elem,name,data[name]);}}}
+jQuery._data(elem,"parsedAttrs",true);}}
+return data;}
+if(typeof key==="object"){return this.each(function(){jQuery.data(this,key);});}
+return arguments.length>1?this.each(function(){jQuery.data(this,key,value);}):elem?dataAttr(elem,key,jQuery.data(elem,key)):undefined;},removeData:function(key){return this.each(function(){jQuery.removeData(this,key);});}});jQuery.extend({queue:function(elem,type,data){var queue;if(elem){type=(type||"fx")+"queue";queue=jQuery._data(elem,type);if(data){if(!queue||jQuery.isArray(data)){queue=jQuery._data(elem,type,jQuery.makeArray(data));}else{queue.push(data);}}
+return queue||[];}},dequeue:function(elem,type){type=type||"fx";var queue=jQuery.queue(elem,type),startLength=queue.length,fn=queue.shift(),hooks=jQuery._queueHooks(elem,type),next=function(){jQuery.dequeue(elem,type);};if(fn==="inprogress"){fn=queue.shift();startLength--;}
+if(fn){if(type==="fx"){queue.unshift("inprogress");}
+delete hooks.stop;fn.call(elem,next,hooks);}
+if(!startLength&&hooks){hooks.empty.fire();}},_queueHooks:function(elem,type){var key=type+"queueHooks";return jQuery._data(elem,key)||jQuery._data(elem,key,{empty:jQuery.Callbacks("once memory").add(function(){jQuery._removeData(elem,type+"queue");jQuery._removeData(elem,key);})});}});jQuery.fn.extend({queue:function(type,data){var setter=2;if(typeof type!=="string"){data=type;type="fx";setter--;}
+if(arguments.lengtha ";support.leadingWhitespace=div.firstChild.nodeType===3;support.tbody=!div.getElementsByTagName("tbody").length;support.htmlSerialize=!!div.getElementsByTagName("link").length;support.html5Clone=document.createElement("nav").cloneNode(true).outerHTML!=="<:nav>";input.type="checkbox";input.checked=true;fragment.appendChild(input);support.appendChecked=input.checked;div.innerHTML="x ";support.noCloneChecked=!!div.cloneNode(true).lastChild.defaultValue;fragment.appendChild(div);div.innerHTML=" ";support.checkClone=div.cloneNode(true).cloneNode(true).lastChild.checked;support.noCloneEvent=true;if(div.attachEvent){div.attachEvent("onclick",function(){support.noCloneEvent=false;});div.cloneNode(true).click();}
+if(support.deleteExpando==null){support.deleteExpando=true;try{delete div.test;}catch(e){support.deleteExpando=false;}}})();(function(){var i,eventName,div=document.createElement("div");for(i in{submit:true,change:true,focusin:true}){eventName="on"+i;if(!(support[i+"Bubbles"]=eventName in window)){div.setAttribute(eventName,"t");support[i+"Bubbles"]=div.attributes[eventName].expando===false;}}
+div=null;})();var rformElems=/^(?:input|select|textarea)$/i,rkeyEvent=/^key/,rmouseEvent=/^(?:mouse|pointer|contextmenu)|click/,rfocusMorph=/^(?:focusinfocus|focusoutblur)$/,rtypenamespace=/^([^.]*)(?:\.(.+)|)$/;function returnTrue(){return true;}
+function returnFalse(){return false;}
+function safeActiveElement(){try{return document.activeElement;}catch(err){}}
+jQuery.event={global:{},add:function(elem,types,handler,data,selector){var tmp,events,t,handleObjIn,special,eventHandle,handleObj,handlers,type,namespaces,origType,elemData=jQuery._data(elem);if(!elemData){return;}
+if(handler.handler){handleObjIn=handler;handler=handleObjIn.handler;selector=handleObjIn.selector;}
+if(!handler.guid){handler.guid=jQuery.guid++;}
+if(!(events=elemData.events)){events=elemData.events={};}
+if(!(eventHandle=elemData.handle)){eventHandle=elemData.handle=function(e){return typeof jQuery!==strundefined&&(!e||jQuery.event.triggered!==e.type)?jQuery.event.dispatch.apply(eventHandle.elem,arguments):undefined;};eventHandle.elem=elem;}
+types=(types||"").match(rnotwhite)||[""];t=types.length;while(t--){tmp=rtypenamespace.exec(types[t])||[];type=origType=tmp[1];namespaces=(tmp[2]||"").split(".").sort();if(!type){continue;}
+special=jQuery.event.special[type]||{};type=(selector?special.delegateType:special.bindType)||type;special=jQuery.event.special[type]||{};handleObj=jQuery.extend({type:type,origType:origType,data:data,handler:handler,guid:handler.guid,selector:selector,needsContext:selector&&jQuery.expr.match.needsContext.test(selector),namespace:namespaces.join(".")},handleObjIn);if(!(handlers=events[type])){handlers=events[type]=[];handlers.delegateCount=0;if(!special.setup||special.setup.call(elem,data,namespaces,eventHandle)===false){if(elem.addEventListener){elem.addEventListener(type,eventHandle,false);}else if(elem.attachEvent){elem.attachEvent("on"+type,eventHandle);}}}
+if(special.add){special.add.call(elem,handleObj);if(!handleObj.handler.guid){handleObj.handler.guid=handler.guid;}}
+if(selector){handlers.splice(handlers.delegateCount++,0,handleObj);}else{handlers.push(handleObj);}
+jQuery.event.global[type]=true;}
+elem=null;},remove:function(elem,types,handler,selector,mappedTypes){var j,handleObj,tmp,origCount,t,events,special,handlers,type,namespaces,origType,elemData=jQuery.hasData(elem)&&jQuery._data(elem);if(!elemData||!(events=elemData.events)){return;}
+types=(types||"").match(rnotwhite)||[""];t=types.length;while(t--){tmp=rtypenamespace.exec(types[t])||[];type=origType=tmp[1];namespaces=(tmp[2]||"").split(".").sort();if(!type){for(type in events){jQuery.event.remove(elem,type+types[t],handler,selector,true);}
+continue;}
+special=jQuery.event.special[type]||{};type=(selector?special.delegateType:special.bindType)||type;handlers=events[type]||[];tmp=tmp[2]&&new RegExp("(^|\\.)"+namespaces.join("\\.(?:.*\\.|)")+"(\\.|$)");origCount=j=handlers.length;while(j--){handleObj=handlers[j];if((mappedTypes||origType===handleObj.origType)&&(!handler||handler.guid===handleObj.guid)&&(!tmp||tmp.test(handleObj.namespace))&&(!selector||selector===handleObj.selector||selector==="**"&&handleObj.selector)){handlers.splice(j,1);if(handleObj.selector){handlers.delegateCount--;}
+if(special.remove){special.remove.call(elem,handleObj);}}}
+if(origCount&&!handlers.length){if(!special.teardown||special.teardown.call(elem,namespaces,elemData.handle)===false){jQuery.removeEvent(elem,type,elemData.handle);}
+delete events[type];}}
+if(jQuery.isEmptyObject(events)){delete elemData.handle;jQuery._removeData(elem,"events");}},trigger:function(event,data,elem,onlyHandlers){var handle,ontype,cur,bubbleType,special,tmp,i,eventPath=[elem||document],type=hasOwn.call(event,"type")?event.type:event,namespaces=hasOwn.call(event,"namespace")?event.namespace.split("."):[];cur=tmp=elem=elem||document;if(elem.nodeType===3||elem.nodeType===8){return;}
+if(rfocusMorph.test(type+jQuery.event.triggered)){return;}
+if(type.indexOf(".")>=0){namespaces=type.split(".");type=namespaces.shift();namespaces.sort();}
+ontype=type.indexOf(":")<0&&"on"+type;event=event[jQuery.expando]?event:new jQuery.Event(type,typeof event==="object"&&event);event.isTrigger=onlyHandlers?2:3;event.namespace=namespaces.join(".");event.namespace_re=event.namespace?new RegExp("(^|\\.)"+namespaces.join("\\.(?:.*\\.|)")+"(\\.|$)"):null;event.result=undefined;if(!event.target){event.target=elem;}
+data=data==null?[event]:jQuery.makeArray(data,[event]);special=jQuery.event.special[type]||{};if(!onlyHandlers&&special.trigger&&special.trigger.apply(elem,data)===false){return;}
+if(!onlyHandlers&&!special.noBubble&&!jQuery.isWindow(elem)){bubbleType=special.delegateType||type;if(!rfocusMorph.test(bubbleType+type)){cur=cur.parentNode;}
+for(;cur;cur=cur.parentNode){eventPath.push(cur);tmp=cur;}
+if(tmp===(elem.ownerDocument||document)){eventPath.push(tmp.defaultView||tmp.parentWindow||window);}}
+i=0;while((cur=eventPath[i++])&&!event.isPropagationStopped()){event.type=i>1?bubbleType:special.bindType||type;handle=(jQuery._data(cur,"events")||{})[event.type]&&jQuery._data(cur,"handle");if(handle){handle.apply(cur,data);}
+handle=ontype&&cur[ontype];if(handle&&handle.apply&&jQuery.acceptData(cur)){event.result=handle.apply(cur,data);if(event.result===false){event.preventDefault();}}}
+event.type=type;if(!onlyHandlers&&!event.isDefaultPrevented()){if((!special._default||special._default.apply(eventPath.pop(),data)===false)&&jQuery.acceptData(elem)){if(ontype&&elem[type]&&!jQuery.isWindow(elem)){tmp=elem[ontype];if(tmp){elem[ontype]=null;}
+jQuery.event.triggered=type;try{elem[type]();}catch(e){}
+jQuery.event.triggered=undefined;if(tmp){elem[ontype]=tmp;}}}}
+return event.result;},dispatch:function(event){event=jQuery.event.fix(event);var i,ret,handleObj,matched,j,handlerQueue=[],args=slice.call(arguments),handlers=(jQuery._data(this,"events")||{})[event.type]||[],special=jQuery.event.special[event.type]||{};args[0]=event;event.delegateTarget=this;if(special.preDispatch&&special.preDispatch.call(this,event)===false){return;}
+handlerQueue=jQuery.event.handlers.call(this,event,handlers);i=0;while((matched=handlerQueue[i++])&&!event.isPropagationStopped()){event.currentTarget=matched.elem;j=0;while((handleObj=matched.handlers[j++])&&!event.isImmediatePropagationStopped()){if(!event.namespace_re||event.namespace_re.test(handleObj.namespace)){event.handleObj=handleObj;event.data=handleObj.data;ret=((jQuery.event.special[handleObj.origType]||{}).handle||handleObj.handler).apply(matched.elem,args);if(ret!==undefined){if((event.result=ret)===false){event.preventDefault();event.stopPropagation();}}}}}
+if(special.postDispatch){special.postDispatch.call(this,event);}
+return event.result;},handlers:function(event,handlers){var sel,handleObj,matches,i,handlerQueue=[],delegateCount=handlers.delegateCount,cur=event.target;if(delegateCount&&cur.nodeType&&(!event.button||event.type!=="click")){for(;cur!=this;cur=cur.parentNode||this){if(cur.nodeType===1&&(cur.disabled!==true||event.type!=="click")){matches=[];for(i=0;i=0:jQuery.find(sel,this,null,[cur]).length;}
+if(matches[sel]){matches.push(handleObj);}}
+if(matches.length){handlerQueue.push({elem:cur,handlers:matches});}}}}
+if(delegateCount ]","i"),rleadingWhitespace=/^\s+/,rxhtmlTag=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,rtagName=/<([\w:]+)/,rtbody=/\s*$/g,wrapMap={option:[1,""," "],legend:[1,""," "],area:[1,""," "],param:[1,""," "],thead:[1,""],tr:[2,""],col:[2,""],td:[3,""],_default:support.htmlSerialize?[0,"",""]:[1,"X","
"]},safeFragment=createSafeFragment(document),fragmentDiv=safeFragment.appendChild(document.createElement("div"));wrapMap.optgroup=wrapMap.option;wrapMap.tbody=wrapMap.tfoot=wrapMap.colgroup=wrapMap.caption=wrapMap.thead;wrapMap.th=wrapMap.td;function getAll(context,tag){var elems,elem,i=0,found=typeof context.getElementsByTagName!==strundefined?context.getElementsByTagName(tag||"*"):typeof context.querySelectorAll!==strundefined?context.querySelectorAll(tag||"*"):undefined;if(!found){for(found=[],elems=context.childNodes||context;(elem=elems[i])!=null;i++){if(!tag||jQuery.nodeName(elem,tag)){found.push(elem);}else{jQuery.merge(found,getAll(elem,tag));}}}
+return tag===undefined||tag&&jQuery.nodeName(context,tag)?jQuery.merge([context],found):found;}
+function fixDefaultChecked(elem){if(rcheckableType.test(elem.type)){elem.defaultChecked=elem.checked;}}
+function manipulationTarget(elem,content){return jQuery.nodeName(elem,"table")&&jQuery.nodeName(content.nodeType!==11?content:content.firstChild,"tr")?elem.getElementsByTagName("tbody")[0]||elem.appendChild(elem.ownerDocument.createElement("tbody")):elem;}
+function disableScript(elem){elem.type=(jQuery.find.attr(elem,"type")!==null)+"/"+elem.type;return elem;}
+function restoreScript(elem){var match=rscriptTypeMasked.exec(elem.type);if(match){elem.type=match[1];}else{elem.removeAttribute("type");}
+return elem;}
+function setGlobalEval(elems,refElements){var elem,i=0;for(;(elem=elems[i])!=null;i++){jQuery._data(elem,"globalEval",!refElements||jQuery._data(refElements[i],"globalEval"));}}
+function cloneCopyEvent(src,dest){if(dest.nodeType!==1||!jQuery.hasData(src)){return;}
+var type,i,l,oldData=jQuery._data(src),curData=jQuery._data(dest,oldData),events=oldData.events;if(events){delete curData.handle;curData.events={};for(type in events){for(i=0,l=events[type].length;i")){clone=elem.cloneNode(true);}else{fragmentDiv.innerHTML=elem.outerHTML;fragmentDiv.removeChild(clone=fragmentDiv.firstChild);}
+if((!support.noCloneEvent||!support.noCloneChecked)&&(elem.nodeType===1||elem.nodeType===11)&&!jQuery.isXMLDoc(elem)){destElements=getAll(clone);srcElements=getAll(elem);for(i=0;(node=srcElements[i])!=null;++i){if(destElements[i]){fixCloneNodeIssues(node,destElements[i]);}}}
+if(dataAndEvents){if(deepDataAndEvents){srcElements=srcElements||getAll(elem);destElements=destElements||getAll(clone);for(i=0;(node=srcElements[i])!=null;i++){cloneCopyEvent(node,destElements[i]);}}else{cloneCopyEvent(elem,clone);}}
+destElements=getAll(clone,"script");if(destElements.length>0){setGlobalEval(destElements,!inPage&&getAll(elem,"script"));}
+destElements=srcElements=node=null;return clone;},buildFragment:function(elems,context,scripts,selection){var j,elem,contains,tmp,tag,tbody,wrap,l=elems.length,safe=createSafeFragment(context),nodes=[],i=0;for(;i$2>")+wrap[2];j=wrap[0];while(j--){tmp=tmp.lastChild;}
+if(!support.leadingWhitespace&&rleadingWhitespace.test(elem)){nodes.push(context.createTextNode(rleadingWhitespace.exec(elem)[0]));}
+if(!support.tbody){elem=tag==="table"&&!rtbody.test(elem)?tmp.firstChild:wrap[1]===""&&!rtbody.test(elem)?tmp:0;j=elem&&elem.childNodes.length;while(j--){if(jQuery.nodeName((tbody=elem.childNodes[j]),"tbody")&&!tbody.childNodes.length){elem.removeChild(tbody);}}}
+jQuery.merge(nodes,tmp.childNodes);tmp.textContent="";while(tmp.firstChild){tmp.removeChild(tmp.firstChild);}
+tmp=safe.lastChild;}}}
+if(tmp){safe.removeChild(tmp);}
+if(!support.appendChecked){jQuery.grep(getAll(nodes,"input"),fixDefaultChecked);}
+i=0;while((elem=nodes[i++])){if(selection&&jQuery.inArray(elem,selection)!==-1){continue;}
+contains=jQuery.contains(elem.ownerDocument,elem);tmp=getAll(safe.appendChild(elem),"script");if(contains){setGlobalEval(tmp);}
+if(scripts){j=0;while((elem=tmp[j++])){if(rscriptType.test(elem.type||"")){scripts.push(elem);}}}}
+tmp=null;return safe;},cleanData:function(elems,acceptData){var elem,type,id,data,i=0,internalKey=jQuery.expando,cache=jQuery.cache,deleteExpando=support.deleteExpando,special=jQuery.event.special;for(;(elem=elems[i])!=null;i++){if(acceptData||jQuery.acceptData(elem)){id=elem[internalKey];data=id&&cache[id];if(data){if(data.events){for(type in data.events){if(special[type]){jQuery.event.remove(elem,type);}else{jQuery.removeEvent(elem,type,data.handle);}}}
+if(cache[id]){delete cache[id];if(deleteExpando){delete elem[internalKey];}else if(typeof elem.removeAttribute!==strundefined){elem.removeAttribute(internalKey);}else{elem[internalKey]=null;}
+deletedIds.push(id);}}}}}});jQuery.fn.extend({text:function(value){return access(this,function(value){return value===undefined?jQuery.text(this):this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(value));},null,value,arguments.length);},append:function(){return this.domManip(arguments,function(elem){if(this.nodeType===1||this.nodeType===11||this.nodeType===9){var target=manipulationTarget(this,elem);target.appendChild(elem);}});},prepend:function(){return this.domManip(arguments,function(elem){if(this.nodeType===1||this.nodeType===11||this.nodeType===9){var target=manipulationTarget(this,elem);target.insertBefore(elem,target.firstChild);}});},before:function(){return this.domManip(arguments,function(elem){if(this.parentNode){this.parentNode.insertBefore(elem,this);}});},after:function(){return this.domManip(arguments,function(elem){if(this.parentNode){this.parentNode.insertBefore(elem,this.nextSibling);}});},remove:function(selector,keepData){var elem,elems=selector?jQuery.filter(selector,this):this,i=0;for(;(elem=elems[i])!=null;i++){if(!keepData&&elem.nodeType===1){jQuery.cleanData(getAll(elem));}
+if(elem.parentNode){if(keepData&&jQuery.contains(elem.ownerDocument,elem)){setGlobalEval(getAll(elem,"script"));}
+elem.parentNode.removeChild(elem);}}
+return this;},empty:function(){var elem,i=0;for(;(elem=this[i])!=null;i++){if(elem.nodeType===1){jQuery.cleanData(getAll(elem,false));}
+while(elem.firstChild){elem.removeChild(elem.firstChild);}
+if(elem.options&&jQuery.nodeName(elem,"select")){elem.options.length=0;}}
+return this;},clone:function(dataAndEvents,deepDataAndEvents){dataAndEvents=dataAndEvents==null?false:dataAndEvents;deepDataAndEvents=deepDataAndEvents==null?dataAndEvents:deepDataAndEvents;return this.map(function(){return jQuery.clone(this,dataAndEvents,deepDataAndEvents);});},html:function(value){return access(this,function(value){var elem=this[0]||{},i=0,l=this.length;if(value===undefined){return elem.nodeType===1?elem.innerHTML.replace(rinlinejQuery,""):undefined;}
+if(typeof value==="string"&&!rnoInnerhtml.test(value)&&(support.htmlSerialize||!rnoshimcache.test(value))&&(support.leadingWhitespace||!rleadingWhitespace.test(value))&&!wrapMap[(rtagName.exec(value)||["",""])[1].toLowerCase()]){value=value.replace(rxhtmlTag,"<$1>$2>");try{for(;i1&&typeof value==="string"&&!support.checkClone&&rchecked.test(value))){return this.each(function(index){var self=set.eq(index);if(isFunction){args[0]=value.call(this,index,self.html());}
+self.domManip(args,callback);});}
+if(l){fragment=jQuery.buildFragment(args,this[0].ownerDocument,false,this);first=fragment.firstChild;if(fragment.childNodes.length===1){fragment=first;}
+if(first){scripts=jQuery.map(getAll(fragment,"script"),disableScript);hasScripts=scripts.length;for(;i ")).appendTo(doc.documentElement);doc=(iframe[0].contentWindow||iframe[0].contentDocument).document;doc.write();doc.close();display=actualDisplay(nodeName,doc);iframe.detach();}
+elemdisplay[nodeName]=display;}
+return display;}
+(function(){var shrinkWrapBlocksVal;support.shrinkWrapBlocks=function(){if(shrinkWrapBlocksVal!=null){return shrinkWrapBlocksVal;}
+shrinkWrapBlocksVal=false;var div,body,container;body=document.getElementsByTagName("body")[0];if(!body||!body.style){return;}
+div=document.createElement("div");container=document.createElement("div");container.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px";body.appendChild(container).appendChild(div);if(typeof div.style.zoom!==strundefined){div.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;"+"box-sizing:content-box;display:block;margin:0;border:0;"+"padding:1px;width:1px;zoom:1";div.appendChild(document.createElement("div")).style.width="5px";shrinkWrapBlocksVal=div.offsetWidth!==3;}
+body.removeChild(container);return shrinkWrapBlocksVal;};})();var rmargin=(/^margin/);var rnumnonpx=new RegExp("^("+pnum+")(?!px)[a-z%]+$","i");var getStyles,curCSS,rposition=/^(top|right|bottom|left)$/;if(window.getComputedStyle){getStyles=function(elem){return elem.ownerDocument.defaultView.getComputedStyle(elem,null);};curCSS=function(elem,name,computed){var width,minWidth,maxWidth,ret,style=elem.style;computed=computed||getStyles(elem);ret=computed?computed.getPropertyValue(name)||computed[name]:undefined;if(computed){if(ret===""&&!jQuery.contains(elem.ownerDocument,elem)){ret=jQuery.style(elem,name);}
+if(rnumnonpx.test(ret)&&rmargin.test(name)){width=style.width;minWidth=style.minWidth;maxWidth=style.maxWidth;style.minWidth=style.maxWidth=style.width=ret;ret=computed.width;style.width=width;style.minWidth=minWidth;style.maxWidth=maxWidth;}}
+return ret===undefined?ret:ret+"";};}else if(document.documentElement.currentStyle){getStyles=function(elem){return elem.currentStyle;};curCSS=function(elem,name,computed){var left,rs,rsLeft,ret,style=elem.style;computed=computed||getStyles(elem);ret=computed?computed[name]:undefined;if(ret==null&&style&&style[name]){ret=style[name];}
+if(rnumnonpx.test(ret)&&!rposition.test(name)){left=style.left;rs=elem.runtimeStyle;rsLeft=rs&&rs.left;if(rsLeft){rs.left=elem.currentStyle.left;}
+style.left=name==="fontSize"?"1em":ret;ret=style.pixelLeft+"px";style.left=left;if(rsLeft){rs.left=rsLeft;}}
+return ret===undefined?ret:ret+""||"auto";};}
+function addGetHookIf(conditionFn,hookFn){return{get:function(){var condition=conditionFn();if(condition==null){return;}
+if(condition){delete this.get;return;}
+return(this.get=hookFn).apply(this,arguments);}};}
+(function(){var div,style,a,pixelPositionVal,boxSizingReliableVal,reliableHiddenOffsetsVal,reliableMarginRightVal;div=document.createElement("div");div.innerHTML=" a ";a=div.getElementsByTagName("a")[0];style=a&&a.style;if(!style){return;}
+style.cssText="float:left;opacity:.5";support.opacity=style.opacity==="0.5";support.cssFloat=!!style.cssFloat;div.style.backgroundClip="content-box";div.cloneNode(true).style.backgroundClip="";support.clearCloneStyle=div.style.backgroundClip==="content-box";support.boxSizing=style.boxSizing===""||style.MozBoxSizing===""||style.WebkitBoxSizing==="";jQuery.extend(support,{reliableHiddenOffsets:function(){if(reliableHiddenOffsetsVal==null){computeStyleTests();}
+return reliableHiddenOffsetsVal;},boxSizingReliable:function(){if(boxSizingReliableVal==null){computeStyleTests();}
+return boxSizingReliableVal;},pixelPosition:function(){if(pixelPositionVal==null){computeStyleTests();}
+return pixelPositionVal;},reliableMarginRight:function(){if(reliableMarginRightVal==null){computeStyleTests();}
+return reliableMarginRightVal;}});function computeStyleTests(){var div,body,container,contents;body=document.getElementsByTagName("body")[0];if(!body||!body.style){return;}
+div=document.createElement("div");container=document.createElement("div");container.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px";body.appendChild(container).appendChild(div);div.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;"+"box-sizing:border-box;display:block;margin-top:1%;top:1%;"+"border:1px;padding:1px;width:4px;position:absolute";pixelPositionVal=boxSizingReliableVal=false;reliableMarginRightVal=true;if(window.getComputedStyle){pixelPositionVal=(window.getComputedStyle(div,null)||{}).top!=="1%";boxSizingReliableVal=(window.getComputedStyle(div,null)||{width:"4px"}).width==="4px";contents=div.appendChild(document.createElement("div"));contents.style.cssText=div.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;"+"box-sizing:content-box;display:block;margin:0;border:0;padding:0";contents.style.marginRight=contents.style.width="0";div.style.width="1px";reliableMarginRightVal=!parseFloat((window.getComputedStyle(contents,null)||{}).marginRight);}
+div.innerHTML="";contents=div.getElementsByTagName("td");contents[0].style.cssText="margin:0;border:0;padding:0;display:none";reliableHiddenOffsetsVal=contents[0].offsetHeight===0;if(reliableHiddenOffsetsVal){contents[0].style.display="";contents[1].style.display="none";reliableHiddenOffsetsVal=contents[0].offsetHeight===0;}
+body.removeChild(container);}})();jQuery.swap=function(elem,options,callback,args){var ret,name,old={};for(name in options){old[name]=elem.style[name];elem.style[name]=options[name];}
+ret=callback.apply(elem,args||[]);for(name in options){elem.style[name]=old[name];}
+return ret;};var
+ralpha=/alpha\([^)]*\)/i,ropacity=/opacity\s*=\s*([^)]*)/,rdisplayswap=/^(none|table(?!-c[ea]).+)/,rnumsplit=new RegExp("^("+pnum+")(.*)$","i"),rrelNum=new RegExp("^([+-])=("+pnum+")","i"),cssShow={position:"absolute",visibility:"hidden",display:"block"},cssNormalTransform={letterSpacing:"0",fontWeight:"400"},cssPrefixes=["Webkit","O","Moz","ms"];function vendorPropName(style,name){if(name in style){return name;}
+var capName=name.charAt(0).toUpperCase()+name.slice(1),origName=name,i=cssPrefixes.length;while(i--){name=cssPrefixes[i]+capName;if(name in style){return name;}}
+return origName;}
+function showHide(elements,show){var display,elem,hidden,values=[],index=0,length=elements.length;for(;index=1||value==="")&&jQuery.trim(filter.replace(ralpha,""))===""&&style.removeAttribute){style.removeAttribute("filter");if(value===""||currentStyle&&!currentStyle.filter){return;}}
+style.filter=ralpha.test(filter)?filter.replace(ralpha,opacity):filter+" "+opacity;}};}
+jQuery.cssHooks.marginRight=addGetHookIf(support.reliableMarginRight,function(elem,computed){if(computed){return jQuery.swap(elem,{"display":"inline-block"},curCSS,[elem,"marginRight"]);}});jQuery.each({margin:"",padding:"",border:"Width"},function(prefix,suffix){jQuery.cssHooks[prefix+suffix]={expand:function(value){var i=0,expanded={},parts=typeof value==="string"?value.split(" "):[value];for(;i<4;i++){expanded[prefix+cssExpand[i]+suffix]=parts[i]||parts[i-2]||parts[0];}
+return expanded;}};if(!rmargin.test(prefix)){jQuery.cssHooks[prefix+suffix].set=setPositiveNumber;}});jQuery.fn.extend({css:function(name,value){return access(this,function(elem,name,value){var styles,len,map={},i=0;if(jQuery.isArray(name)){styles=getStyles(elem);len=name.length;for(;i1);},show:function(){return showHide(this,true);},hide:function(){return showHide(this);},toggle:function(state){if(typeof state==="boolean"){return state?this.show():this.hide();}
+return this.each(function(){if(isHidden(this)){jQuery(this).show();}else{jQuery(this).hide();}});}});function Tween(elem,options,prop,end,easing){return new Tween.prototype.init(elem,options,prop,end,easing);}
+jQuery.Tween=Tween;Tween.prototype={constructor:Tween,init:function(elem,options,prop,end,easing,unit){this.elem=elem;this.prop=prop;this.easing=easing||"swing";this.options=options;this.start=this.now=this.cur();this.end=end;this.unit=unit||(jQuery.cssNumber[prop]?"":"px");},cur:function(){var hooks=Tween.propHooks[this.prop];return hooks&&hooks.get?hooks.get(this):Tween.propHooks._default.get(this);},run:function(percent){var eased,hooks=Tween.propHooks[this.prop];if(this.options.duration){this.pos=eased=jQuery.easing[this.easing](percent,this.options.duration*percent,0,1,this.options.duration);}else{this.pos=eased=percent;}
+this.now=(this.end-this.start)*eased+this.start;if(this.options.step){this.options.step.call(this.elem,this.now,this);}
+if(hooks&&hooks.set){hooks.set(this);}else{Tween.propHooks._default.set(this);}
+return this;}};Tween.prototype.init.prototype=Tween.prototype;Tween.propHooks={_default:{get:function(tween){var result;if(tween.elem[tween.prop]!=null&&(!tween.elem.style||tween.elem.style[tween.prop]==null)){return tween.elem[tween.prop];}
+result=jQuery.css(tween.elem,tween.prop,"");return!result||result==="auto"?0:result;},set:function(tween){if(jQuery.fx.step[tween.prop]){jQuery.fx.step[tween.prop](tween);}else if(tween.elem.style&&(tween.elem.style[jQuery.cssProps[tween.prop]]!=null||jQuery.cssHooks[tween.prop])){jQuery.style(tween.elem,tween.prop,tween.now+tween.unit);}else{tween.elem[tween.prop]=tween.now;}}}};Tween.propHooks.scrollTop=Tween.propHooks.scrollLeft={set:function(tween){if(tween.elem.nodeType&&tween.elem.parentNode){tween.elem[tween.prop]=tween.now;}}};jQuery.easing={linear:function(p){return p;},swing:function(p){return 0.5-Math.cos(p*Math.PI)/2;}};jQuery.fx=Tween.prototype.init;jQuery.fx.step={};var
+fxNow,timerId,rfxtypes=/^(?:toggle|show|hide)$/,rfxnum=new RegExp("^(?:([+-])=|)("+pnum+")([a-z%]*)$","i"),rrun=/queueHooks$/,animationPrefilters=[defaultPrefilter],tweeners={"*":[function(prop,value){var tween=this.createTween(prop,value),target=tween.cur(),parts=rfxnum.exec(value),unit=parts&&parts[3]||(jQuery.cssNumber[prop]?"":"px"),start=(jQuery.cssNumber[prop]||unit!=="px"&&+target)&&rfxnum.exec(jQuery.css(tween.elem,prop)),scale=1,maxIterations=20;if(start&&start[3]!==unit){unit=unit||start[3];parts=parts||[];start=+target||1;do{scale=scale||".5";start=start/scale;jQuery.style(tween.elem,prop,start+unit);}while(scale!==(scale=tween.cur()/target)&&scale!==1&&--maxIterations);}
+if(parts){start=tween.start=+start||+target||0;tween.unit=unit;tween.end=parts[1]?start+(parts[1]+1)*parts[2]:+parts[2];}
+return tween;}]};function createFxNow(){setTimeout(function(){fxNow=undefined;});return(fxNow=jQuery.now());}
+function genFx(type,includeWidth){var which,attrs={height:type},i=0;includeWidth=includeWidth?1:0;for(;i<4;i+=2-includeWidth){which=cssExpand[i];attrs["margin"+which]=attrs["padding"+which]=type;}
+if(includeWidth){attrs.opacity=attrs.width=type;}
+return attrs;}
+function createTween(value,prop,animation){var tween,collection=(tweeners[prop]||[]).concat(tweeners["*"]),index=0,length=collection.length;for(;indexa ";a=div.getElementsByTagName("a")[0];select=document.createElement("select");opt=select.appendChild(document.createElement("option"));input=div.getElementsByTagName("input")[0];a.style.cssText="top:1px";support.getSetAttribute=div.className!=="t";support.style=/top/.test(a.getAttribute("style"));support.hrefNormalized=a.getAttribute("href")==="/a";support.checkOn=!!input.value;support.optSelected=opt.selected;support.enctype=!!document.createElement("form").enctype;select.disabled=true;support.optDisabled=!opt.disabled;input=document.createElement("input");input.setAttribute("value","");support.input=input.getAttribute("value")==="";input.value="t";input.setAttribute("type","radio");support.radioValue=input.value==="t";})();var rreturn=/\r/g;jQuery.fn.extend({val:function(value){var hooks,ret,isFunction,elem=this[0];if(!arguments.length){if(elem){hooks=jQuery.valHooks[elem.type]||jQuery.valHooks[elem.nodeName.toLowerCase()];if(hooks&&"get"in hooks&&(ret=hooks.get(elem,"value"))!==undefined){return ret;}
+ret=elem.value;return typeof ret==="string"?ret.replace(rreturn,""):ret==null?"":ret;}
+return;}
+isFunction=jQuery.isFunction(value);return this.each(function(i){var val;if(this.nodeType!==1){return;}
+if(isFunction){val=value.call(this,i,jQuery(this).val());}else{val=value;}
+if(val==null){val="";}else if(typeof val==="number"){val+="";}else if(jQuery.isArray(val)){val=jQuery.map(val,function(value){return value==null?"":value+"";});}
+hooks=jQuery.valHooks[this.type]||jQuery.valHooks[this.nodeName.toLowerCase()];if(!hooks||!("set"in hooks)||hooks.set(this,val,"value")===undefined){this.value=val;}});}});jQuery.extend({valHooks:{option:{get:function(elem){var val=jQuery.find.attr(elem,"value");return val!=null?val:jQuery.trim(jQuery.text(elem));}},select:{get:function(elem){var value,option,options=elem.options,index=elem.selectedIndex,one=elem.type==="select-one"||index<0,values=one?null:[],max=one?index+1:options.length,i=index<0?max:one?index:0;for(;i=0){try{option.selected=optionSet=true;}catch(_){option.scrollHeight;}}else{option.selected=false;}}
+if(!optionSet){elem.selectedIndex=-1;}
+return options;}}}});jQuery.each(["radio","checkbox"],function(){jQuery.valHooks[this]={set:function(elem,value){if(jQuery.isArray(value)){return(elem.checked=jQuery.inArray(jQuery(elem).val(),value)>=0);}}};if(!support.checkOn){jQuery.valHooks[this].get=function(elem){return elem.getAttribute("value")===null?"on":elem.value;};}});var nodeHook,boolHook,attrHandle=jQuery.expr.attrHandle,ruseDefault=/^(?:checked|selected)$/i,getSetAttribute=support.getSetAttribute,getSetInput=support.input;jQuery.fn.extend({attr:function(name,value){return access(this,jQuery.attr,name,value,arguments.length>1);},removeAttr:function(name){return this.each(function(){jQuery.removeAttr(this,name);});}});jQuery.extend({attr:function(elem,name,value){var hooks,ret,nType=elem.nodeType;if(!elem||nType===3||nType===8||nType===2){return;}
+if(typeof elem.getAttribute===strundefined){return jQuery.prop(elem,name,value);}
+if(nType!==1||!jQuery.isXMLDoc(elem)){name=name.toLowerCase();hooks=jQuery.attrHooks[name]||(jQuery.expr.match.bool.test(name)?boolHook:nodeHook);}
+if(value!==undefined){if(value===null){jQuery.removeAttr(elem,name);}else if(hooks&&"set"in hooks&&(ret=hooks.set(elem,value,name))!==undefined){return ret;}else{elem.setAttribute(name,value+"");return value;}}else if(hooks&&"get"in hooks&&(ret=hooks.get(elem,name))!==null){return ret;}else{ret=jQuery.find.attr(elem,name);return ret==null?undefined:ret;}},removeAttr:function(elem,value){var name,propName,i=0,attrNames=value&&value.match(rnotwhite);if(attrNames&&elem.nodeType===1){while((name=attrNames[i++])){propName=jQuery.propFix[name]||name;if(jQuery.expr.match.bool.test(name)){if(getSetInput&&getSetAttribute||!ruseDefault.test(name)){elem[propName]=false;}else{elem[jQuery.camelCase("default-"+name)]=elem[propName]=false;}}else{jQuery.attr(elem,name,"");}
+elem.removeAttribute(getSetAttribute?name:propName);}}},attrHooks:{type:{set:function(elem,value){if(!support.radioValue&&value==="radio"&&jQuery.nodeName(elem,"input")){var val=elem.value;elem.setAttribute("type",value);if(val){elem.value=val;}
+return value;}}}}});boolHook={set:function(elem,value,name){if(value===false){jQuery.removeAttr(elem,name);}else if(getSetInput&&getSetAttribute||!ruseDefault.test(name)){elem.setAttribute(!getSetAttribute&&jQuery.propFix[name]||name,name);}else{elem[jQuery.camelCase("default-"+name)]=elem[name]=true;}
+return name;}};jQuery.each(jQuery.expr.match.bool.source.match(/\w+/g),function(i,name){var getter=attrHandle[name]||jQuery.find.attr;attrHandle[name]=getSetInput&&getSetAttribute||!ruseDefault.test(name)?function(elem,name,isXML){var ret,handle;if(!isXML){handle=attrHandle[name];attrHandle[name]=ret;ret=getter(elem,name,isXML)!=null?name.toLowerCase():null;attrHandle[name]=handle;}
+return ret;}:function(elem,name,isXML){if(!isXML){return elem[jQuery.camelCase("default-"+name)]?name.toLowerCase():null;}};});if(!getSetInput||!getSetAttribute){jQuery.attrHooks.value={set:function(elem,value,name){if(jQuery.nodeName(elem,"input")){elem.defaultValue=value;}else{return nodeHook&&nodeHook.set(elem,value,name);}}};}
+if(!getSetAttribute){nodeHook={set:function(elem,value,name){var ret=elem.getAttributeNode(name);if(!ret){elem.setAttributeNode((ret=elem.ownerDocument.createAttribute(name)));}
+ret.value=value+="";if(name==="value"||value===elem.getAttribute(name)){return value;}}};attrHandle.id=attrHandle.name=attrHandle.coords=function(elem,name,isXML){var ret;if(!isXML){return(ret=elem.getAttributeNode(name))&&ret.value!==""?ret.value:null;}};jQuery.valHooks.button={get:function(elem,name){var ret=elem.getAttributeNode(name);if(ret&&ret.specified){return ret.value;}},set:nodeHook.set};jQuery.attrHooks.contenteditable={set:function(elem,value,name){nodeHook.set(elem,value===""?false:value,name);}};jQuery.each(["width","height"],function(i,name){jQuery.attrHooks[name]={set:function(elem,value){if(value===""){elem.setAttribute(name,"auto");return value;}}};});}
+if(!support.style){jQuery.attrHooks.style={get:function(elem){return elem.style.cssText||undefined;},set:function(elem,value){return(elem.style.cssText=value+"");}};}
+var rfocusable=/^(?:input|select|textarea|button|object)$/i,rclickable=/^(?:a|area)$/i;jQuery.fn.extend({prop:function(name,value){return access(this,jQuery.prop,name,value,arguments.length>1);},removeProp:function(name){name=jQuery.propFix[name]||name;return this.each(function(){try{this[name]=undefined;delete this[name];}catch(e){}});}});jQuery.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(elem,name,value){var ret,hooks,notxml,nType=elem.nodeType;if(!elem||nType===3||nType===8||nType===2){return;}
+notxml=nType!==1||!jQuery.isXMLDoc(elem);if(notxml){name=jQuery.propFix[name]||name;hooks=jQuery.propHooks[name];}
+if(value!==undefined){return hooks&&"set"in hooks&&(ret=hooks.set(elem,value,name))!==undefined?ret:(elem[name]=value);}else{return hooks&&"get"in hooks&&(ret=hooks.get(elem,name))!==null?ret:elem[name];}},propHooks:{tabIndex:{get:function(elem){var tabindex=jQuery.find.attr(elem,"tabindex");return tabindex?parseInt(tabindex,10):rfocusable.test(elem.nodeName)||rclickable.test(elem.nodeName)&&elem.href?0:-1;}}}});if(!support.hrefNormalized){jQuery.each(["href","src"],function(i,name){jQuery.propHooks[name]={get:function(elem){return elem.getAttribute(name,4);}};});}
+if(!support.optSelected){jQuery.propHooks.selected={get:function(elem){var parent=elem.parentNode;if(parent){parent.selectedIndex;if(parent.parentNode){parent.parentNode.selectedIndex;}}
+return null;}};}
+jQuery.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){jQuery.propFix[this.toLowerCase()]=this;});if(!support.enctype){jQuery.propFix.enctype="encoding";}
+var rclass=/[\t\r\n\f]/g;jQuery.fn.extend({addClass:function(value){var classes,elem,cur,clazz,j,finalValue,i=0,len=this.length,proceed=typeof value==="string"&&value;if(jQuery.isFunction(value)){return this.each(function(j){jQuery(this).addClass(value.call(this,j,this.className));});}
+if(proceed){classes=(value||"").match(rnotwhite)||[];for(;i=0){cur=cur.replace(" "+clazz+" "," ");}}
+finalValue=value?jQuery.trim(cur):"";if(elem.className!==finalValue){elem.className=finalValue;}}}}
+return this;},toggleClass:function(value,stateVal){var type=typeof value;if(typeof stateVal==="boolean"&&type==="string"){return stateVal?this.addClass(value):this.removeClass(value);}
+if(jQuery.isFunction(value)){return this.each(function(i){jQuery(this).toggleClass(value.call(this,i,this.className,stateVal),stateVal);});}
+return this.each(function(){if(type==="string"){var className,i=0,self=jQuery(this),classNames=value.match(rnotwhite)||[];while((className=classNames[i++])){if(self.hasClass(className)){self.removeClass(className);}else{self.addClass(className);}}}else if(type===strundefined||type==="boolean"){if(this.className){jQuery._data(this,"__className__",this.className);}
+this.className=this.className||value===false?"":jQuery._data(this,"__className__")||"";}});},hasClass:function(selector){var className=" "+selector+" ",i=0,l=this.length;for(;i=0){return true;}}
+return false;}});jQuery.each(("blur focus focusin focusout load resize scroll unload click dblclick "+"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave "+"change select submit keydown keypress keyup error contextmenu").split(" "),function(i,name){jQuery.fn[name]=function(data,fn){return arguments.length>0?this.on(name,null,data,fn):this.trigger(name);};});jQuery.fn.extend({hover:function(fnOver,fnOut){return this.mouseenter(fnOver).mouseleave(fnOut||fnOver);},bind:function(types,data,fn){return this.on(types,null,data,fn);},unbind:function(types,fn){return this.off(types,null,fn);},delegate:function(selector,types,data,fn){return this.on(types,selector,data,fn);},undelegate:function(selector,types,fn){return arguments.length===1?this.off(selector,"**"):this.off(types,selector||"**",fn);}});var nonce=jQuery.now();var rquery=(/\?/);var rvalidtokens=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;jQuery.parseJSON=function(data){if(window.JSON&&window.JSON.parse){return window.JSON.parse(data+"");}
+var requireNonComma,depth=null,str=jQuery.trim(data+"");return str&&!jQuery.trim(str.replace(rvalidtokens,function(token,comma,open,close){if(requireNonComma&&comma){depth=0;}
+if(depth===0){return token;}
+requireNonComma=open||comma;depth+=!close-!open;return"";}))?(Function("return "+str))():jQuery.error("Invalid JSON: "+data);};jQuery.parseXML=function(data){var xml,tmp;if(!data||typeof data!=="string"){return null;}
+try{if(window.DOMParser){tmp=new DOMParser();xml=tmp.parseFromString(data,"text/xml");}else{xml=new ActiveXObject("Microsoft.XMLDOM");xml.async="false";xml.loadXML(data);}}catch(e){xml=undefined;}
+if(!xml||!xml.documentElement||xml.getElementsByTagName("parsererror").length){jQuery.error("Invalid XML: "+data);}
+return xml;};var
+ajaxLocParts,ajaxLocation,rhash=/#.*$/,rts=/([?&])_=[^&]*/,rheaders=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,rlocalProtocol=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,rnoContent=/^(?:GET|HEAD)$/,rprotocol=/^\/\//,rurl=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,prefilters={},transports={},allTypes="*/".concat("*");try{ajaxLocation=location.href;}catch(e){ajaxLocation=document.createElement("a");ajaxLocation.href="";ajaxLocation=ajaxLocation.href;}
+ajaxLocParts=rurl.exec(ajaxLocation.toLowerCase())||[];function addToPrefiltersOrTransports(structure){return function(dataTypeExpression,func){if(typeof dataTypeExpression!=="string"){func=dataTypeExpression;dataTypeExpression="*";}
+var dataType,i=0,dataTypes=dataTypeExpression.toLowerCase().match(rnotwhite)||[];if(jQuery.isFunction(func)){while((dataType=dataTypes[i++])){if(dataType.charAt(0)==="+"){dataType=dataType.slice(1)||"*";(structure[dataType]=structure[dataType]||[]).unshift(func);}else{(structure[dataType]=structure[dataType]||[]).push(func);}}}};}
+function inspectPrefiltersOrTransports(structure,options,originalOptions,jqXHR){var inspected={},seekingTransport=(structure===transports);function inspect(dataType){var selected;inspected[dataType]=true;jQuery.each(structure[dataType]||[],function(_,prefilterOrFactory){var dataTypeOrTransport=prefilterOrFactory(options,originalOptions,jqXHR);if(typeof dataTypeOrTransport==="string"&&!seekingTransport&&!inspected[dataTypeOrTransport]){options.dataTypes.unshift(dataTypeOrTransport);inspect(dataTypeOrTransport);return false;}else if(seekingTransport){return!(selected=dataTypeOrTransport);}});return selected;}
+return inspect(options.dataTypes[0])||!inspected["*"]&&inspect("*");}
+function ajaxExtend(target,src){var deep,key,flatOptions=jQuery.ajaxSettings.flatOptions||{};for(key in src){if(src[key]!==undefined){(flatOptions[key]?target:(deep||(deep={})))[key]=src[key];}}
+if(deep){jQuery.extend(true,target,deep);}
+return target;}
+function ajaxHandleResponses(s,jqXHR,responses){var firstDataType,ct,finalDataType,type,contents=s.contents,dataTypes=s.dataTypes;while(dataTypes[0]==="*"){dataTypes.shift();if(ct===undefined){ct=s.mimeType||jqXHR.getResponseHeader("Content-Type");}}
+if(ct){for(type in contents){if(contents[type]&&contents[type].test(ct)){dataTypes.unshift(type);break;}}}
+if(dataTypes[0]in responses){finalDataType=dataTypes[0];}else{for(type in responses){if(!dataTypes[0]||s.converters[type+" "+dataTypes[0]]){finalDataType=type;break;}
+if(!firstDataType){firstDataType=type;}}
+finalDataType=finalDataType||firstDataType;}
+if(finalDataType){if(finalDataType!==dataTypes[0]){dataTypes.unshift(finalDataType);}
+return responses[finalDataType];}}
+function ajaxConvert(s,response,jqXHR,isSuccess){var conv2,current,conv,tmp,prev,converters={},dataTypes=s.dataTypes.slice();if(dataTypes[1]){for(conv in s.converters){converters[conv.toLowerCase()]=s.converters[conv];}}
+current=dataTypes.shift();while(current){if(s.responseFields[current]){jqXHR[s.responseFields[current]]=response;}
+if(!prev&&isSuccess&&s.dataFilter){response=s.dataFilter(response,s.dataType);}
+prev=current;current=dataTypes.shift();if(current){if(current==="*"){current=prev;}else if(prev!=="*"&&prev!==current){conv=converters[prev+" "+current]||converters["* "+current];if(!conv){for(conv2 in converters){tmp=conv2.split(" ");if(tmp[1]===current){conv=converters[prev+" "+tmp[0]]||converters["* "+tmp[0]];if(conv){if(conv===true){conv=converters[conv2];}else if(converters[conv2]!==true){current=tmp[0];dataTypes.unshift(tmp[1]);}
+break;}}}}
+if(conv!==true){if(conv&&s["throws"]){response=conv(response);}else{try{response=conv(response);}catch(e){return{state:"parsererror",error:conv?e:"No conversion from "+prev+" to "+current};}}}}}}
+return{state:"success",data:response};}
+jQuery.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:ajaxLocation,type:"GET",isLocal:rlocalProtocol.test(ajaxLocParts[1]),global:true,processData:true,async:true,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":allTypes,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":true,"text json":jQuery.parseJSON,"text xml":jQuery.parseXML},flatOptions:{url:true,context:true}},ajaxSetup:function(target,settings){return settings?ajaxExtend(ajaxExtend(target,jQuery.ajaxSettings),settings):ajaxExtend(jQuery.ajaxSettings,target);},ajaxPrefilter:addToPrefiltersOrTransports(prefilters),ajaxTransport:addToPrefiltersOrTransports(transports),ajax:function(url,options){if(typeof url==="object"){options=url;url=undefined;}
+options=options||{};var
+parts,i,cacheURL,responseHeadersString,timeoutTimer,fireGlobals,transport,responseHeaders,s=jQuery.ajaxSetup({},options),callbackContext=s.context||s,globalEventContext=s.context&&(callbackContext.nodeType||callbackContext.jquery)?jQuery(callbackContext):jQuery.event,deferred=jQuery.Deferred(),completeDeferred=jQuery.Callbacks("once memory"),statusCode=s.statusCode||{},requestHeaders={},requestHeadersNames={},state=0,strAbort="canceled",jqXHR={readyState:0,getResponseHeader:function(key){var match;if(state===2){if(!responseHeaders){responseHeaders={};while((match=rheaders.exec(responseHeadersString))){responseHeaders[match[1].toLowerCase()]=match[2];}}
+match=responseHeaders[key.toLowerCase()];}
+return match==null?null:match;},getAllResponseHeaders:function(){return state===2?responseHeadersString:null;},setRequestHeader:function(name,value){var lname=name.toLowerCase();if(!state){name=requestHeadersNames[lname]=requestHeadersNames[lname]||name;requestHeaders[name]=value;}
+return this;},overrideMimeType:function(type){if(!state){s.mimeType=type;}
+return this;},statusCode:function(map){var code;if(map){if(state<2){for(code in map){statusCode[code]=[statusCode[code],map[code]];}}else{jqXHR.always(map[jqXHR.status]);}}
+return this;},abort:function(statusText){var finalText=statusText||strAbort;if(transport){transport.abort(finalText);}
+done(0,finalText);return this;}};deferred.promise(jqXHR).complete=completeDeferred.add;jqXHR.success=jqXHR.done;jqXHR.error=jqXHR.fail;s.url=((url||s.url||ajaxLocation)+"").replace(rhash,"").replace(rprotocol,ajaxLocParts[1]+"//");s.type=options.method||options.type||s.method||s.type;s.dataTypes=jQuery.trim(s.dataType||"*").toLowerCase().match(rnotwhite)||[""];if(s.crossDomain==null){parts=rurl.exec(s.url.toLowerCase());s.crossDomain=!!(parts&&(parts[1]!==ajaxLocParts[1]||parts[2]!==ajaxLocParts[2]||(parts[3]||(parts[1]==="http:"?"80":"443"))!==(ajaxLocParts[3]||(ajaxLocParts[1]==="http:"?"80":"443"))));}
+if(s.data&&s.processData&&typeof s.data!=="string"){s.data=jQuery.param(s.data,s.traditional);}
+inspectPrefiltersOrTransports(prefilters,s,options,jqXHR);if(state===2){return jqXHR;}
+fireGlobals=s.global;if(fireGlobals&&jQuery.active++===0){jQuery.event.trigger("ajaxStart");}
+s.type=s.type.toUpperCase();s.hasContent=!rnoContent.test(s.type);cacheURL=s.url;if(!s.hasContent){if(s.data){cacheURL=(s.url+=(rquery.test(cacheURL)?"&":"?")+s.data);delete s.data;}
+if(s.cache===false){s.url=rts.test(cacheURL)?cacheURL.replace(rts,"$1_="+nonce++):cacheURL+(rquery.test(cacheURL)?"&":"?")+"_="+nonce++;}}
+if(s.ifModified){if(jQuery.lastModified[cacheURL]){jqXHR.setRequestHeader("If-Modified-Since",jQuery.lastModified[cacheURL]);}
+if(jQuery.etag[cacheURL]){jqXHR.setRequestHeader("If-None-Match",jQuery.etag[cacheURL]);}}
+if(s.data&&s.hasContent&&s.contentType!==false||options.contentType){jqXHR.setRequestHeader("Content-Type",s.contentType);}
+jqXHR.setRequestHeader("Accept",s.dataTypes[0]&&s.accepts[s.dataTypes[0]]?s.accepts[s.dataTypes[0]]+(s.dataTypes[0]!=="*"?", "+allTypes+"; q=0.01":""):s.accepts["*"]);for(i in s.headers){jqXHR.setRequestHeader(i,s.headers[i]);}
+if(s.beforeSend&&(s.beforeSend.call(callbackContext,jqXHR,s)===false||state===2)){return jqXHR.abort();}
+strAbort="abort";for(i in{success:1,error:1,complete:1}){jqXHR[i](s[i]);}
+transport=inspectPrefiltersOrTransports(transports,s,options,jqXHR);if(!transport){done(-1,"No Transport");}else{jqXHR.readyState=1;if(fireGlobals){globalEventContext.trigger("ajaxSend",[jqXHR,s]);}
+if(s.async&&s.timeout>0){timeoutTimer=setTimeout(function(){jqXHR.abort("timeout");},s.timeout);}
+try{state=1;transport.send(requestHeaders,done);}catch(e){if(state<2){done(-1,e);}else{throw e;}}}
+function done(status,nativeStatusText,responses,headers){var isSuccess,success,error,response,modified,statusText=nativeStatusText;if(state===2){return;}
+state=2;if(timeoutTimer){clearTimeout(timeoutTimer);}
+transport=undefined;responseHeadersString=headers||"";jqXHR.readyState=status>0?4:0;isSuccess=status>=200&&status<300||status===304;if(responses){response=ajaxHandleResponses(s,jqXHR,responses);}
+response=ajaxConvert(s,response,jqXHR,isSuccess);if(isSuccess){if(s.ifModified){modified=jqXHR.getResponseHeader("Last-Modified");if(modified){jQuery.lastModified[cacheURL]=modified;}
+modified=jqXHR.getResponseHeader("etag");if(modified){jQuery.etag[cacheURL]=modified;}}
+if(status===204||s.type==="HEAD"){statusText="nocontent";}else if(status===304){statusText="notmodified";}else{statusText=response.state;success=response.data;error=response.error;isSuccess=!error;}}else{error=statusText;if(status||!statusText){statusText="error";if(status<0){status=0;}}}
+jqXHR.status=status;jqXHR.statusText=(nativeStatusText||statusText)+"";if(isSuccess){deferred.resolveWith(callbackContext,[success,statusText,jqXHR]);}else{deferred.rejectWith(callbackContext,[jqXHR,statusText,error]);}
+jqXHR.statusCode(statusCode);statusCode=undefined;if(fireGlobals){globalEventContext.trigger(isSuccess?"ajaxSuccess":"ajaxError",[jqXHR,s,isSuccess?success:error]);}
+completeDeferred.fireWith(callbackContext,[jqXHR,statusText]);if(fireGlobals){globalEventContext.trigger("ajaxComplete",[jqXHR,s]);if(!(--jQuery.active)){jQuery.event.trigger("ajaxStop");}}}
+return jqXHR;},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json");},getScript:function(url,callback){return jQuery.get(url,undefined,callback,"script");}});jQuery.each(["get","post"],function(i,method){jQuery[method]=function(url,data,callback,type){if(jQuery.isFunction(data)){type=type||callback;callback=data;data=undefined;}
+return jQuery.ajax({url:url,type:method,dataType:type,data:data,success:callback});};});jQuery.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(i,type){jQuery.fn[type]=function(fn){return this.on(type,fn);};});jQuery._evalUrl=function(url){return jQuery.ajax({url:url,type:"GET",dataType:"script",async:false,global:false,"throws":true});};jQuery.fn.extend({wrapAll:function(html){if(jQuery.isFunction(html)){return this.each(function(i){jQuery(this).wrapAll(html.call(this,i));});}
+if(this[0]){var wrap=jQuery(html,this[0].ownerDocument).eq(0).clone(true);if(this[0].parentNode){wrap.insertBefore(this[0]);}
+wrap.map(function(){var elem=this;while(elem.firstChild&&elem.firstChild.nodeType===1){elem=elem.firstChild;}
+return elem;}).append(this);}
+return this;},wrapInner:function(html){if(jQuery.isFunction(html)){return this.each(function(i){jQuery(this).wrapInner(html.call(this,i));});}
+return this.each(function(){var self=jQuery(this),contents=self.contents();if(contents.length){contents.wrapAll(html);}else{self.append(html);}});},wrap:function(html){var isFunction=jQuery.isFunction(html);return this.each(function(i){jQuery(this).wrapAll(isFunction?html.call(this,i):html);});},unwrap:function(){return this.parent().each(function(){if(!jQuery.nodeName(this,"body")){jQuery(this).replaceWith(this.childNodes);}}).end();}});jQuery.expr.filters.hidden=function(elem){return elem.offsetWidth<=0&&elem.offsetHeight<=0||(!support.reliableHiddenOffsets()&&((elem.style&&elem.style.display)||jQuery.css(elem,"display"))==="none");};jQuery.expr.filters.visible=function(elem){return!jQuery.expr.filters.hidden(elem);};var r20=/%20/g,rbracket=/\[\]$/,rCRLF=/\r?\n/g,rsubmitterTypes=/^(?:submit|button|image|reset|file)$/i,rsubmittable=/^(?:input|select|textarea|keygen)/i;function buildParams(prefix,obj,traditional,add){var name;if(jQuery.isArray(obj)){jQuery.each(obj,function(i,v){if(traditional||rbracket.test(prefix)){add(prefix,v);}else{buildParams(prefix+"["+(typeof v==="object"?i:"")+"]",v,traditional,add);}});}else if(!traditional&&jQuery.type(obj)==="object"){for(name in obj){buildParams(prefix+"["+name+"]",obj[name],traditional,add);}}else{add(prefix,obj);}}
+jQuery.param=function(a,traditional){var prefix,s=[],add=function(key,value){value=jQuery.isFunction(value)?value():(value==null?"":value);s[s.length]=encodeURIComponent(key)+"="+encodeURIComponent(value);};if(traditional===undefined){traditional=jQuery.ajaxSettings&&jQuery.ajaxSettings.traditional;}
+if(jQuery.isArray(a)||(a.jquery&&!jQuery.isPlainObject(a))){jQuery.each(a,function(){add(this.name,this.value);});}else{for(prefix in a){buildParams(prefix,a[prefix],traditional,add);}}
+return s.join("&").replace(r20,"+");};jQuery.fn.extend({serialize:function(){return jQuery.param(this.serializeArray());},serializeArray:function(){return this.map(function(){var elements=jQuery.prop(this,"elements");return elements?jQuery.makeArray(elements):this;}).filter(function(){var type=this.type;return this.name&&!jQuery(this).is(":disabled")&&rsubmittable.test(this.nodeName)&&!rsubmitterTypes.test(type)&&(this.checked||!rcheckableType.test(type));}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:jQuery.isArray(val)?jQuery.map(val,function(val){return{name:elem.name,value:val.replace(rCRLF,"\r\n")};}):{name:elem.name,value:val.replace(rCRLF,"\r\n")};}).get();}});jQuery.ajaxSettings.xhr=window.ActiveXObject!==undefined?function(){return!this.isLocal&&/^(get|post|head|put|delete|options)$/i.test(this.type)&&createStandardXHR()||createActiveXHR();}:createStandardXHR;var xhrId=0,xhrCallbacks={},xhrSupported=jQuery.ajaxSettings.xhr();if(window.ActiveXObject){jQuery(window).on("unload",function(){for(var key in xhrCallbacks){xhrCallbacks[key](undefined,true);}});}
+support.cors=!!xhrSupported&&("withCredentials"in xhrSupported);xhrSupported=support.ajax=!!xhrSupported;if(xhrSupported){jQuery.ajaxTransport(function(options){if(!options.crossDomain||support.cors){var callback;return{send:function(headers,complete){var i,xhr=options.xhr(),id=++xhrId;xhr.open(options.type,options.url,options.async,options.username,options.password);if(options.xhrFields){for(i in options.xhrFields){xhr[i]=options.xhrFields[i];}}
+if(options.mimeType&&xhr.overrideMimeType){xhr.overrideMimeType(options.mimeType);}
+if(!options.crossDomain&&!headers["X-Requested-With"]){headers["X-Requested-With"]="XMLHttpRequest";}
+for(i in headers){if(headers[i]!==undefined){xhr.setRequestHeader(i,headers[i]+"");}}
+xhr.send((options.hasContent&&options.data)||null);callback=function(_,isAbort){var status,statusText,responses;if(callback&&(isAbort||xhr.readyState===4)){delete xhrCallbacks[id];callback=undefined;xhr.onreadystatechange=jQuery.noop;if(isAbort){if(xhr.readyState!==4){xhr.abort();}}else{responses={};status=xhr.status;if(typeof xhr.responseText==="string"){responses.text=xhr.responseText;}
+try{statusText=xhr.statusText;}catch(e){statusText="";}
+if(!status&&options.isLocal&&!options.crossDomain){status=responses.text?200:404;}else if(status===1223){status=204;}}}
+if(responses){complete(status,statusText,responses,xhr.getAllResponseHeaders());}};if(!options.async){callback();}else if(xhr.readyState===4){setTimeout(callback);}else{xhr.onreadystatechange=xhrCallbacks[id]=callback;}},abort:function(){if(callback){callback(undefined,true);}}};}});}
+function createStandardXHR(){try{return new window.XMLHttpRequest();}catch(e){}}
+function createActiveXHR(){try{return new window.ActiveXObject("Microsoft.XMLHTTP");}catch(e){}}
+jQuery.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(text){jQuery.globalEval(text);return text;}}});jQuery.ajaxPrefilter("script",function(s){if(s.cache===undefined){s.cache=false;}
+if(s.crossDomain){s.type="GET";s.global=false;}});jQuery.ajaxTransport("script",function(s){if(s.crossDomain){var script,head=document.head||jQuery("head")[0]||document.documentElement;return{send:function(_,callback){script=document.createElement("script");script.async=true;if(s.scriptCharset){script.charset=s.scriptCharset;}
+script.src=s.url;script.onload=script.onreadystatechange=function(_,isAbort){if(isAbort||!script.readyState||/loaded|complete/.test(script.readyState)){script.onload=script.onreadystatechange=null;if(script.parentNode){script.parentNode.removeChild(script);}
+script=null;if(!isAbort){callback(200,"success");}}};head.insertBefore(script,head.firstChild);},abort:function(){if(script){script.onload(undefined,true);}}};}});var oldCallbacks=[],rjsonp=/(=)\?(?=&|$)|\?\?/;jQuery.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var callback=oldCallbacks.pop()||(jQuery.expando+"_"+(nonce++));this[callback]=true;return callback;}});jQuery.ajaxPrefilter("json jsonp",function(s,originalSettings,jqXHR){var callbackName,overwritten,responseContainer,jsonProp=s.jsonp!==false&&(rjsonp.test(s.url)?"url":typeof s.data==="string"&&!(s.contentType||"").indexOf("application/x-www-form-urlencoded")&&rjsonp.test(s.data)&&"data");if(jsonProp||s.dataTypes[0]==="jsonp"){callbackName=s.jsonpCallback=jQuery.isFunction(s.jsonpCallback)?s.jsonpCallback():s.jsonpCallback;if(jsonProp){s[jsonProp]=s[jsonProp].replace(rjsonp,"$1"+callbackName);}else if(s.jsonp!==false){s.url+=(rquery.test(s.url)?"&":"?")+s.jsonp+"="+callbackName;}
+s.converters["script json"]=function(){if(!responseContainer){jQuery.error(callbackName+" was not called");}
+return responseContainer[0];};s.dataTypes[0]="json";overwritten=window[callbackName];window[callbackName]=function(){responseContainer=arguments;};jqXHR.always(function(){window[callbackName]=overwritten;if(s[callbackName]){s.jsonpCallback=originalSettings.jsonpCallback;oldCallbacks.push(callbackName);}
+if(responseContainer&&jQuery.isFunction(overwritten)){overwritten(responseContainer[0]);}
+responseContainer=overwritten=undefined;});return"script";}});jQuery.parseHTML=function(data,context,keepScripts){if(!data||typeof data!=="string"){return null;}
+if(typeof context==="boolean"){keepScripts=context;context=false;}
+context=context||document;var parsed=rsingleTag.exec(data),scripts=!keepScripts&&[];if(parsed){return[context.createElement(parsed[1])];}
+parsed=jQuery.buildFragment([data],context,scripts);if(scripts&&scripts.length){jQuery(scripts).remove();}
+return jQuery.merge([],parsed.childNodes);};var _load=jQuery.fn.load;jQuery.fn.load=function(url,params,callback){if(typeof url!=="string"&&_load){return _load.apply(this,arguments);}
+var selector,response,type,self=this,off=url.indexOf(" ");if(off>=0){selector=jQuery.trim(url.slice(off,url.length));url=url.slice(0,off);}
+if(jQuery.isFunction(params)){callback=params;params=undefined;}else if(params&&typeof params==="object"){type="POST";}
+if(self.length>0){jQuery.ajax({url:url,type:type,dataType:"html",data:params}).done(function(responseText){response=arguments;self.html(selector?jQuery("").append(jQuery.parseHTML(responseText)).find(selector):responseText);}).complete(callback&&function(jqXHR,status){self.each(callback,response||[jqXHR.responseText,status,jqXHR]);});}
+return this;};jQuery.expr.filters.animated=function(elem){return jQuery.grep(jQuery.timers,function(fn){return elem===fn.elem;}).length;};var docElem=window.document.documentElement;function getWindow(elem){return jQuery.isWindow(elem)?elem:elem.nodeType===9?elem.defaultView||elem.parentWindow:false;}
+jQuery.offset={setOffset:function(elem,options,i){var curPosition,curLeft,curCSSTop,curTop,curOffset,curCSSLeft,calculatePosition,position=jQuery.css(elem,"position"),curElem=jQuery(elem),props={};if(position==="static"){elem.style.position="relative";}
+curOffset=curElem.offset();curCSSTop=jQuery.css(elem,"top");curCSSLeft=jQuery.css(elem,"left");calculatePosition=(position==="absolute"||position==="fixed")&&jQuery.inArray("auto",[curCSSTop,curCSSLeft])>-1;if(calculatePosition){curPosition=curElem.position();curTop=curPosition.top;curLeft=curPosition.left;}else{curTop=parseFloat(curCSSTop)||0;curLeft=parseFloat(curCSSLeft)||0;}
+if(jQuery.isFunction(options)){options=options.call(elem,i,curOffset);}
+if(options.top!=null){props.top=(options.top-curOffset.top)+curTop;}
+if(options.left!=null){props.left=(options.left-curOffset.left)+curLeft;}
+if("using"in options){options.using.call(elem,props);}else{curElem.css(props);}}};jQuery.fn.extend({offset:function(options){if(arguments.length){return options===undefined?this:this.each(function(i){jQuery.offset.setOffset(this,options,i);});}
+var docElem,win,box={top:0,left:0},elem=this[0],doc=elem&&elem.ownerDocument;if(!doc){return;}
+docElem=doc.documentElement;if(!jQuery.contains(docElem,elem)){return box;}
+if(typeof elem.getBoundingClientRect!==strundefined){box=elem.getBoundingClientRect();}
+win=getWindow(doc);return{top:box.top+(win.pageYOffset||docElem.scrollTop)-(docElem.clientTop||0),left:box.left+(win.pageXOffset||docElem.scrollLeft)-(docElem.clientLeft||0)};},position:function(){if(!this[0]){return;}
+var offsetParent,offset,parentOffset={top:0,left:0},elem=this[0];if(jQuery.css(elem,"position")==="fixed"){offset=elem.getBoundingClientRect();}else{offsetParent=this.offsetParent();offset=this.offset();if(!jQuery.nodeName(offsetParent[0],"html")){parentOffset=offsetParent.offset();}
+parentOffset.top+=jQuery.css(offsetParent[0],"borderTopWidth",true);parentOffset.left+=jQuery.css(offsetParent[0],"borderLeftWidth",true);}
+return{top:offset.top-parentOffset.top-jQuery.css(elem,"marginTop",true),left:offset.left-parentOffset.left-jQuery.css(elem,"marginLeft",true)};},offsetParent:function(){return this.map(function(){var offsetParent=this.offsetParent||docElem;while(offsetParent&&(!jQuery.nodeName(offsetParent,"html")&&jQuery.css(offsetParent,"position")==="static")){offsetParent=offsetParent.offsetParent;}
+return offsetParent||docElem;});}});jQuery.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(method,prop){var top=/Y/.test(prop);jQuery.fn[method]=function(val){return access(this,function(elem,method,val){var win=getWindow(elem);if(val===undefined){return win?(prop in win)?win[prop]:win.document.documentElement[method]:elem[method];}
+if(win){win.scrollTo(!top?val:jQuery(win).scrollLeft(),top?val:jQuery(win).scrollTop());}else{elem[method]=val;}},method,val,arguments.length,null);};});jQuery.each(["top","left"],function(i,prop){jQuery.cssHooks[prop]=addGetHookIf(support.pixelPosition,function(elem,computed){if(computed){computed=curCSS(elem,prop);return rnumnonpx.test(computed)?jQuery(elem).position()[prop]+"px":computed;}});});jQuery.each({Height:"height",Width:"width"},function(name,type){jQuery.each({padding:"inner"+name,content:type,"":"outer"+name},function(defaultExtra,funcName){jQuery.fn[funcName]=function(margin,value){var chainable=arguments.length&&(defaultExtra||typeof margin!=="boolean"),extra=defaultExtra||(margin===true||value===true?"margin":"border");return access(this,function(elem,type,value){var doc;if(jQuery.isWindow(elem)){return elem.document.documentElement["client"+name];}
+if(elem.nodeType===9){doc=elem.documentElement;return Math.max(elem.body["scroll"+name],doc["scroll"+name],elem.body["offset"+name],doc["offset"+name],doc["client"+name]);}
+return value===undefined?jQuery.css(elem,type,extra):jQuery.style(elem,type,value,extra);},type,chainable?margin:undefined,chainable,null);};});});jQuery.fn.size=function(){return this.length;};jQuery.fn.andSelf=jQuery.fn.addBack;if(typeof define==="function"&&define.amd){define("jquery",[],function(){return jQuery;});}
+var
+_jQuery=window.jQuery,_$=window.$;jQuery.noConflict=function(deep){if(window.$===jQuery){window.$=_$;}
+if(deep&&window.jQuery===jQuery){window.jQuery=_jQuery;}
+return jQuery;};if(typeof noGlobal===strundefined){window.jQuery=window.$=jQuery;}
+return jQuery;}));
\ No newline at end of file
diff --git a/dotclear._no/inc/js/jquery/1.11.3/jquery.cookie.js b/dotclear._no/inc/js/jquery/1.11.3/jquery.cookie.js
new file mode 100755
index 0000000..741936b
--- /dev/null
+++ b/dotclear._no/inc/js/jquery/1.11.3/jquery.cookie.js
@@ -0,0 +1,13 @@
+
+(function(factory){if(typeof define==='function'&&define.amd){define(['jquery'],factory);}else if(typeof exports==='object'){factory(require('jquery'));}else{factory(jQuery);}}(function($){var pluses=/\+/g;function encode(s){return config.raw?s:encodeURIComponent(s);}
+function decode(s){return config.raw?s:decodeURIComponent(s);}
+function stringifyCookieValue(value){return encode(config.json?JSON.stringify(value):String(value));}
+function parseCookieValue(s){if(s.indexOf('"')===0){s=s.slice(1,-1).replace(/\\"/g,'"').replace(/\\\\/g,'\\');}
+try{s=decodeURIComponent(s.replace(pluses,' '));return config.json?JSON.parse(s):s;}catch(e){}}
+function read(s,converter){var value=config.raw?s:parseCookieValue(s);return $.isFunction(converter)?converter(value):value;}
+var config=$.cookie=function(key,value,options){if(arguments.length>1&&!$.isFunction(value)){options=$.extend({},config.defaults,options);if(typeof options.expires==='number'){var days=options.expires,t=options.expires=new Date();t.setTime(+t+days*864e+5);}
+return(document.cookie=[encode(key),'=',stringifyCookieValue(value),options.expires?'; expires='+options.expires.toUTCString():'',options.path?'; path='+options.path:'',options.domain?'; domain='+options.domain:'',options.secure?'; secure':''].join(''));}
+var result=key?undefined:{};var cookies=document.cookie?document.cookie.split('; '):[];for(var i=0,l=cookies.length;i
=0&&j=0;},isEmptyObject:function(obj){var name;for(name in obj){return false;}
+return true;},isPlainObject:function(obj){var key;if(!obj||jQuery.type(obj)!=="object"||obj.nodeType||jQuery.isWindow(obj)){return false;}
+try{if(obj.constructor&&!hasOwn.call(obj,"constructor")&&!hasOwn.call(obj.constructor.prototype,"isPrototypeOf")){return false;}}catch(e){return false;}
+if(support.ownLast){for(key in obj){return hasOwn.call(obj,key);}}
+for(key in obj){}
+return key===undefined||hasOwn.call(obj,key);},type:function(obj){if(obj==null){return obj+"";}
+return typeof obj==="object"||typeof obj==="function"?class2type[toString.call(obj)]||"object":typeof obj;},globalEval:function(data){if(data&&jQuery.trim(data)){(window.execScript||function(data){window["eval"].call(window,data);})(data);}},camelCase:function(string){return string.replace(rmsPrefix,"ms-").replace(rdashAlpha,fcamelCase);},nodeName:function(elem,name){return elem.nodeName&&elem.nodeName.toLowerCase()===name.toLowerCase();},each:function(obj,callback,args){var value,i=0,length=obj.length,isArray=isArraylike(obj);if(args){if(isArray){for(;i0&&(length-1)in obj;}
+var Sizzle=(function(window){var i,support,Expr,getText,isXML,tokenize,compile,select,outermostContext,sortInput,hasDuplicate,setDocument,document,docElem,documentIsHTML,rbuggyQSA,rbuggyMatches,matches,contains,expando="sizzle"+1*new Date(),preferredDoc=window.document,dirruns=0,done=0,classCache=createCache(),tokenCache=createCache(),compilerCache=createCache(),sortOrder=function(a,b){if(a===b){hasDuplicate=true;}
+return 0;},MAX_NEGATIVE=1<<31,hasOwn=({}).hasOwnProperty,arr=[],pop=arr.pop,push_native=arr.push,push=arr.push,slice=arr.slice,indexOf=function(list,elem){var i=0,len=list.length;for(;i+~]|"+whitespace+")"+whitespace+"*"),rattributeQuotes=new RegExp("="+whitespace+"*([^\\]'\"]*?)"+whitespace+"*\\]","g"),rpseudo=new RegExp(pseudos),ridentifier=new RegExp("^"+identifier+"$"),matchExpr={"ID":new RegExp("^#("+characterEncoding+")"),"CLASS":new RegExp("^\\.("+characterEncoding+")"),"TAG":new RegExp("^("+characterEncoding.replace("w","w*")+")"),"ATTR":new RegExp("^"+attributes),"PSEUDO":new RegExp("^"+pseudos),"CHILD":new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+whitespace+"*(even|odd|(([+-]|)(\\d*)n|)"+whitespace+"*(?:([+-]|)"+whitespace+"*(\\d+)|))"+whitespace+"*\\)|)","i"),"bool":new RegExp("^(?:"+booleans+")$","i"),"needsContext":new RegExp("^"+whitespace+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+
+whitespace+"*((?:-\\d)?\\d*)"+whitespace+"*\\)|)(?=[^-]|$)","i")},rinputs=/^(?:input|select|textarea|button)$/i,rheader=/^h\d$/i,rnative=/^[^{]+\{\s*\[native \w/,rquickExpr=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,rsibling=/[+~]/,rescape=/'|\\/g,runescape=new RegExp("\\\\([\\da-f]{1,6}"+whitespace+"?|("+whitespace+")|.)","ig"),funescape=function(_,escaped,escapedWhitespace){var high="0x"+escaped-0x10000;return high!==high||escapedWhitespace?escaped:high<0?String.fromCharCode(high+0x10000):String.fromCharCode(high>>10|0xD800,high&0x3FF|0xDC00);},unloadHandler=function(){setDocument();};try{push.apply((arr=slice.call(preferredDoc.childNodes)),preferredDoc.childNodes);arr[preferredDoc.childNodes.length].nodeType;}catch(e){push={apply:arr.length?function(target,els){push_native.apply(target,slice.call(els));}:function(target,els){var j=target.length,i=0;while((target[j++]=els[i++])){}
+target.length=j-1;}};}
+function Sizzle(selector,context,results,seed){var match,elem,m,nodeType,i,groups,old,nid,newContext,newSelector;if((context?context.ownerDocument||context:preferredDoc)!==document){setDocument(context);}
+context=context||document;results=results||[];nodeType=context.nodeType;if(typeof selector!=="string"||!selector||nodeType!==1&&nodeType!==9&&nodeType!==11){return results;}
+if(!seed&&documentIsHTML){if(nodeType!==11&&(match=rquickExpr.exec(selector))){if((m=match[1])){if(nodeType===9){elem=context.getElementById(m);if(elem&&elem.parentNode){if(elem.id===m){results.push(elem);return results;}}else{return results;}}else{if(context.ownerDocument&&(elem=context.ownerDocument.getElementById(m))&&contains(context,elem)&&elem.id===m){results.push(elem);return results;}}}else if(match[2]){push.apply(results,context.getElementsByTagName(selector));return results;}else if((m=match[3])&&support.getElementsByClassName){push.apply(results,context.getElementsByClassName(m));return results;}}
+if(support.qsa&&(!rbuggyQSA||!rbuggyQSA.test(selector))){nid=old=expando;newContext=context;newSelector=nodeType!==1&&selector;if(nodeType===1&&context.nodeName.toLowerCase()!=="object"){groups=tokenize(selector);if((old=context.getAttribute("id"))){nid=old.replace(rescape,"\\$&");}else{context.setAttribute("id",nid);}
+nid="[id='"+nid+"'] ";i=groups.length;while(i--){groups[i]=nid+toSelector(groups[i]);}
+newContext=rsibling.test(selector)&&testContext(context.parentNode)||context;newSelector=groups.join(",");}
+if(newSelector){try{push.apply(results,newContext.querySelectorAll(newSelector));return results;}catch(qsaError){}finally{if(!old){context.removeAttribute("id");}}}}}
+return select(selector.replace(rtrim,"$1"),context,results,seed);}
+function createCache(){var keys=[];function cache(key,value){if(keys.push(key+" ")>Expr.cacheLength){delete cache[keys.shift()];}
+return(cache[key+" "]=value);}
+return cache;}
+function markFunction(fn){fn[expando]=true;return fn;}
+function assert(fn){var div=document.createElement("div");try{return!!fn(div);}catch(e){return false;}finally{if(div.parentNode){div.parentNode.removeChild(div);}
+div=null;}}
+function addHandle(attrs,handler){var arr=attrs.split("|"),i=attrs.length;while(i--){Expr.attrHandle[arr[i]]=handler;}}
+function siblingCheck(a,b){var cur=b&&a,diff=cur&&a.nodeType===1&&b.nodeType===1&&(~b.sourceIndex||MAX_NEGATIVE)-
+(~a.sourceIndex||MAX_NEGATIVE);if(diff){return diff;}
+if(cur){while((cur=cur.nextSibling)){if(cur===b){return-1;}}}
+return a?1:-1;}
+function createInputPseudo(type){return function(elem){var name=elem.nodeName.toLowerCase();return name==="input"&&elem.type===type;};}
+function createButtonPseudo(type){return function(elem){var name=elem.nodeName.toLowerCase();return(name==="input"||name==="button")&&elem.type===type;};}
+function createPositionalPseudo(fn){return markFunction(function(argument){argument=+argument;return markFunction(function(seed,matches){var j,matchIndexes=fn([],seed.length,argument),i=matchIndexes.length;while(i--){if(seed[(j=matchIndexes[i])]){seed[j]=!(matches[j]=seed[j]);}}});});}
+function testContext(context){return context&&typeof context.getElementsByTagName!=="undefined"&&context;}
+support=Sizzle.support={};isXML=Sizzle.isXML=function(elem){var documentElement=elem&&(elem.ownerDocument||elem).documentElement;return documentElement?documentElement.nodeName!=="HTML":false;};setDocument=Sizzle.setDocument=function(node){var hasCompare,parent,doc=node?node.ownerDocument||node:preferredDoc;if(doc===document||doc.nodeType!==9||!doc.documentElement){return document;}
+document=doc;docElem=doc.documentElement;parent=doc.defaultView;if(parent&&parent!==parent.top){if(parent.addEventListener){parent.addEventListener("unload",unloadHandler,false);}else if(parent.attachEvent){parent.attachEvent("onunload",unloadHandler);}}
+documentIsHTML=!isXML(doc);support.attributes=assert(function(div){div.className="i";return!div.getAttribute("className");});support.getElementsByTagName=assert(function(div){div.appendChild(doc.createComment(""));return!div.getElementsByTagName("*").length;});support.getElementsByClassName=rnative.test(doc.getElementsByClassName);support.getById=assert(function(div){docElem.appendChild(div).id=expando;return!doc.getElementsByName||!doc.getElementsByName(expando).length;});if(support.getById){Expr.find["ID"]=function(id,context){if(typeof context.getElementById!=="undefined"&&documentIsHTML){var m=context.getElementById(id);return m&&m.parentNode?[m]:[];}};Expr.filter["ID"]=function(id){var attrId=id.replace(runescape,funescape);return function(elem){return elem.getAttribute("id")===attrId;};};}else{delete Expr.find["ID"];Expr.filter["ID"]=function(id){var attrId=id.replace(runescape,funescape);return function(elem){var node=typeof elem.getAttributeNode!=="undefined"&&elem.getAttributeNode("id");return node&&node.value===attrId;};};}
+Expr.find["TAG"]=support.getElementsByTagName?function(tag,context){if(typeof context.getElementsByTagName!=="undefined"){return context.getElementsByTagName(tag);}else if(support.qsa){return context.querySelectorAll(tag);}}:function(tag,context){var elem,tmp=[],i=0,results=context.getElementsByTagName(tag);if(tag==="*"){while((elem=results[i++])){if(elem.nodeType===1){tmp.push(elem);}}
+return tmp;}
+return results;};Expr.find["CLASS"]=support.getElementsByClassName&&function(className,context){if(documentIsHTML){return context.getElementsByClassName(className);}};rbuggyMatches=[];rbuggyQSA=[];if((support.qsa=rnative.test(doc.querySelectorAll))){assert(function(div){docElem.appendChild(div).innerHTML=" "+""+" ";if(div.querySelectorAll("[msallowcapture^='']").length){rbuggyQSA.push("[*^$]="+whitespace+"*(?:''|\"\")");}
+if(!div.querySelectorAll("[selected]").length){rbuggyQSA.push("\\["+whitespace+"*(?:value|"+booleans+")");}
+if(!div.querySelectorAll("[id~="+expando+"-]").length){rbuggyQSA.push("~=");}
+if(!div.querySelectorAll(":checked").length){rbuggyQSA.push(":checked");}
+if(!div.querySelectorAll("a#"+expando+"+*").length){rbuggyQSA.push(".#.+[+~]");}});assert(function(div){var input=doc.createElement("input");input.setAttribute("type","hidden");div.appendChild(input).setAttribute("name","D");if(div.querySelectorAll("[name=d]").length){rbuggyQSA.push("name"+whitespace+"*[*^$|!~]?=");}
+if(!div.querySelectorAll(":enabled").length){rbuggyQSA.push(":enabled",":disabled");}
+div.querySelectorAll("*,:x");rbuggyQSA.push(",.*:");});}
+if((support.matchesSelector=rnative.test((matches=docElem.matches||docElem.webkitMatchesSelector||docElem.mozMatchesSelector||docElem.oMatchesSelector||docElem.msMatchesSelector)))){assert(function(div){support.disconnectedMatch=matches.call(div,"div");matches.call(div,"[s!='']:x");rbuggyMatches.push("!=",pseudos);});}
+rbuggyQSA=rbuggyQSA.length&&new RegExp(rbuggyQSA.join("|"));rbuggyMatches=rbuggyMatches.length&&new RegExp(rbuggyMatches.join("|"));hasCompare=rnative.test(docElem.compareDocumentPosition);contains=hasCompare||rnative.test(docElem.contains)?function(a,b){var adown=a.nodeType===9?a.documentElement:a,bup=b&&b.parentNode;return a===bup||!!(bup&&bup.nodeType===1&&(adown.contains?adown.contains(bup):a.compareDocumentPosition&&a.compareDocumentPosition(bup)&16));}:function(a,b){if(b){while((b=b.parentNode)){if(b===a){return true;}}}
+return false;};sortOrder=hasCompare?function(a,b){if(a===b){hasDuplicate=true;return 0;}
+var compare=!a.compareDocumentPosition-!b.compareDocumentPosition;if(compare){return compare;}
+compare=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1;if(compare&1||(!support.sortDetached&&b.compareDocumentPosition(a)===compare)){if(a===doc||a.ownerDocument===preferredDoc&&contains(preferredDoc,a)){return-1;}
+if(b===doc||b.ownerDocument===preferredDoc&&contains(preferredDoc,b)){return 1;}
+return sortInput?(indexOf(sortInput,a)-indexOf(sortInput,b)):0;}
+return compare&4?-1:1;}:function(a,b){if(a===b){hasDuplicate=true;return 0;}
+var cur,i=0,aup=a.parentNode,bup=b.parentNode,ap=[a],bp=[b];if(!aup||!bup){return a===doc?-1:b===doc?1:aup?-1:bup?1:sortInput?(indexOf(sortInput,a)-indexOf(sortInput,b)):0;}else if(aup===bup){return siblingCheck(a,b);}
+cur=a;while((cur=cur.parentNode)){ap.unshift(cur);}
+cur=b;while((cur=cur.parentNode)){bp.unshift(cur);}
+while(ap[i]===bp[i]){i++;}
+return i?siblingCheck(ap[i],bp[i]):ap[i]===preferredDoc?-1:bp[i]===preferredDoc?1:0;};return doc;};Sizzle.matches=function(expr,elements){return Sizzle(expr,null,null,elements);};Sizzle.matchesSelector=function(elem,expr){if((elem.ownerDocument||elem)!==document){setDocument(elem);}
+expr=expr.replace(rattributeQuotes,"='$1']");if(support.matchesSelector&&documentIsHTML&&(!rbuggyMatches||!rbuggyMatches.test(expr))&&(!rbuggyQSA||!rbuggyQSA.test(expr))){try{var ret=matches.call(elem,expr);if(ret||support.disconnectedMatch||elem.document&&elem.document.nodeType!==11){return ret;}}catch(e){}}
+return Sizzle(expr,document,null,[elem]).length>0;};Sizzle.contains=function(context,elem){if((context.ownerDocument||context)!==document){setDocument(context);}
+return contains(context,elem);};Sizzle.attr=function(elem,name){if((elem.ownerDocument||elem)!==document){setDocument(elem);}
+var fn=Expr.attrHandle[name.toLowerCase()],val=fn&&hasOwn.call(Expr.attrHandle,name.toLowerCase())?fn(elem,name,!documentIsHTML):undefined;return val!==undefined?val:support.attributes||!documentIsHTML?elem.getAttribute(name):(val=elem.getAttributeNode(name))&&val.specified?val.value:null;};Sizzle.error=function(msg){throw new Error("Syntax error, unrecognized expression: "+msg);};Sizzle.uniqueSort=function(results){var elem,duplicates=[],j=0,i=0;hasDuplicate=!support.detectDuplicates;sortInput=!support.sortStable&&results.slice(0);results.sort(sortOrder);if(hasDuplicate){while((elem=results[i++])){if(elem===results[i]){j=duplicates.push(i);}}
+while(j--){results.splice(duplicates[j],1);}}
+sortInput=null;return results;};getText=Sizzle.getText=function(elem){var node,ret="",i=0,nodeType=elem.nodeType;if(!nodeType){while((node=elem[i++])){ret+=getText(node);}}else if(nodeType===1||nodeType===9||nodeType===11){if(typeof elem.textContent==="string"){return elem.textContent;}else{for(elem=elem.firstChild;elem;elem=elem.nextSibling){ret+=getText(elem);}}}else if(nodeType===3||nodeType===4){return elem.nodeValue;}
+return ret;};Expr=Sizzle.selectors={cacheLength:50,createPseudo:markFunction,match:matchExpr,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:true}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:true},"~":{dir:"previousSibling"}},preFilter:{"ATTR":function(match){match[1]=match[1].replace(runescape,funescape);match[3]=(match[3]||match[4]||match[5]||"").replace(runescape,funescape);if(match[2]==="~="){match[3]=" "+match[3]+" ";}
+return match.slice(0,4);},"CHILD":function(match){match[1]=match[1].toLowerCase();if(match[1].slice(0,3)==="nth"){if(!match[3]){Sizzle.error(match[0]);}
+match[4]=+(match[4]?match[5]+(match[6]||1):2*(match[3]==="even"||match[3]==="odd"));match[5]=+((match[7]+match[8])||match[3]==="odd");}else if(match[3]){Sizzle.error(match[0]);}
+return match;},"PSEUDO":function(match){var excess,unquoted=!match[6]&&match[2];if(matchExpr["CHILD"].test(match[0])){return null;}
+if(match[3]){match[2]=match[4]||match[5]||"";}else if(unquoted&&rpseudo.test(unquoted)&&(excess=tokenize(unquoted,true))&&(excess=unquoted.indexOf(")",unquoted.length-excess)-unquoted.length)){match[0]=match[0].slice(0,excess);match[2]=unquoted.slice(0,excess);}
+return match.slice(0,3);}},filter:{"TAG":function(nodeNameSelector){var nodeName=nodeNameSelector.replace(runescape,funescape).toLowerCase();return nodeNameSelector==="*"?function(){return true;}:function(elem){return elem.nodeName&&elem.nodeName.toLowerCase()===nodeName;};},"CLASS":function(className){var pattern=classCache[className+" "];return pattern||(pattern=new RegExp("(^|"+whitespace+")"+className+"("+whitespace+"|$)"))&&classCache(className,function(elem){return pattern.test(typeof elem.className==="string"&&elem.className||typeof elem.getAttribute!=="undefined"&&elem.getAttribute("class")||"");});},"ATTR":function(name,operator,check){return function(elem){var result=Sizzle.attr(elem,name);if(result==null){return operator==="!=";}
+if(!operator){return true;}
+result+="";return operator==="="?result===check:operator==="!="?result!==check:operator==="^="?check&&result.indexOf(check)===0:operator==="*="?check&&result.indexOf(check)>-1:operator==="$="?check&&result.slice(-check.length)===check:operator==="~="?(" "+result.replace(rwhitespace," ")+" ").indexOf(check)>-1:operator==="|="?result===check||result.slice(0,check.length+1)===check+"-":false;};},"CHILD":function(type,what,argument,first,last){var simple=type.slice(0,3)!=="nth",forward=type.slice(-4)!=="last",ofType=what==="of-type";return first===1&&last===0?function(elem){return!!elem.parentNode;}:function(elem,context,xml){var cache,outerCache,node,diff,nodeIndex,start,dir=simple!==forward?"nextSibling":"previousSibling",parent=elem.parentNode,name=ofType&&elem.nodeName.toLowerCase(),useCache=!xml&&!ofType;if(parent){if(simple){while(dir){node=elem;while((node=node[dir])){if(ofType?node.nodeName.toLowerCase()===name:node.nodeType===1){return false;}}
+start=dir=type==="only"&&!start&&"nextSibling";}
+return true;}
+start=[forward?parent.firstChild:parent.lastChild];if(forward&&useCache){outerCache=parent[expando]||(parent[expando]={});cache=outerCache[type]||[];nodeIndex=cache[0]===dirruns&&cache[1];diff=cache[0]===dirruns&&cache[2];node=nodeIndex&&parent.childNodes[nodeIndex];while((node=++nodeIndex&&node&&node[dir]||(diff=nodeIndex=0)||start.pop())){if(node.nodeType===1&&++diff&&node===elem){outerCache[type]=[dirruns,nodeIndex,diff];break;}}}else if(useCache&&(cache=(elem[expando]||(elem[expando]={}))[type])&&cache[0]===dirruns){diff=cache[1];}else{while((node=++nodeIndex&&node&&node[dir]||(diff=nodeIndex=0)||start.pop())){if((ofType?node.nodeName.toLowerCase()===name:node.nodeType===1)&&++diff){if(useCache){(node[expando]||(node[expando]={}))[type]=[dirruns,diff];}
+if(node===elem){break;}}}}
+diff-=last;return diff===first||(diff%first===0&&diff/first>=0);}};},"PSEUDO":function(pseudo,argument){var args,fn=Expr.pseudos[pseudo]||Expr.setFilters[pseudo.toLowerCase()]||Sizzle.error("unsupported pseudo: "+pseudo);if(fn[expando]){return fn(argument);}
+if(fn.length>1){args=[pseudo,pseudo,"",argument];return Expr.setFilters.hasOwnProperty(pseudo.toLowerCase())?markFunction(function(seed,matches){var idx,matched=fn(seed,argument),i=matched.length;while(i--){idx=indexOf(seed,matched[i]);seed[idx]=!(matches[idx]=matched[i]);}}):function(elem){return fn(elem,0,args);};}
+return fn;}},pseudos:{"not":markFunction(function(selector){var input=[],results=[],matcher=compile(selector.replace(rtrim,"$1"));return matcher[expando]?markFunction(function(seed,matches,context,xml){var elem,unmatched=matcher(seed,null,xml,[]),i=seed.length;while(i--){if((elem=unmatched[i])){seed[i]=!(matches[i]=elem);}}}):function(elem,context,xml){input[0]=elem;matcher(input,null,xml,results);input[0]=null;return!results.pop();};}),"has":markFunction(function(selector){return function(elem){return Sizzle(selector,elem).length>0;};}),"contains":markFunction(function(text){text=text.replace(runescape,funescape);return function(elem){return(elem.textContent||elem.innerText||getText(elem)).indexOf(text)>-1;};}),"lang":markFunction(function(lang){if(!ridentifier.test(lang||"")){Sizzle.error("unsupported lang: "+lang);}
+lang=lang.replace(runescape,funescape).toLowerCase();return function(elem){var elemLang;do{if((elemLang=documentIsHTML?elem.lang:elem.getAttribute("xml:lang")||elem.getAttribute("lang"))){elemLang=elemLang.toLowerCase();return elemLang===lang||elemLang.indexOf(lang+"-")===0;}}while((elem=elem.parentNode)&&elem.nodeType===1);return false;};}),"target":function(elem){var hash=window.location&&window.location.hash;return hash&&hash.slice(1)===elem.id;},"root":function(elem){return elem===docElem;},"focus":function(elem){return elem===document.activeElement&&(!document.hasFocus||document.hasFocus())&&!!(elem.type||elem.href||~elem.tabIndex);},"enabled":function(elem){return elem.disabled===false;},"disabled":function(elem){return elem.disabled===true;},"checked":function(elem){var nodeName=elem.nodeName.toLowerCase();return(nodeName==="input"&&!!elem.checked)||(nodeName==="option"&&!!elem.selected);},"selected":function(elem){if(elem.parentNode){elem.parentNode.selectedIndex;}
+return elem.selected===true;},"empty":function(elem){for(elem=elem.firstChild;elem;elem=elem.nextSibling){if(elem.nodeType<6){return false;}}
+return true;},"parent":function(elem){return!Expr.pseudos["empty"](elem);},"header":function(elem){return rheader.test(elem.nodeName);},"input":function(elem){return rinputs.test(elem.nodeName);},"button":function(elem){var name=elem.nodeName.toLowerCase();return name==="input"&&elem.type==="button"||name==="button";},"text":function(elem){var attr;return elem.nodeName.toLowerCase()==="input"&&elem.type==="text"&&((attr=elem.getAttribute("type"))==null||attr.toLowerCase()==="text");},"first":createPositionalPseudo(function(){return[0];}),"last":createPositionalPseudo(function(matchIndexes,length){return[length-1];}),"eq":createPositionalPseudo(function(matchIndexes,length,argument){return[argument<0?argument+length:argument];}),"even":createPositionalPseudo(function(matchIndexes,length){var i=0;for(;i=0;){matchIndexes.push(i);}
+return matchIndexes;}),"gt":createPositionalPseudo(function(matchIndexes,length,argument){var i=argument<0?argument+length:argument;for(;++i1?function(elem,context,xml){var i=matchers.length;while(i--){if(!matchers[i](elem,context,xml)){return false;}}
+return true;}:matchers[0];}
+function multipleContexts(selector,contexts,results){var i=0,len=contexts.length;for(;i-1){seed[temp]=!(results[temp]=elem);}}}}else{matcherOut=condense(matcherOut===results?matcherOut.splice(preexisting,matcherOut.length):matcherOut);if(postFinder){postFinder(null,results,matcherOut,xml);}else{push.apply(results,matcherOut);}}});}
+function matcherFromTokens(tokens){var checkContext,matcher,j,len=tokens.length,leadingRelative=Expr.relative[tokens[0].type],implicitRelative=leadingRelative||Expr.relative[" "],i=leadingRelative?1:0,matchContext=addCombinator(function(elem){return elem===checkContext;},implicitRelative,true),matchAnyContext=addCombinator(function(elem){return indexOf(checkContext,elem)>-1;},implicitRelative,true),matchers=[function(elem,context,xml){var ret=(!leadingRelative&&(xml||context!==outermostContext))||((checkContext=context).nodeType?matchContext(elem,context,xml):matchAnyContext(elem,context,xml));checkContext=null;return ret;}];for(;i1&&elementMatcher(matchers),i>1&&toSelector(tokens.slice(0,i-1).concat({value:tokens[i-2].type===" "?"*":""})).replace(rtrim,"$1"),matcher,i0,byElement=elementMatchers.length>0,superMatcher=function(seed,context,xml,results,outermost){var elem,j,matcher,matchedCount=0,i="0",unmatched=seed&&[],setMatched=[],contextBackup=outermostContext,elems=seed||byElement&&Expr.find["TAG"]("*",outermost),dirrunsUnique=(dirruns+=contextBackup==null?1:Math.random()||0.1),len=elems.length;if(outermost){outermostContext=context!==document&&context;}
+for(;i!==len&&(elem=elems[i])!=null;i++){if(byElement&&elem){j=0;while((matcher=elementMatchers[j++])){if(matcher(elem,context,xml)){results.push(elem);break;}}
+if(outermost){dirruns=dirrunsUnique;}}
+if(bySet){if((elem=!matcher&&elem)){matchedCount--;}
+if(seed){unmatched.push(elem);}}}
+matchedCount+=i;if(bySet&&i!==matchedCount){j=0;while((matcher=setMatchers[j++])){matcher(unmatched,setMatched,context,xml);}
+if(seed){if(matchedCount>0){while(i--){if(!(unmatched[i]||setMatched[i])){setMatched[i]=pop.call(results);}}}
+setMatched=condense(setMatched);}
+push.apply(results,setMatched);if(outermost&&!seed&&setMatched.length>0&&(matchedCount+setMatchers.length)>1){Sizzle.uniqueSort(results);}}
+if(outermost){dirruns=dirrunsUnique;outermostContext=contextBackup;}
+return unmatched;};return bySet?markFunction(superMatcher):superMatcher;}
+compile=Sizzle.compile=function(selector,match){var i,setMatchers=[],elementMatchers=[],cached=compilerCache[selector+" "];if(!cached){if(!match){match=tokenize(selector);}
+i=match.length;while(i--){cached=matcherFromTokens(match[i]);if(cached[expando]){setMatchers.push(cached);}else{elementMatchers.push(cached);}}
+cached=compilerCache(selector,matcherFromGroupMatchers(elementMatchers,setMatchers));cached.selector=selector;}
+return cached;};select=Sizzle.select=function(selector,context,results,seed){var i,tokens,token,type,find,compiled=typeof selector==="function"&&selector,match=!seed&&tokenize((selector=compiled.selector||selector));results=results||[];if(match.length===1){tokens=match[0]=match[0].slice(0);if(tokens.length>2&&(token=tokens[0]).type==="ID"&&support.getById&&context.nodeType===9&&documentIsHTML&&Expr.relative[tokens[1].type]){context=(Expr.find["ID"](token.matches[0].replace(runescape,funescape),context)||[])[0];if(!context){return results;}else if(compiled){context=context.parentNode;}
+selector=selector.slice(tokens.shift().value.length);}
+i=matchExpr["needsContext"].test(selector)?0:tokens.length;while(i--){token=tokens[i];if(Expr.relative[(type=token.type)]){break;}
+if((find=Expr.find[type])){if((seed=find(token.matches[0].replace(runescape,funescape),rsibling.test(tokens[0].type)&&testContext(context.parentNode)||context))){tokens.splice(i,1);selector=seed.length&&toSelector(tokens);if(!selector){push.apply(results,seed);return results;}
+break;}}}}
+(compiled||compile(selector,match))(seed,context,!documentIsHTML,results,rsibling.test(selector)&&testContext(context.parentNode)||context);return results;};support.sortStable=expando.split("").sort(sortOrder).join("")===expando;support.detectDuplicates=!!hasDuplicate;setDocument();support.sortDetached=assert(function(div1){return div1.compareDocumentPosition(document.createElement("div"))&1;});if(!assert(function(div){div.innerHTML=" ";return div.firstChild.getAttribute("href")==="#";})){addHandle("type|href|height|width",function(elem,name,isXML){if(!isXML){return elem.getAttribute(name,name.toLowerCase()==="type"?1:2);}});}
+if(!support.attributes||!assert(function(div){div.innerHTML=" ";div.firstChild.setAttribute("value","");return div.firstChild.getAttribute("value")==="";})){addHandle("value",function(elem,name,isXML){if(!isXML&&elem.nodeName.toLowerCase()==="input"){return elem.defaultValue;}});}
+if(!assert(function(div){return div.getAttribute("disabled")==null;})){addHandle(booleans,function(elem,name,isXML){var val;if(!isXML){return elem[name]===true?name.toLowerCase():(val=elem.getAttributeNode(name))&&val.specified?val.value:null;}});}
+return Sizzle;})(window);jQuery.find=Sizzle;jQuery.expr=Sizzle.selectors;jQuery.expr[":"]=jQuery.expr.pseudos;jQuery.unique=Sizzle.uniqueSort;jQuery.text=Sizzle.getText;jQuery.isXMLDoc=Sizzle.isXML;jQuery.contains=Sizzle.contains;var rneedsContext=jQuery.expr.match.needsContext;var rsingleTag=(/^<(\w+)\s*\/?>(?:<\/\1>|)$/);var risSimple=/^.[^:#\[\.,]*$/;function winnow(elements,qualifier,not){if(jQuery.isFunction(qualifier)){return jQuery.grep(elements,function(elem,i){return!!qualifier.call(elem,i,elem)!==not;});}
+if(qualifier.nodeType){return jQuery.grep(elements,function(elem){return(elem===qualifier)!==not;});}
+if(typeof qualifier==="string"){if(risSimple.test(qualifier)){return jQuery.filter(qualifier,elements,not);}
+qualifier=jQuery.filter(qualifier,elements);}
+return jQuery.grep(elements,function(elem){return(jQuery.inArray(elem,qualifier)>=0)!==not;});}
+jQuery.filter=function(expr,elems,not){var elem=elems[0];if(not){expr=":not("+expr+")";}
+return elems.length===1&&elem.nodeType===1?jQuery.find.matchesSelector(elem,expr)?[elem]:[]:jQuery.find.matches(expr,jQuery.grep(elems,function(elem){return elem.nodeType===1;}));};jQuery.fn.extend({find:function(selector){var i,ret=[],self=this,len=self.length;if(typeof selector!=="string"){return this.pushStack(jQuery(selector).filter(function(){for(i=0;i1?jQuery.unique(ret):ret);ret.selector=this.selector?this.selector+" "+selector:selector;return ret;},filter:function(selector){return this.pushStack(winnow(this,selector||[],false));},not:function(selector){return this.pushStack(winnow(this,selector||[],true));},is:function(selector){return!!winnow(this,typeof selector==="string"&&rneedsContext.test(selector)?jQuery(selector):selector||[],false).length;}});var rootjQuery,document=window.document,rquickExpr=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,init=jQuery.fn.init=function(selector,context){var match,elem;if(!selector){return this;}
+if(typeof selector==="string"){if(selector.charAt(0)==="<"&&selector.charAt(selector.length-1)===">"&&selector.length>=3){match=[null,selector,null];}else{match=rquickExpr.exec(selector);}
+if(match&&(match[1]||!context)){if(match[1]){context=context instanceof jQuery?context[0]:context;jQuery.merge(this,jQuery.parseHTML(match[1],context&&context.nodeType?context.ownerDocument||context:document,true));if(rsingleTag.test(match[1])&&jQuery.isPlainObject(context)){for(match in context){if(jQuery.isFunction(this[match])){this[match](context[match]);}else{this.attr(match,context[match]);}}}
+return this;}else{elem=document.getElementById(match[2]);if(elem&&elem.parentNode){if(elem.id!==match[2]){return rootjQuery.find(selector);}
+this.length=1;this[0]=elem;}
+this.context=document;this.selector=selector;return this;}}else if(!context||context.jquery){return(context||rootjQuery).find(selector);}else{return this.constructor(context).find(selector);}}else if(selector.nodeType){this.context=this[0]=selector;this.length=1;return this;}else if(jQuery.isFunction(selector)){return typeof rootjQuery.ready!=="undefined"?rootjQuery.ready(selector):selector(jQuery);}
+if(selector.selector!==undefined){this.selector=selector.selector;this.context=selector.context;}
+return jQuery.makeArray(selector,this);};init.prototype=jQuery.fn;rootjQuery=jQuery(document);var rparentsprev=/^(?:parents|prev(?:Until|All))/,guaranteedUnique={children:true,contents:true,next:true,prev:true};jQuery.extend({dir:function(elem,dir,until){var matched=[],cur=elem[dir];while(cur&&cur.nodeType!==9&&(until===undefined||cur.nodeType!==1||!jQuery(cur).is(until))){if(cur.nodeType===1){matched.push(cur);}
+cur=cur[dir];}
+return matched;},sibling:function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType===1&&n!==elem){r.push(n);}}
+return r;}});jQuery.fn.extend({has:function(target){var i,targets=jQuery(target,this),len=targets.length;return this.filter(function(){for(i=0;i-1:cur.nodeType===1&&jQuery.find.matchesSelector(cur,selectors))){matched.push(cur);break;}}}
+return this.pushStack(matched.length>1?jQuery.unique(matched):matched);},index:function(elem){if(!elem){return(this[0]&&this[0].parentNode)?this.first().prevAll().length:-1;}
+if(typeof elem==="string"){return jQuery.inArray(this[0],jQuery(elem));}
+return jQuery.inArray(elem.jquery?elem[0]:elem,this);},add:function(selector,context){return this.pushStack(jQuery.unique(jQuery.merge(this.get(),jQuery(selector,context))));},addBack:function(selector){return this.add(selector==null?this.prevObject:this.prevObject.filter(selector));}});function sibling(cur,dir){do{cur=cur[dir];}while(cur&&cur.nodeType!==1);return cur;}
+jQuery.each({parent:function(elem){var parent=elem.parentNode;return parent&&parent.nodeType!==11?parent:null;},parents:function(elem){return jQuery.dir(elem,"parentNode");},parentsUntil:function(elem,i,until){return jQuery.dir(elem,"parentNode",until);},next:function(elem){return sibling(elem,"nextSibling");},prev:function(elem){return sibling(elem,"previousSibling");},nextAll:function(elem){return jQuery.dir(elem,"nextSibling");},prevAll:function(elem){return jQuery.dir(elem,"previousSibling");},nextUntil:function(elem,i,until){return jQuery.dir(elem,"nextSibling",until);},prevUntil:function(elem,i,until){return jQuery.dir(elem,"previousSibling",until);},siblings:function(elem){return jQuery.sibling((elem.parentNode||{}).firstChild,elem);},children:function(elem){return jQuery.sibling(elem.firstChild);},contents:function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.merge([],elem.childNodes);}},function(name,fn){jQuery.fn[name]=function(until,selector){var ret=jQuery.map(this,fn,until);if(name.slice(-5)!=="Until"){selector=until;}
+if(selector&&typeof selector==="string"){ret=jQuery.filter(selector,ret);}
+if(this.length>1){if(!guaranteedUnique[name]){ret=jQuery.unique(ret);}
+if(rparentsprev.test(name)){ret=ret.reverse();}}
+return this.pushStack(ret);};});var rnotwhite=(/\S+/g);var optionsCache={};function createOptions(options){var object=optionsCache[options]={};jQuery.each(options.match(rnotwhite)||[],function(_,flag){object[flag]=true;});return object;}
+jQuery.Callbacks=function(options){options=typeof options==="string"?(optionsCache[options]||createOptions(options)):jQuery.extend({},options);var
+firing,memory,fired,firingLength,firingIndex,firingStart,list=[],stack=!options.once&&[],fire=function(data){memory=options.memory&&data;fired=true;firingIndex=firingStart||0;firingStart=0;firingLength=list.length;firing=true;for(;list&&firingIndex-1){list.splice(index,1);if(firing){if(index<=firingLength){firingLength--;}
+if(index<=firingIndex){firingIndex--;}}}});}
+return this;},has:function(fn){return fn?jQuery.inArray(fn,list)>-1:!!(list&&list.length);},empty:function(){list=[];firingLength=0;return this;},disable:function(){list=stack=memory=undefined;return this;},disabled:function(){return!list;},lock:function(){stack=undefined;if(!memory){self.disable();}
+return this;},locked:function(){return!stack;},fireWith:function(context,args){if(list&&(!fired||stack)){args=args||[];args=[context,args.slice?args.slice():args];if(firing){stack.push(args);}else{fire(args);}}
+return this;},fire:function(){self.fireWith(this,arguments);return this;},fired:function(){return!!fired;}};return self;};jQuery.extend({Deferred:function(func){var tuples=[["resolve","done",jQuery.Callbacks("once memory"),"resolved"],["reject","fail",jQuery.Callbacks("once memory"),"rejected"],["notify","progress",jQuery.Callbacks("memory")]],state="pending",promise={state:function(){return state;},always:function(){deferred.done(arguments).fail(arguments);return this;},then:function(){var fns=arguments;return jQuery.Deferred(function(newDefer){jQuery.each(tuples,function(i,tuple){var fn=jQuery.isFunction(fns[i])&&fns[i];deferred[tuple[1]](function(){var returned=fn&&fn.apply(this,arguments);if(returned&&jQuery.isFunction(returned.promise)){returned.promise().done(newDefer.resolve).fail(newDefer.reject).progress(newDefer.notify);}else{newDefer[tuple[0]+"With"](this===promise?newDefer.promise():this,fn?[returned]:arguments);}});});fns=null;}).promise();},promise:function(obj){return obj!=null?jQuery.extend(obj,promise):promise;}},deferred={};promise.pipe=promise.then;jQuery.each(tuples,function(i,tuple){var list=tuple[2],stateString=tuple[3];promise[tuple[1]]=list.add;if(stateString){list.add(function(){state=stateString;},tuples[i^1][2].disable,tuples[2][2].lock);}
+deferred[tuple[0]]=function(){deferred[tuple[0]+"With"](this===deferred?promise:this,arguments);return this;};deferred[tuple[0]+"With"]=list.fireWith;});promise.promise(deferred);if(func){func.call(deferred,deferred);}
+return deferred;},when:function(subordinate){var i=0,resolveValues=slice.call(arguments),length=resolveValues.length,remaining=length!==1||(subordinate&&jQuery.isFunction(subordinate.promise))?length:0,deferred=remaining===1?subordinate:jQuery.Deferred(),updateFunc=function(i,contexts,values){return function(value){contexts[i]=this;values[i]=arguments.length>1?slice.call(arguments):value;if(values===progressValues){deferred.notifyWith(contexts,values);}else if(!(--remaining)){deferred.resolveWith(contexts,values);}};},progressValues,progressContexts,resolveContexts;if(length>1){progressValues=new Array(length);progressContexts=new Array(length);resolveContexts=new Array(length);for(;i0){return;}
+readyList.resolveWith(document,[jQuery]);if(jQuery.fn.triggerHandler){jQuery(document).triggerHandler("ready");jQuery(document).off("ready");}}});function detach(){if(document.addEventListener){document.removeEventListener("DOMContentLoaded",completed,false);window.removeEventListener("load",completed,false);}else{document.detachEvent("onreadystatechange",completed);window.detachEvent("onload",completed);}}
+function completed(){if(document.addEventListener||event.type==="load"||document.readyState==="complete"){detach();jQuery.ready();}}
+jQuery.ready.promise=function(obj){if(!readyList){readyList=jQuery.Deferred();if(document.readyState==="complete"){setTimeout(jQuery.ready);}else if(document.addEventListener){document.addEventListener("DOMContentLoaded",completed,false);window.addEventListener("load",completed,false);}else{document.attachEvent("onreadystatechange",completed);window.attachEvent("onload",completed);var top=false;try{top=window.frameElement==null&&document.documentElement;}catch(e){}
+if(top&&top.doScroll){(function doScrollCheck(){if(!jQuery.isReady){try{top.doScroll("left");}catch(e){return setTimeout(doScrollCheck,50);}
+detach();jQuery.ready();}})();}}}
+return readyList.promise(obj);};var strundefined=typeof undefined;var i;for(i in jQuery(support)){break;}
+support.ownLast=i!=="0";support.inlineBlockNeedsLayout=false;jQuery(function(){var val,div,body,container;body=document.getElementsByTagName("body")[0];if(!body||!body.style){return;}
+div=document.createElement("div");container=document.createElement("div");container.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px";body.appendChild(container).appendChild(div);if(typeof div.style.zoom!==strundefined){div.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1";support.inlineBlockNeedsLayout=val=div.offsetWidth===3;if(val){body.style.zoom=1;}}
+body.removeChild(container);});(function(){var div=document.createElement("div");if(support.deleteExpando==null){support.deleteExpando=true;try{delete div.test;}catch(e){support.deleteExpando=false;}}
+div=null;})();jQuery.acceptData=function(elem){var noData=jQuery.noData[(elem.nodeName+" ").toLowerCase()],nodeType=+elem.nodeType||1;return nodeType!==1&&nodeType!==9?false:!noData||noData!==true&&elem.getAttribute("classid")===noData;};var rbrace=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,rmultiDash=/([A-Z])/g;function dataAttr(elem,key,data){if(data===undefined&&elem.nodeType===1){var name="data-"+key.replace(rmultiDash,"-$1").toLowerCase();data=elem.getAttribute(name);if(typeof data==="string"){try{data=data==="true"?true:data==="false"?false:data==="null"?null:+data+""===data?+data:rbrace.test(data)?jQuery.parseJSON(data):data;}catch(e){}
+jQuery.data(elem,key,data);}else{data=undefined;}}
+return data;}
+function isEmptyDataObject(obj){var name;for(name in obj){if(name==="data"&&jQuery.isEmptyObject(obj[name])){continue;}
+if(name!=="toJSON"){return false;}}
+return true;}
+function internalData(elem,name,data,pvt){if(!jQuery.acceptData(elem)){return;}
+var ret,thisCache,internalKey=jQuery.expando,isNode=elem.nodeType,cache=isNode?jQuery.cache:elem,id=isNode?elem[internalKey]:elem[internalKey]&&internalKey;if((!id||!cache[id]||(!pvt&&!cache[id].data))&&data===undefined&&typeof name==="string"){return;}
+if(!id){if(isNode){id=elem[internalKey]=deletedIds.pop()||jQuery.guid++;}else{id=internalKey;}}
+if(!cache[id]){cache[id]=isNode?{}:{toJSON:jQuery.noop};}
+if(typeof name==="object"||typeof name==="function"){if(pvt){cache[id]=jQuery.extend(cache[id],name);}else{cache[id].data=jQuery.extend(cache[id].data,name);}}
+thisCache=cache[id];if(!pvt){if(!thisCache.data){thisCache.data={};}
+thisCache=thisCache.data;}
+if(data!==undefined){thisCache[jQuery.camelCase(name)]=data;}
+if(typeof name==="string"){ret=thisCache[name];if(ret==null){ret=thisCache[jQuery.camelCase(name)];}}else{ret=thisCache;}
+return ret;}
+function internalRemoveData(elem,name,pvt){if(!jQuery.acceptData(elem)){return;}
+var thisCache,i,isNode=elem.nodeType,cache=isNode?jQuery.cache:elem,id=isNode?elem[jQuery.expando]:jQuery.expando;if(!cache[id]){return;}
+if(name){thisCache=pvt?cache[id]:cache[id].data;if(thisCache){if(!jQuery.isArray(name)){if(name in thisCache){name=[name];}else{name=jQuery.camelCase(name);if(name in thisCache){name=[name];}else{name=name.split(" ");}}}else{name=name.concat(jQuery.map(name,jQuery.camelCase));}
+i=name.length;while(i--){delete thisCache[name[i]];}
+if(pvt?!isEmptyDataObject(thisCache):!jQuery.isEmptyObject(thisCache)){return;}}}
+if(!pvt){delete cache[id].data;if(!isEmptyDataObject(cache[id])){return;}}
+if(isNode){jQuery.cleanData([elem],true);}else if(support.deleteExpando||cache!=cache.window){delete cache[id];}else{cache[id]=null;}}
+jQuery.extend({cache:{},noData:{"applet ":true,"embed ":true,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(elem){elem=elem.nodeType?jQuery.cache[elem[jQuery.expando]]:elem[jQuery.expando];return!!elem&&!isEmptyDataObject(elem);},data:function(elem,name,data){return internalData(elem,name,data);},removeData:function(elem,name){return internalRemoveData(elem,name);},_data:function(elem,name,data){return internalData(elem,name,data,true);},_removeData:function(elem,name){return internalRemoveData(elem,name,true);}});jQuery.fn.extend({data:function(key,value){var i,name,data,elem=this[0],attrs=elem&&elem.attributes;if(key===undefined){if(this.length){data=jQuery.data(elem);if(elem.nodeType===1&&!jQuery._data(elem,"parsedAttrs")){i=attrs.length;while(i--){if(attrs[i]){name=attrs[i].name;if(name.indexOf("data-")===0){name=jQuery.camelCase(name.slice(5));dataAttr(elem,name,data[name]);}}}
+jQuery._data(elem,"parsedAttrs",true);}}
+return data;}
+if(typeof key==="object"){return this.each(function(){jQuery.data(this,key);});}
+return arguments.length>1?this.each(function(){jQuery.data(this,key,value);}):elem?dataAttr(elem,key,jQuery.data(elem,key)):undefined;},removeData:function(key){return this.each(function(){jQuery.removeData(this,key);});}});jQuery.extend({queue:function(elem,type,data){var queue;if(elem){type=(type||"fx")+"queue";queue=jQuery._data(elem,type);if(data){if(!queue||jQuery.isArray(data)){queue=jQuery._data(elem,type,jQuery.makeArray(data));}else{queue.push(data);}}
+return queue||[];}},dequeue:function(elem,type){type=type||"fx";var queue=jQuery.queue(elem,type),startLength=queue.length,fn=queue.shift(),hooks=jQuery._queueHooks(elem,type),next=function(){jQuery.dequeue(elem,type);};if(fn==="inprogress"){fn=queue.shift();startLength--;}
+if(fn){if(type==="fx"){queue.unshift("inprogress");}
+delete hooks.stop;fn.call(elem,next,hooks);}
+if(!startLength&&hooks){hooks.empty.fire();}},_queueHooks:function(elem,type){var key=type+"queueHooks";return jQuery._data(elem,key)||jQuery._data(elem,key,{empty:jQuery.Callbacks("once memory").add(function(){jQuery._removeData(elem,type+"queue");jQuery._removeData(elem,key);})});}});jQuery.fn.extend({queue:function(type,data){var setter=2;if(typeof type!=="string"){data=type;type="fx";setter--;}
+if(arguments.lengtha ";support.leadingWhitespace=div.firstChild.nodeType===3;support.tbody=!div.getElementsByTagName("tbody").length;support.htmlSerialize=!!div.getElementsByTagName("link").length;support.html5Clone=document.createElement("nav").cloneNode(true).outerHTML!=="<:nav>";input.type="checkbox";input.checked=true;fragment.appendChild(input);support.appendChecked=input.checked;div.innerHTML="x ";support.noCloneChecked=!!div.cloneNode(true).lastChild.defaultValue;fragment.appendChild(div);div.innerHTML=" ";support.checkClone=div.cloneNode(true).cloneNode(true).lastChild.checked;support.noCloneEvent=true;if(div.attachEvent){div.attachEvent("onclick",function(){support.noCloneEvent=false;});div.cloneNode(true).click();}
+if(support.deleteExpando==null){support.deleteExpando=true;try{delete div.test;}catch(e){support.deleteExpando=false;}}})();(function(){var i,eventName,div=document.createElement("div");for(i in{submit:true,change:true,focusin:true}){eventName="on"+i;if(!(support[i+"Bubbles"]=eventName in window)){div.setAttribute(eventName,"t");support[i+"Bubbles"]=div.attributes[eventName].expando===false;}}
+div=null;})();var rformElems=/^(?:input|select|textarea)$/i,rkeyEvent=/^key/,rmouseEvent=/^(?:mouse|pointer|contextmenu)|click/,rfocusMorph=/^(?:focusinfocus|focusoutblur)$/,rtypenamespace=/^([^.]*)(?:\.(.+)|)$/;function returnTrue(){return true;}
+function returnFalse(){return false;}
+function safeActiveElement(){try{return document.activeElement;}catch(err){}}
+jQuery.event={global:{},add:function(elem,types,handler,data,selector){var tmp,events,t,handleObjIn,special,eventHandle,handleObj,handlers,type,namespaces,origType,elemData=jQuery._data(elem);if(!elemData){return;}
+if(handler.handler){handleObjIn=handler;handler=handleObjIn.handler;selector=handleObjIn.selector;}
+if(!handler.guid){handler.guid=jQuery.guid++;}
+if(!(events=elemData.events)){events=elemData.events={};}
+if(!(eventHandle=elemData.handle)){eventHandle=elemData.handle=function(e){return typeof jQuery!==strundefined&&(!e||jQuery.event.triggered!==e.type)?jQuery.event.dispatch.apply(eventHandle.elem,arguments):undefined;};eventHandle.elem=elem;}
+types=(types||"").match(rnotwhite)||[""];t=types.length;while(t--){tmp=rtypenamespace.exec(types[t])||[];type=origType=tmp[1];namespaces=(tmp[2]||"").split(".").sort();if(!type){continue;}
+special=jQuery.event.special[type]||{};type=(selector?special.delegateType:special.bindType)||type;special=jQuery.event.special[type]||{};handleObj=jQuery.extend({type:type,origType:origType,data:data,handler:handler,guid:handler.guid,selector:selector,needsContext:selector&&jQuery.expr.match.needsContext.test(selector),namespace:namespaces.join(".")},handleObjIn);if(!(handlers=events[type])){handlers=events[type]=[];handlers.delegateCount=0;if(!special.setup||special.setup.call(elem,data,namespaces,eventHandle)===false){if(elem.addEventListener){elem.addEventListener(type,eventHandle,false);}else if(elem.attachEvent){elem.attachEvent("on"+type,eventHandle);}}}
+if(special.add){special.add.call(elem,handleObj);if(!handleObj.handler.guid){handleObj.handler.guid=handler.guid;}}
+if(selector){handlers.splice(handlers.delegateCount++,0,handleObj);}else{handlers.push(handleObj);}
+jQuery.event.global[type]=true;}
+elem=null;},remove:function(elem,types,handler,selector,mappedTypes){var j,handleObj,tmp,origCount,t,events,special,handlers,type,namespaces,origType,elemData=jQuery.hasData(elem)&&jQuery._data(elem);if(!elemData||!(events=elemData.events)){return;}
+types=(types||"").match(rnotwhite)||[""];t=types.length;while(t--){tmp=rtypenamespace.exec(types[t])||[];type=origType=tmp[1];namespaces=(tmp[2]||"").split(".").sort();if(!type){for(type in events){jQuery.event.remove(elem,type+types[t],handler,selector,true);}
+continue;}
+special=jQuery.event.special[type]||{};type=(selector?special.delegateType:special.bindType)||type;handlers=events[type]||[];tmp=tmp[2]&&new RegExp("(^|\\.)"+namespaces.join("\\.(?:.*\\.|)")+"(\\.|$)");origCount=j=handlers.length;while(j--){handleObj=handlers[j];if((mappedTypes||origType===handleObj.origType)&&(!handler||handler.guid===handleObj.guid)&&(!tmp||tmp.test(handleObj.namespace))&&(!selector||selector===handleObj.selector||selector==="**"&&handleObj.selector)){handlers.splice(j,1);if(handleObj.selector){handlers.delegateCount--;}
+if(special.remove){special.remove.call(elem,handleObj);}}}
+if(origCount&&!handlers.length){if(!special.teardown||special.teardown.call(elem,namespaces,elemData.handle)===false){jQuery.removeEvent(elem,type,elemData.handle);}
+delete events[type];}}
+if(jQuery.isEmptyObject(events)){delete elemData.handle;jQuery._removeData(elem,"events");}},trigger:function(event,data,elem,onlyHandlers){var handle,ontype,cur,bubbleType,special,tmp,i,eventPath=[elem||document],type=hasOwn.call(event,"type")?event.type:event,namespaces=hasOwn.call(event,"namespace")?event.namespace.split("."):[];cur=tmp=elem=elem||document;if(elem.nodeType===3||elem.nodeType===8){return;}
+if(rfocusMorph.test(type+jQuery.event.triggered)){return;}
+if(type.indexOf(".")>=0){namespaces=type.split(".");type=namespaces.shift();namespaces.sort();}
+ontype=type.indexOf(":")<0&&"on"+type;event=event[jQuery.expando]?event:new jQuery.Event(type,typeof event==="object"&&event);event.isTrigger=onlyHandlers?2:3;event.namespace=namespaces.join(".");event.namespace_re=event.namespace?new RegExp("(^|\\.)"+namespaces.join("\\.(?:.*\\.|)")+"(\\.|$)"):null;event.result=undefined;if(!event.target){event.target=elem;}
+data=data==null?[event]:jQuery.makeArray(data,[event]);special=jQuery.event.special[type]||{};if(!onlyHandlers&&special.trigger&&special.trigger.apply(elem,data)===false){return;}
+if(!onlyHandlers&&!special.noBubble&&!jQuery.isWindow(elem)){bubbleType=special.delegateType||type;if(!rfocusMorph.test(bubbleType+type)){cur=cur.parentNode;}
+for(;cur;cur=cur.parentNode){eventPath.push(cur);tmp=cur;}
+if(tmp===(elem.ownerDocument||document)){eventPath.push(tmp.defaultView||tmp.parentWindow||window);}}
+i=0;while((cur=eventPath[i++])&&!event.isPropagationStopped()){event.type=i>1?bubbleType:special.bindType||type;handle=(jQuery._data(cur,"events")||{})[event.type]&&jQuery._data(cur,"handle");if(handle){handle.apply(cur,data);}
+handle=ontype&&cur[ontype];if(handle&&handle.apply&&jQuery.acceptData(cur)){event.result=handle.apply(cur,data);if(event.result===false){event.preventDefault();}}}
+event.type=type;if(!onlyHandlers&&!event.isDefaultPrevented()){if((!special._default||special._default.apply(eventPath.pop(),data)===false)&&jQuery.acceptData(elem)){if(ontype&&elem[type]&&!jQuery.isWindow(elem)){tmp=elem[ontype];if(tmp){elem[ontype]=null;}
+jQuery.event.triggered=type;try{elem[type]();}catch(e){}
+jQuery.event.triggered=undefined;if(tmp){elem[ontype]=tmp;}}}}
+return event.result;},dispatch:function(event){event=jQuery.event.fix(event);var i,ret,handleObj,matched,j,handlerQueue=[],args=slice.call(arguments),handlers=(jQuery._data(this,"events")||{})[event.type]||[],special=jQuery.event.special[event.type]||{};args[0]=event;event.delegateTarget=this;if(special.preDispatch&&special.preDispatch.call(this,event)===false){return;}
+handlerQueue=jQuery.event.handlers.call(this,event,handlers);i=0;while((matched=handlerQueue[i++])&&!event.isPropagationStopped()){event.currentTarget=matched.elem;j=0;while((handleObj=matched.handlers[j++])&&!event.isImmediatePropagationStopped()){if(!event.namespace_re||event.namespace_re.test(handleObj.namespace)){event.handleObj=handleObj;event.data=handleObj.data;ret=((jQuery.event.special[handleObj.origType]||{}).handle||handleObj.handler).apply(matched.elem,args);if(ret!==undefined){if((event.result=ret)===false){event.preventDefault();event.stopPropagation();}}}}}
+if(special.postDispatch){special.postDispatch.call(this,event);}
+return event.result;},handlers:function(event,handlers){var sel,handleObj,matches,i,handlerQueue=[],delegateCount=handlers.delegateCount,cur=event.target;if(delegateCount&&cur.nodeType&&(!event.button||event.type!=="click")){for(;cur!=this;cur=cur.parentNode||this){if(cur.nodeType===1&&(cur.disabled!==true||event.type!=="click")){matches=[];for(i=0;i=0:jQuery.find(sel,this,null,[cur]).length;}
+if(matches[sel]){matches.push(handleObj);}}
+if(matches.length){handlerQueue.push({elem:cur,handlers:matches});}}}}
+if(delegateCount ]","i"),rleadingWhitespace=/^\s+/,rxhtmlTag=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,rtagName=/<([\w:]+)/,rtbody=/\s*$/g,wrapMap={option:[1,""," "],legend:[1,""," "],area:[1,""," "],param:[1,""," "],thead:[1,""],tr:[2,""],col:[2,""],td:[3,""],_default:support.htmlSerialize?[0,"",""]:[1,"X","
"]},safeFragment=createSafeFragment(document),fragmentDiv=safeFragment.appendChild(document.createElement("div"));wrapMap.optgroup=wrapMap.option;wrapMap.tbody=wrapMap.tfoot=wrapMap.colgroup=wrapMap.caption=wrapMap.thead;wrapMap.th=wrapMap.td;function getAll(context,tag){var elems,elem,i=0,found=typeof context.getElementsByTagName!==strundefined?context.getElementsByTagName(tag||"*"):typeof context.querySelectorAll!==strundefined?context.querySelectorAll(tag||"*"):undefined;if(!found){for(found=[],elems=context.childNodes||context;(elem=elems[i])!=null;i++){if(!tag||jQuery.nodeName(elem,tag)){found.push(elem);}else{jQuery.merge(found,getAll(elem,tag));}}}
+return tag===undefined||tag&&jQuery.nodeName(context,tag)?jQuery.merge([context],found):found;}
+function fixDefaultChecked(elem){if(rcheckableType.test(elem.type)){elem.defaultChecked=elem.checked;}}
+function manipulationTarget(elem,content){return jQuery.nodeName(elem,"table")&&jQuery.nodeName(content.nodeType!==11?content:content.firstChild,"tr")?elem.getElementsByTagName("tbody")[0]||elem.appendChild(elem.ownerDocument.createElement("tbody")):elem;}
+function disableScript(elem){elem.type=(jQuery.find.attr(elem,"type")!==null)+"/"+elem.type;return elem;}
+function restoreScript(elem){var match=rscriptTypeMasked.exec(elem.type);if(match){elem.type=match[1];}else{elem.removeAttribute("type");}
+return elem;}
+function setGlobalEval(elems,refElements){var elem,i=0;for(;(elem=elems[i])!=null;i++){jQuery._data(elem,"globalEval",!refElements||jQuery._data(refElements[i],"globalEval"));}}
+function cloneCopyEvent(src,dest){if(dest.nodeType!==1||!jQuery.hasData(src)){return;}
+var type,i,l,oldData=jQuery._data(src),curData=jQuery._data(dest,oldData),events=oldData.events;if(events){delete curData.handle;curData.events={};for(type in events){for(i=0,l=events[type].length;i")){clone=elem.cloneNode(true);}else{fragmentDiv.innerHTML=elem.outerHTML;fragmentDiv.removeChild(clone=fragmentDiv.firstChild);}
+if((!support.noCloneEvent||!support.noCloneChecked)&&(elem.nodeType===1||elem.nodeType===11)&&!jQuery.isXMLDoc(elem)){destElements=getAll(clone);srcElements=getAll(elem);for(i=0;(node=srcElements[i])!=null;++i){if(destElements[i]){fixCloneNodeIssues(node,destElements[i]);}}}
+if(dataAndEvents){if(deepDataAndEvents){srcElements=srcElements||getAll(elem);destElements=destElements||getAll(clone);for(i=0;(node=srcElements[i])!=null;i++){cloneCopyEvent(node,destElements[i]);}}else{cloneCopyEvent(elem,clone);}}
+destElements=getAll(clone,"script");if(destElements.length>0){setGlobalEval(destElements,!inPage&&getAll(elem,"script"));}
+destElements=srcElements=node=null;return clone;},buildFragment:function(elems,context,scripts,selection){var j,elem,contains,tmp,tag,tbody,wrap,l=elems.length,safe=createSafeFragment(context),nodes=[],i=0;for(;i$2>")+wrap[2];j=wrap[0];while(j--){tmp=tmp.lastChild;}
+if(!support.leadingWhitespace&&rleadingWhitespace.test(elem)){nodes.push(context.createTextNode(rleadingWhitespace.exec(elem)[0]));}
+if(!support.tbody){elem=tag==="table"&&!rtbody.test(elem)?tmp.firstChild:wrap[1]===""&&!rtbody.test(elem)?tmp:0;j=elem&&elem.childNodes.length;while(j--){if(jQuery.nodeName((tbody=elem.childNodes[j]),"tbody")&&!tbody.childNodes.length){elem.removeChild(tbody);}}}
+jQuery.merge(nodes,tmp.childNodes);tmp.textContent="";while(tmp.firstChild){tmp.removeChild(tmp.firstChild);}
+tmp=safe.lastChild;}}}
+if(tmp){safe.removeChild(tmp);}
+if(!support.appendChecked){jQuery.grep(getAll(nodes,"input"),fixDefaultChecked);}
+i=0;while((elem=nodes[i++])){if(selection&&jQuery.inArray(elem,selection)!==-1){continue;}
+contains=jQuery.contains(elem.ownerDocument,elem);tmp=getAll(safe.appendChild(elem),"script");if(contains){setGlobalEval(tmp);}
+if(scripts){j=0;while((elem=tmp[j++])){if(rscriptType.test(elem.type||"")){scripts.push(elem);}}}}
+tmp=null;return safe;},cleanData:function(elems,acceptData){var elem,type,id,data,i=0,internalKey=jQuery.expando,cache=jQuery.cache,deleteExpando=support.deleteExpando,special=jQuery.event.special;for(;(elem=elems[i])!=null;i++){if(acceptData||jQuery.acceptData(elem)){id=elem[internalKey];data=id&&cache[id];if(data){if(data.events){for(type in data.events){if(special[type]){jQuery.event.remove(elem,type);}else{jQuery.removeEvent(elem,type,data.handle);}}}
+if(cache[id]){delete cache[id];if(deleteExpando){delete elem[internalKey];}else if(typeof elem.removeAttribute!==strundefined){elem.removeAttribute(internalKey);}else{elem[internalKey]=null;}
+deletedIds.push(id);}}}}}});jQuery.fn.extend({text:function(value){return access(this,function(value){return value===undefined?jQuery.text(this):this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(value));},null,value,arguments.length);},append:function(){return this.domManip(arguments,function(elem){if(this.nodeType===1||this.nodeType===11||this.nodeType===9){var target=manipulationTarget(this,elem);target.appendChild(elem);}});},prepend:function(){return this.domManip(arguments,function(elem){if(this.nodeType===1||this.nodeType===11||this.nodeType===9){var target=manipulationTarget(this,elem);target.insertBefore(elem,target.firstChild);}});},before:function(){return this.domManip(arguments,function(elem){if(this.parentNode){this.parentNode.insertBefore(elem,this);}});},after:function(){return this.domManip(arguments,function(elem){if(this.parentNode){this.parentNode.insertBefore(elem,this.nextSibling);}});},remove:function(selector,keepData){var elem,elems=selector?jQuery.filter(selector,this):this,i=0;for(;(elem=elems[i])!=null;i++){if(!keepData&&elem.nodeType===1){jQuery.cleanData(getAll(elem));}
+if(elem.parentNode){if(keepData&&jQuery.contains(elem.ownerDocument,elem)){setGlobalEval(getAll(elem,"script"));}
+elem.parentNode.removeChild(elem);}}
+return this;},empty:function(){var elem,i=0;for(;(elem=this[i])!=null;i++){if(elem.nodeType===1){jQuery.cleanData(getAll(elem,false));}
+while(elem.firstChild){elem.removeChild(elem.firstChild);}
+if(elem.options&&jQuery.nodeName(elem,"select")){elem.options.length=0;}}
+return this;},clone:function(dataAndEvents,deepDataAndEvents){dataAndEvents=dataAndEvents==null?false:dataAndEvents;deepDataAndEvents=deepDataAndEvents==null?dataAndEvents:deepDataAndEvents;return this.map(function(){return jQuery.clone(this,dataAndEvents,deepDataAndEvents);});},html:function(value){return access(this,function(value){var elem=this[0]||{},i=0,l=this.length;if(value===undefined){return elem.nodeType===1?elem.innerHTML.replace(rinlinejQuery,""):undefined;}
+if(typeof value==="string"&&!rnoInnerhtml.test(value)&&(support.htmlSerialize||!rnoshimcache.test(value))&&(support.leadingWhitespace||!rleadingWhitespace.test(value))&&!wrapMap[(rtagName.exec(value)||["",""])[1].toLowerCase()]){value=value.replace(rxhtmlTag,"<$1>$2>");try{for(;i1&&typeof value==="string"&&!support.checkClone&&rchecked.test(value))){return this.each(function(index){var self=set.eq(index);if(isFunction){args[0]=value.call(this,index,self.html());}
+self.domManip(args,callback);});}
+if(l){fragment=jQuery.buildFragment(args,this[0].ownerDocument,false,this);first=fragment.firstChild;if(fragment.childNodes.length===1){fragment=first;}
+if(first){scripts=jQuery.map(getAll(fragment,"script"),disableScript);hasScripts=scripts.length;for(;i ")).appendTo(doc.documentElement);doc=(iframe[0].contentWindow||iframe[0].contentDocument).document;doc.write();doc.close();display=actualDisplay(nodeName,doc);iframe.detach();}
+elemdisplay[nodeName]=display;}
+return display;}
+(function(){var shrinkWrapBlocksVal;support.shrinkWrapBlocks=function(){if(shrinkWrapBlocksVal!=null){return shrinkWrapBlocksVal;}
+shrinkWrapBlocksVal=false;var div,body,container;body=document.getElementsByTagName("body")[0];if(!body||!body.style){return;}
+div=document.createElement("div");container=document.createElement("div");container.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px";body.appendChild(container).appendChild(div);if(typeof div.style.zoom!==strundefined){div.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;"+"box-sizing:content-box;display:block;margin:0;border:0;"+"padding:1px;width:1px;zoom:1";div.appendChild(document.createElement("div")).style.width="5px";shrinkWrapBlocksVal=div.offsetWidth!==3;}
+body.removeChild(container);return shrinkWrapBlocksVal;};})();var rmargin=(/^margin/);var rnumnonpx=new RegExp("^("+pnum+")(?!px)[a-z%]+$","i");var getStyles,curCSS,rposition=/^(top|right|bottom|left)$/;if(window.getComputedStyle){getStyles=function(elem){if(elem.ownerDocument.defaultView.opener){return elem.ownerDocument.defaultView.getComputedStyle(elem,null);}
+return window.getComputedStyle(elem,null);};curCSS=function(elem,name,computed){var width,minWidth,maxWidth,ret,style=elem.style;computed=computed||getStyles(elem);ret=computed?computed.getPropertyValue(name)||computed[name]:undefined;if(computed){if(ret===""&&!jQuery.contains(elem.ownerDocument,elem)){ret=jQuery.style(elem,name);}
+if(rnumnonpx.test(ret)&&rmargin.test(name)){width=style.width;minWidth=style.minWidth;maxWidth=style.maxWidth;style.minWidth=style.maxWidth=style.width=ret;ret=computed.width;style.width=width;style.minWidth=minWidth;style.maxWidth=maxWidth;}}
+return ret===undefined?ret:ret+"";};}else if(document.documentElement.currentStyle){getStyles=function(elem){return elem.currentStyle;};curCSS=function(elem,name,computed){var left,rs,rsLeft,ret,style=elem.style;computed=computed||getStyles(elem);ret=computed?computed[name]:undefined;if(ret==null&&style&&style[name]){ret=style[name];}
+if(rnumnonpx.test(ret)&&!rposition.test(name)){left=style.left;rs=elem.runtimeStyle;rsLeft=rs&&rs.left;if(rsLeft){rs.left=elem.currentStyle.left;}
+style.left=name==="fontSize"?"1em":ret;ret=style.pixelLeft+"px";style.left=left;if(rsLeft){rs.left=rsLeft;}}
+return ret===undefined?ret:ret+""||"auto";};}
+function addGetHookIf(conditionFn,hookFn){return{get:function(){var condition=conditionFn();if(condition==null){return;}
+if(condition){delete this.get;return;}
+return(this.get=hookFn).apply(this,arguments);}};}
+(function(){var div,style,a,pixelPositionVal,boxSizingReliableVal,reliableHiddenOffsetsVal,reliableMarginRightVal;div=document.createElement("div");div.innerHTML=" a ";a=div.getElementsByTagName("a")[0];style=a&&a.style;if(!style){return;}
+style.cssText="float:left;opacity:.5";support.opacity=style.opacity==="0.5";support.cssFloat=!!style.cssFloat;div.style.backgroundClip="content-box";div.cloneNode(true).style.backgroundClip="";support.clearCloneStyle=div.style.backgroundClip==="content-box";support.boxSizing=style.boxSizing===""||style.MozBoxSizing===""||style.WebkitBoxSizing==="";jQuery.extend(support,{reliableHiddenOffsets:function(){if(reliableHiddenOffsetsVal==null){computeStyleTests();}
+return reliableHiddenOffsetsVal;},boxSizingReliable:function(){if(boxSizingReliableVal==null){computeStyleTests();}
+return boxSizingReliableVal;},pixelPosition:function(){if(pixelPositionVal==null){computeStyleTests();}
+return pixelPositionVal;},reliableMarginRight:function(){if(reliableMarginRightVal==null){computeStyleTests();}
+return reliableMarginRightVal;}});function computeStyleTests(){var div,body,container,contents;body=document.getElementsByTagName("body")[0];if(!body||!body.style){return;}
+div=document.createElement("div");container=document.createElement("div");container.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px";body.appendChild(container).appendChild(div);div.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;"+"box-sizing:border-box;display:block;margin-top:1%;top:1%;"+"border:1px;padding:1px;width:4px;position:absolute";pixelPositionVal=boxSizingReliableVal=false;reliableMarginRightVal=true;if(window.getComputedStyle){pixelPositionVal=(window.getComputedStyle(div,null)||{}).top!=="1%";boxSizingReliableVal=(window.getComputedStyle(div,null)||{width:"4px"}).width==="4px";contents=div.appendChild(document.createElement("div"));contents.style.cssText=div.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;"+"box-sizing:content-box;display:block;margin:0;border:0;padding:0";contents.style.marginRight=contents.style.width="0";div.style.width="1px";reliableMarginRightVal=!parseFloat((window.getComputedStyle(contents,null)||{}).marginRight);div.removeChild(contents);}
+div.innerHTML="";contents=div.getElementsByTagName("td");contents[0].style.cssText="margin:0;border:0;padding:0;display:none";reliableHiddenOffsetsVal=contents[0].offsetHeight===0;if(reliableHiddenOffsetsVal){contents[0].style.display="";contents[1].style.display="none";reliableHiddenOffsetsVal=contents[0].offsetHeight===0;}
+body.removeChild(container);}})();jQuery.swap=function(elem,options,callback,args){var ret,name,old={};for(name in options){old[name]=elem.style[name];elem.style[name]=options[name];}
+ret=callback.apply(elem,args||[]);for(name in options){elem.style[name]=old[name];}
+return ret;};var
+ralpha=/alpha\([^)]*\)/i,ropacity=/opacity\s*=\s*([^)]*)/,rdisplayswap=/^(none|table(?!-c[ea]).+)/,rnumsplit=new RegExp("^("+pnum+")(.*)$","i"),rrelNum=new RegExp("^([+-])=("+pnum+")","i"),cssShow={position:"absolute",visibility:"hidden",display:"block"},cssNormalTransform={letterSpacing:"0",fontWeight:"400"},cssPrefixes=["Webkit","O","Moz","ms"];function vendorPropName(style,name){if(name in style){return name;}
+var capName=name.charAt(0).toUpperCase()+name.slice(1),origName=name,i=cssPrefixes.length;while(i--){name=cssPrefixes[i]+capName;if(name in style){return name;}}
+return origName;}
+function showHide(elements,show){var display,elem,hidden,values=[],index=0,length=elements.length;for(;index=1||value==="")&&jQuery.trim(filter.replace(ralpha,""))===""&&style.removeAttribute){style.removeAttribute("filter");if(value===""||currentStyle&&!currentStyle.filter){return;}}
+style.filter=ralpha.test(filter)?filter.replace(ralpha,opacity):filter+" "+opacity;}};}
+jQuery.cssHooks.marginRight=addGetHookIf(support.reliableMarginRight,function(elem,computed){if(computed){return jQuery.swap(elem,{"display":"inline-block"},curCSS,[elem,"marginRight"]);}});jQuery.each({margin:"",padding:"",border:"Width"},function(prefix,suffix){jQuery.cssHooks[prefix+suffix]={expand:function(value){var i=0,expanded={},parts=typeof value==="string"?value.split(" "):[value];for(;i<4;i++){expanded[prefix+cssExpand[i]+suffix]=parts[i]||parts[i-2]||parts[0];}
+return expanded;}};if(!rmargin.test(prefix)){jQuery.cssHooks[prefix+suffix].set=setPositiveNumber;}});jQuery.fn.extend({css:function(name,value){return access(this,function(elem,name,value){var styles,len,map={},i=0;if(jQuery.isArray(name)){styles=getStyles(elem);len=name.length;for(;i1);},show:function(){return showHide(this,true);},hide:function(){return showHide(this);},toggle:function(state){if(typeof state==="boolean"){return state?this.show():this.hide();}
+return this.each(function(){if(isHidden(this)){jQuery(this).show();}else{jQuery(this).hide();}});}});function Tween(elem,options,prop,end,easing){return new Tween.prototype.init(elem,options,prop,end,easing);}
+jQuery.Tween=Tween;Tween.prototype={constructor:Tween,init:function(elem,options,prop,end,easing,unit){this.elem=elem;this.prop=prop;this.easing=easing||"swing";this.options=options;this.start=this.now=this.cur();this.end=end;this.unit=unit||(jQuery.cssNumber[prop]?"":"px");},cur:function(){var hooks=Tween.propHooks[this.prop];return hooks&&hooks.get?hooks.get(this):Tween.propHooks._default.get(this);},run:function(percent){var eased,hooks=Tween.propHooks[this.prop];if(this.options.duration){this.pos=eased=jQuery.easing[this.easing](percent,this.options.duration*percent,0,1,this.options.duration);}else{this.pos=eased=percent;}
+this.now=(this.end-this.start)*eased+this.start;if(this.options.step){this.options.step.call(this.elem,this.now,this);}
+if(hooks&&hooks.set){hooks.set(this);}else{Tween.propHooks._default.set(this);}
+return this;}};Tween.prototype.init.prototype=Tween.prototype;Tween.propHooks={_default:{get:function(tween){var result;if(tween.elem[tween.prop]!=null&&(!tween.elem.style||tween.elem.style[tween.prop]==null)){return tween.elem[tween.prop];}
+result=jQuery.css(tween.elem,tween.prop,"");return!result||result==="auto"?0:result;},set:function(tween){if(jQuery.fx.step[tween.prop]){jQuery.fx.step[tween.prop](tween);}else if(tween.elem.style&&(tween.elem.style[jQuery.cssProps[tween.prop]]!=null||jQuery.cssHooks[tween.prop])){jQuery.style(tween.elem,tween.prop,tween.now+tween.unit);}else{tween.elem[tween.prop]=tween.now;}}}};Tween.propHooks.scrollTop=Tween.propHooks.scrollLeft={set:function(tween){if(tween.elem.nodeType&&tween.elem.parentNode){tween.elem[tween.prop]=tween.now;}}};jQuery.easing={linear:function(p){return p;},swing:function(p){return 0.5-Math.cos(p*Math.PI)/2;}};jQuery.fx=Tween.prototype.init;jQuery.fx.step={};var
+fxNow,timerId,rfxtypes=/^(?:toggle|show|hide)$/,rfxnum=new RegExp("^(?:([+-])=|)("+pnum+")([a-z%]*)$","i"),rrun=/queueHooks$/,animationPrefilters=[defaultPrefilter],tweeners={"*":[function(prop,value){var tween=this.createTween(prop,value),target=tween.cur(),parts=rfxnum.exec(value),unit=parts&&parts[3]||(jQuery.cssNumber[prop]?"":"px"),start=(jQuery.cssNumber[prop]||unit!=="px"&&+target)&&rfxnum.exec(jQuery.css(tween.elem,prop)),scale=1,maxIterations=20;if(start&&start[3]!==unit){unit=unit||start[3];parts=parts||[];start=+target||1;do{scale=scale||".5";start=start/scale;jQuery.style(tween.elem,prop,start+unit);}while(scale!==(scale=tween.cur()/target)&&scale!==1&&--maxIterations);}
+if(parts){start=tween.start=+start||+target||0;tween.unit=unit;tween.end=parts[1]?start+(parts[1]+1)*parts[2]:+parts[2];}
+return tween;}]};function createFxNow(){setTimeout(function(){fxNow=undefined;});return(fxNow=jQuery.now());}
+function genFx(type,includeWidth){var which,attrs={height:type},i=0;includeWidth=includeWidth?1:0;for(;i<4;i+=2-includeWidth){which=cssExpand[i];attrs["margin"+which]=attrs["padding"+which]=type;}
+if(includeWidth){attrs.opacity=attrs.width=type;}
+return attrs;}
+function createTween(value,prop,animation){var tween,collection=(tweeners[prop]||[]).concat(tweeners["*"]),index=0,length=collection.length;for(;indexa ";a=div.getElementsByTagName("a")[0];select=document.createElement("select");opt=select.appendChild(document.createElement("option"));input=div.getElementsByTagName("input")[0];a.style.cssText="top:1px";support.getSetAttribute=div.className!=="t";support.style=/top/.test(a.getAttribute("style"));support.hrefNormalized=a.getAttribute("href")==="/a";support.checkOn=!!input.value;support.optSelected=opt.selected;support.enctype=!!document.createElement("form").enctype;select.disabled=true;support.optDisabled=!opt.disabled;input=document.createElement("input");input.setAttribute("value","");support.input=input.getAttribute("value")==="";input.value="t";input.setAttribute("type","radio");support.radioValue=input.value==="t";})();var rreturn=/\r/g;jQuery.fn.extend({val:function(value){var hooks,ret,isFunction,elem=this[0];if(!arguments.length){if(elem){hooks=jQuery.valHooks[elem.type]||jQuery.valHooks[elem.nodeName.toLowerCase()];if(hooks&&"get"in hooks&&(ret=hooks.get(elem,"value"))!==undefined){return ret;}
+ret=elem.value;return typeof ret==="string"?ret.replace(rreturn,""):ret==null?"":ret;}
+return;}
+isFunction=jQuery.isFunction(value);return this.each(function(i){var val;if(this.nodeType!==1){return;}
+if(isFunction){val=value.call(this,i,jQuery(this).val());}else{val=value;}
+if(val==null){val="";}else if(typeof val==="number"){val+="";}else if(jQuery.isArray(val)){val=jQuery.map(val,function(value){return value==null?"":value+"";});}
+hooks=jQuery.valHooks[this.type]||jQuery.valHooks[this.nodeName.toLowerCase()];if(!hooks||!("set"in hooks)||hooks.set(this,val,"value")===undefined){this.value=val;}});}});jQuery.extend({valHooks:{option:{get:function(elem){var val=jQuery.find.attr(elem,"value");return val!=null?val:jQuery.trim(jQuery.text(elem));}},select:{get:function(elem){var value,option,options=elem.options,index=elem.selectedIndex,one=elem.type==="select-one"||index<0,values=one?null:[],max=one?index+1:options.length,i=index<0?max:one?index:0;for(;i=0){try{option.selected=optionSet=true;}catch(_){option.scrollHeight;}}else{option.selected=false;}}
+if(!optionSet){elem.selectedIndex=-1;}
+return options;}}}});jQuery.each(["radio","checkbox"],function(){jQuery.valHooks[this]={set:function(elem,value){if(jQuery.isArray(value)){return(elem.checked=jQuery.inArray(jQuery(elem).val(),value)>=0);}}};if(!support.checkOn){jQuery.valHooks[this].get=function(elem){return elem.getAttribute("value")===null?"on":elem.value;};}});var nodeHook,boolHook,attrHandle=jQuery.expr.attrHandle,ruseDefault=/^(?:checked|selected)$/i,getSetAttribute=support.getSetAttribute,getSetInput=support.input;jQuery.fn.extend({attr:function(name,value){return access(this,jQuery.attr,name,value,arguments.length>1);},removeAttr:function(name){return this.each(function(){jQuery.removeAttr(this,name);});}});jQuery.extend({attr:function(elem,name,value){var hooks,ret,nType=elem.nodeType;if(!elem||nType===3||nType===8||nType===2){return;}
+if(typeof elem.getAttribute===strundefined){return jQuery.prop(elem,name,value);}
+if(nType!==1||!jQuery.isXMLDoc(elem)){name=name.toLowerCase();hooks=jQuery.attrHooks[name]||(jQuery.expr.match.bool.test(name)?boolHook:nodeHook);}
+if(value!==undefined){if(value===null){jQuery.removeAttr(elem,name);}else if(hooks&&"set"in hooks&&(ret=hooks.set(elem,value,name))!==undefined){return ret;}else{elem.setAttribute(name,value+"");return value;}}else if(hooks&&"get"in hooks&&(ret=hooks.get(elem,name))!==null){return ret;}else{ret=jQuery.find.attr(elem,name);return ret==null?undefined:ret;}},removeAttr:function(elem,value){var name,propName,i=0,attrNames=value&&value.match(rnotwhite);if(attrNames&&elem.nodeType===1){while((name=attrNames[i++])){propName=jQuery.propFix[name]||name;if(jQuery.expr.match.bool.test(name)){if(getSetInput&&getSetAttribute||!ruseDefault.test(name)){elem[propName]=false;}else{elem[jQuery.camelCase("default-"+name)]=elem[propName]=false;}}else{jQuery.attr(elem,name,"");}
+elem.removeAttribute(getSetAttribute?name:propName);}}},attrHooks:{type:{set:function(elem,value){if(!support.radioValue&&value==="radio"&&jQuery.nodeName(elem,"input")){var val=elem.value;elem.setAttribute("type",value);if(val){elem.value=val;}
+return value;}}}}});boolHook={set:function(elem,value,name){if(value===false){jQuery.removeAttr(elem,name);}else if(getSetInput&&getSetAttribute||!ruseDefault.test(name)){elem.setAttribute(!getSetAttribute&&jQuery.propFix[name]||name,name);}else{elem[jQuery.camelCase("default-"+name)]=elem[name]=true;}
+return name;}};jQuery.each(jQuery.expr.match.bool.source.match(/\w+/g),function(i,name){var getter=attrHandle[name]||jQuery.find.attr;attrHandle[name]=getSetInput&&getSetAttribute||!ruseDefault.test(name)?function(elem,name,isXML){var ret,handle;if(!isXML){handle=attrHandle[name];attrHandle[name]=ret;ret=getter(elem,name,isXML)!=null?name.toLowerCase():null;attrHandle[name]=handle;}
+return ret;}:function(elem,name,isXML){if(!isXML){return elem[jQuery.camelCase("default-"+name)]?name.toLowerCase():null;}};});if(!getSetInput||!getSetAttribute){jQuery.attrHooks.value={set:function(elem,value,name){if(jQuery.nodeName(elem,"input")){elem.defaultValue=value;}else{return nodeHook&&nodeHook.set(elem,value,name);}}};}
+if(!getSetAttribute){nodeHook={set:function(elem,value,name){var ret=elem.getAttributeNode(name);if(!ret){elem.setAttributeNode((ret=elem.ownerDocument.createAttribute(name)));}
+ret.value=value+="";if(name==="value"||value===elem.getAttribute(name)){return value;}}};attrHandle.id=attrHandle.name=attrHandle.coords=function(elem,name,isXML){var ret;if(!isXML){return(ret=elem.getAttributeNode(name))&&ret.value!==""?ret.value:null;}};jQuery.valHooks.button={get:function(elem,name){var ret=elem.getAttributeNode(name);if(ret&&ret.specified){return ret.value;}},set:nodeHook.set};jQuery.attrHooks.contenteditable={set:function(elem,value,name){nodeHook.set(elem,value===""?false:value,name);}};jQuery.each(["width","height"],function(i,name){jQuery.attrHooks[name]={set:function(elem,value){if(value===""){elem.setAttribute(name,"auto");return value;}}};});}
+if(!support.style){jQuery.attrHooks.style={get:function(elem){return elem.style.cssText||undefined;},set:function(elem,value){return(elem.style.cssText=value+"");}};}
+var rfocusable=/^(?:input|select|textarea|button|object)$/i,rclickable=/^(?:a|area)$/i;jQuery.fn.extend({prop:function(name,value){return access(this,jQuery.prop,name,value,arguments.length>1);},removeProp:function(name){name=jQuery.propFix[name]||name;return this.each(function(){try{this[name]=undefined;delete this[name];}catch(e){}});}});jQuery.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(elem,name,value){var ret,hooks,notxml,nType=elem.nodeType;if(!elem||nType===3||nType===8||nType===2){return;}
+notxml=nType!==1||!jQuery.isXMLDoc(elem);if(notxml){name=jQuery.propFix[name]||name;hooks=jQuery.propHooks[name];}
+if(value!==undefined){return hooks&&"set"in hooks&&(ret=hooks.set(elem,value,name))!==undefined?ret:(elem[name]=value);}else{return hooks&&"get"in hooks&&(ret=hooks.get(elem,name))!==null?ret:elem[name];}},propHooks:{tabIndex:{get:function(elem){var tabindex=jQuery.find.attr(elem,"tabindex");return tabindex?parseInt(tabindex,10):rfocusable.test(elem.nodeName)||rclickable.test(elem.nodeName)&&elem.href?0:-1;}}}});if(!support.hrefNormalized){jQuery.each(["href","src"],function(i,name){jQuery.propHooks[name]={get:function(elem){return elem.getAttribute(name,4);}};});}
+if(!support.optSelected){jQuery.propHooks.selected={get:function(elem){var parent=elem.parentNode;if(parent){parent.selectedIndex;if(parent.parentNode){parent.parentNode.selectedIndex;}}
+return null;}};}
+jQuery.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){jQuery.propFix[this.toLowerCase()]=this;});if(!support.enctype){jQuery.propFix.enctype="encoding";}
+var rclass=/[\t\r\n\f]/g;jQuery.fn.extend({addClass:function(value){var classes,elem,cur,clazz,j,finalValue,i=0,len=this.length,proceed=typeof value==="string"&&value;if(jQuery.isFunction(value)){return this.each(function(j){jQuery(this).addClass(value.call(this,j,this.className));});}
+if(proceed){classes=(value||"").match(rnotwhite)||[];for(;i